Update frontend libpq to remove limits on query lengths,
authorTom Lane
Tue, 31 Aug 1999 01:37:37 +0000 (01:37 +0000)
committerTom Lane
Tue, 31 Aug 1999 01:37:37 +0000 (01:37 +0000)
error/notice message lengths, and number of fields per tuple.  Add
pqexpbuffer.c/.h, a frontend version of backend's stringinfo module.
This is first step in applying Mike Ansley's long-query patches,
even though he didn't do any of these particular changes...

src/interfaces/libpq/Makefile.in
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-lobj.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/fe-print.c
src/interfaces/libpq/libpq-int.h
src/interfaces/libpq/pqexpbuffer.c [new file with mode: 0644]
src/interfaces/libpq/pqexpbuffer.h [new file with mode: 0644]
src/interfaces/libpq/win32.mak

index 73f333cae12e1ee29cf2e35218c731b4dd083b52..ec955dcd4bc1fc5e103e37ae4dafa897209cc8fc 100644 (file)
@@ -6,7 +6,7 @@
 # Copyright (c) 1994, Regents of the University of California
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.46 1999/06/30 23:57:25 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.47 1999/08/31 01:37:36 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -28,7 +28,7 @@ CFLAGS+= $(MBFLAGS)
 endif
 
 OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
-      dllist.o pqsignal.o
+      pqexpbuffer.o dllist.o pqsignal.o
 
 ifdef MULTIBYTE
 OBJS+= common.o wchar.o conv.o big5.o
@@ -80,6 +80,7 @@ install-headers: libpq-fe.h libpq-int.h
    @if [ ! -d $(HEADERDIR) ]; then mkdir $(HEADERDIR); fi
    $(INSTALL) $(INSTLOPTS) libpq-fe.h $(HEADERDIR)/libpq-fe.h
    $(INSTALL) $(INSTLOPTS) libpq-int.h $(HEADERDIR)/libpq-int.h
+   $(INSTALL) $(INSTLOPTS) pqexpbuffer.h $(HEADERDIR)/pqexpbuffer.h
 
 
 .PHONY: clean
index f72f76b4080eefc467da9d118d480419431c2d02..574d78c25d55aa3b7181c8484bdef08c34ae47b3 100644 (file)
@@ -5,9 +5,11 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
+ * NOTE: the error message strings returned by this module must not
+ * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.32 1999/07/19 06:25:38 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.33 1999/08/31 01:37:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
index 4edb4e8e5960c2c8d5a977585ee9137a642bd40b..4e86a8db91e1ab3059c1decb6510aaafd4add8b2 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.101 1999/07/19 06:25:38 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.102 1999/08/31 01:37:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,7 @@ static ConnStatusType connectDB(PGconn *conn);
 static PGconn *makeEmptyPGconn(void);
 static void freePGconn(PGconn *conn);
 static void closePGconn(PGconn *conn);
-static int conninfo_parse(const char *conninfo, char *errorMessage);
+static int conninfo_parse(const char *conninfo, PQExpBuffer errorMessage);
 static char *conninfo_getval(char *keyword);
 static void conninfo_free(void);
 static void defaultNoticeProcessor(void *arg, const char *message);
@@ -178,7 +178,7 @@ PQconnectdb(const char *conninfo)
     * Parse the conninfo string and save settings in conn structure
     * ----------
     */
-   if (conninfo_parse(conninfo, conn->errorMessage) < 0)
+   if (conninfo_parse(conninfo, &conn->errorMessage) < 0)
    {
        conn->status = CONNECTION_BAD;
        conninfo_free();
@@ -226,9 +226,11 @@ PQconnectdb(const char *conninfo)
 PQconninfoOption *
 PQconndefaults(void)
 {
-   char        errorMessage[ERROR_MSG_LENGTH];
+   PQExpBufferData  errorBuf;
 
-   conninfo_parse("", errorMessage);
+   initPQExpBuffer(&errorBuf);
+   conninfo_parse("", &errorBuf);
+   termPQExpBuffer(&errorBuf);
    return PQconninfoOptions;
 }
 
@@ -328,13 +330,17 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
    else if ((tmp = getenv("PGUSER")) != NULL)
        conn->pguser = strdup(tmp);
    else
-       conn->pguser = fe_getauthname(conn->errorMessage);
+   {
+       /* fe-auth.c has not been fixed to support PQExpBuffers, so: */
+       conn->pguser = fe_getauthname(conn->errorMessage.data);
+       conn->errorMessage.len = strlen(conn->errorMessage.data);
+   }
 
    if (conn->pguser == NULL)
    {
        error = TRUE;
-       sprintf(conn->errorMessage,
-               "FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
    }
 
    if (pwd)
@@ -469,8 +475,8 @@ update_db_info(PGconn *conn)
                conn->pghost = NULL;
                if (strcmp(old + offset, "localhost") != 0)
                {
-                   (void) sprintf(conn->errorMessage,
-                                  "connectDB() -- non-tcp access only possible on localhost\n");
+                   printfPQExpBuffer(&conn->errorMessage,
+                                     "connectDB() -- non-tcp access only possible on localhost\n");
                    return 1;
                }
            }
@@ -533,9 +539,9 @@ connectDB(PGconn *conn)
        hp = gethostbyname(conn->pghost);
        if ((hp == NULL) || (hp->h_addrtype != AF_INET))
        {
-           (void) sprintf(conn->errorMessage,
-                          "connectDB() --  unknown hostname: %s\n",
-                          conn->pghost);
+           printfPQExpBuffer(&conn->errorMessage,
+                             "connectDB() --  unknown hostname: %s\n",
+                             conn->pghost);
            goto connect_errReturn;
        }
        family = AF_INET;
@@ -567,21 +573,21 @@ connectDB(PGconn *conn)
    /* Connect to the server  */
    if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
    {
-       (void) sprintf(conn->errorMessage,
-                      "connectDB() -- socket() failed: errno=%d\n%s\n",
-                      errno, strerror(errno));
+       printfPQExpBuffer(&conn->errorMessage,
+                         "connectDB() -- socket() failed: errno=%d\n%s\n",
+                         errno, strerror(errno));
        goto connect_errReturn;
    }
    if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
    {
-       (void) sprintf(conn->errorMessage,
-                      "connectDB() -- connect() failed: %s\n"
-                      "Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n",
-                      strerror(errno),
-                      (family == AF_INET) ? " (with -i)" : "",
-                      conn->pghost ? conn->pghost : "localhost",
-                    (family == AF_INET) ? "TCP/IP port" : "Unix socket",
-                      conn->pgport);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "connectDB() -- connect() failed: %s\n"
+                         "Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n",
+                         strerror(errno),
+                         (family == AF_INET) ? " (with -i)" : "",
+                         conn->pghost ? conn->pghost : "localhost",
+                         (family == AF_INET) ? "TCP/IP port" : "Unix socket",
+                         conn->pgport);
        goto connect_errReturn;
    }
 
@@ -596,9 +602,9 @@ connectDB(PGconn *conn)
    if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)
 #endif
    {
-       (void) sprintf(conn->errorMessage,
-                      "connectDB() -- fcntl() failed: errno=%d\n%s\n",
-                      errno, strerror(errno));
+       printfPQExpBuffer(&conn->errorMessage,
+                         "connectDB() -- fcntl() failed: errno=%d\n%s\n",
+                         errno, strerror(errno));
        goto connect_errReturn;
    }
 
@@ -609,8 +615,8 @@ connectDB(PGconn *conn)
        pe = getprotobyname("TCP");
        if (pe == NULL)
        {
-           (void) sprintf(conn->errorMessage,
-                          "connectDB(): getprotobyname failed\n");
+           printfPQExpBuffer(&conn->errorMessage,
+                             "connectDB(): getprotobyname failed\n");
            goto connect_errReturn;
        }
        if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY,
@@ -620,9 +626,9 @@ connectDB(PGconn *conn)
                       &on,
                       sizeof(on)) < 0)
        {
-           (void) sprintf(conn->errorMessage,
-                     "connectDB() -- setsockopt failed: errno=%d\n%s\n",
-                          errno, strerror(errno));
+           printfPQExpBuffer(&conn->errorMessage,
+                             "connectDB() -- setsockopt failed: errno=%d\n%s\n",
+                             errno, strerror(errno));
 #ifdef WIN32
            printf("Winsock error: %i\n", WSAGetLastError());
 #endif
@@ -634,9 +640,9 @@ connectDB(PGconn *conn)
    laddrlen = sizeof(conn->laddr);
    if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
    {
-       (void) sprintf(conn->errorMessage,
-                  "connectDB() -- getsockname() failed: errno=%d\n%s\n",
-                      errno, strerror(errno));
+       printfPQExpBuffer(&conn->errorMessage,
+                         "connectDB() -- getsockname() failed: errno=%d\n%s\n",
+                         errno, strerror(errno));
        goto connect_errReturn;
    }
 
@@ -648,9 +654,9 @@ connectDB(PGconn *conn)
 
    if (pqPacketSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK)
    {
-       sprintf(conn->errorMessage,
-         "connectDB() --  couldn't send startup packet: errno=%d\n%s\n",
-               errno, strerror(errno));
+       printfPQExpBuffer(&conn->errorMessage,
+                         "connectDB() --  couldn't send startup packet: errno=%d\n%s\n",
+                         errno, strerror(errno));
        goto connect_errReturn;
    }
 
@@ -681,7 +687,7 @@ connectDB(PGconn *conn)
        /* Handle errors. */
        if (beresp == 'E')
        {
-           if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn))
+           if (pqGets(&conn->errorMessage, conn))
                continue;
            goto connect_errReturn;
        }
@@ -689,8 +695,8 @@ connectDB(PGconn *conn)
        /* Otherwise it should be an authentication request. */
        if (beresp != 'R')
        {
-           (void) sprintf(conn->errorMessage,
-                    "connectDB() -- expected authentication request\n");
+           printfPQExpBuffer(&conn->errorMessage,
+                             "connectDB() -- expected authentication request\n");
            goto connect_errReturn;
        }
 
@@ -709,9 +715,14 @@ connectDB(PGconn *conn)
        conn->inStart = conn->inCursor;
 
        /* Respond to the request if necessary. */
+       /* fe-auth.c has not been fixed to support PQExpBuffers, so: */
        if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
-                       conn->errorMessage) != STATUS_OK)
+                       conn->errorMessage.data) != STATUS_OK)
+       {
+           conn->errorMessage.len = strlen(conn->errorMessage.data);
            goto connect_errReturn;
+       }
+
        if (pqFlush(conn))
            goto connect_errReturn;
 
@@ -737,36 +748,12 @@ connectDB(PGconn *conn)
    if (res)
    {
        if (res->resultStatus != PGRES_FATAL_ERROR)
-           sprintf(conn->errorMessage,
-                   "connectDB() -- unexpected message during startup\n");
+           printfPQExpBuffer(&conn->errorMessage,
+                             "connectDB() -- unexpected message during startup\n");
        PQclear(res);
        goto connect_errReturn;
    }
 
-   /*
-    * Given the new protocol that sends a ReadyForQuery message after
-    * successful backend startup, it should no longer be necessary to
-    * send an empty query to test for startup.
-    */
-
-#ifdef NOT_USED
-
-   /*
-    * Send a blank query to make sure everything works; in particular,
-    * that the database exists.
-    */
-   res = PQexec(conn, " ");
-   if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
-   {
-       /* PQexec has put error message in conn->errorMessage */
-       closePGconn(conn);
-       PQclear(res);
-       goto connect_errReturn;
-   }
-   PQclear(res);
-
-#endif
-
    /*
     * Post-connection housekeeping. Send environment variables to server
     */
@@ -870,11 +857,27 @@ makeEmptyPGconn(void)
    conn->asyncStatus = PGASYNC_IDLE;
    conn->notifyList = DLNewList();
    conn->sock = -1;
-   conn->inBufSize = PQ_BUFFER_SIZE;
+   /*
+    * The output buffer size is set to 8K, which is the usual size of pipe
+    * buffers on Unix systems.  That way, when we are sending a large
+    * amount of data, we avoid incurring extra kernel context swaps for
+    * partial bufferloads.  Note that we currently don't ever enlarge
+    * the output buffer.
+    *
+    * With the same goal of minimizing context swaps, the input buffer will
+    * be enlarged anytime it has less than 8K free, so we initially allocate
+    * twice that.
+    */
+   conn->inBufSize = 16 * 1024;
    conn->inBuffer = (char *) malloc(conn->inBufSize);
-   conn->outBufSize = PQ_BUFFER_SIZE;
+   conn->outBufSize = 8 * 1024;
    conn->outBuffer = (char *) malloc(conn->outBufSize);
-   if (conn->inBuffer == NULL || conn->outBuffer == NULL)
+   initPQExpBuffer(&conn->errorMessage);
+   initPQExpBuffer(&conn->workBuffer);
+   if (conn->inBuffer == NULL ||
+       conn->outBuffer == NULL ||
+       conn->errorMessage.data == NULL ||
+       conn->workBuffer.data == NULL)
    {
        freePGconn(conn);
        conn = NULL;
@@ -922,6 +925,8 @@ freePGconn(PGconn *conn)
        free(conn->inBuffer);
    if (conn->outBuffer)
        free(conn->outBuffer);
+   termPQExpBuffer(&conn->errorMessage);
+   termPQExpBuffer(&conn->workBuffer);
    free(conn);
 }
 
@@ -1002,16 +1007,24 @@ PQreset(PGconn *conn)
  * PQrequestCancel: attempt to request cancellation of the current operation.
  *
  * The return value is TRUE if the cancel request was successfully
- * dispatched, FALSE if not (in which case errorMessage is set).
+ * dispatched, FALSE if not (in which case conn->errorMessage is set).
  * Note: successful dispatch is no guarantee that there will be any effect at
  * the backend.  The application must read the operation result as usual.
  *
+ * XXX it was a bad idea to have the error message returned in
+ * conn->errorMessage, since it could overwrite a message already there.
+ * Would be better to return it in a char array passed by the caller.
+ *
  * CAUTION: we want this routine to be safely callable from a signal handler
  * (for example, an application might want to call it in a SIGINT handler).
  * This means we cannot use any C library routine that might be non-reentrant.
  * malloc/free are often non-reentrant, and anything that might call them is
  * just as dangerous.  We avoid sprintf here for that reason.  Building up
  * error messages with strcpy/strcat is tedious but should be quite safe.
+ *
+ * NOTE: this routine must not generate any error message longer than
+ * INITIAL_EXPBUFFER_SIZE (currently 256), since we dare not try to
+ * expand conn->errorMessage!
  */
 
 int
@@ -1030,8 +1043,9 @@ PQrequestCancel(PGconn *conn)
 
    if (conn->sock < 0)
    {
-       strcpy(conn->errorMessage,
+       strcpy(conn->errorMessage.data,
               "PQrequestCancel() -- connection is not open\n");
+       conn->errorMessage.len = strlen(conn->errorMessage.data);
        return FALSE;
    }
 
@@ -1041,12 +1055,14 @@ PQrequestCancel(PGconn *conn)
     */
    if ((tmpsock = socket(conn->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
    {
-       strcpy(conn->errorMessage, "PQrequestCancel() -- socket() failed: ");
+       strcpy(conn->errorMessage.data,
+              "PQrequestCancel() -- socket() failed: ");
        goto cancel_errReturn;
    }
    if (connect(tmpsock, &conn->raddr.sa, conn->raddr_len) < 0)
    {
-       strcpy(conn->errorMessage, "PQrequestCancel() -- connect() failed: ");
+       strcpy(conn->errorMessage.data,
+              "PQrequestCancel() -- connect() failed: ");
        goto cancel_errReturn;
    }
 
@@ -1063,7 +1079,8 @@ PQrequestCancel(PGconn *conn)
 
    if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
    {
-       strcpy(conn->errorMessage, "PQrequestCancel() -- send() failed: ");
+       strcpy(conn->errorMessage.data,
+              "PQrequestCancel() -- send() failed: ");
        goto cancel_errReturn;
    }
 
@@ -1077,8 +1094,9 @@ PQrequestCancel(PGconn *conn)
    return TRUE;
 
 cancel_errReturn:
-   strcat(conn->errorMessage, strerror(errno));
-   strcat(conn->errorMessage, "\n");
+   strcat(conn->errorMessage.data, strerror(errno));
+   strcat(conn->errorMessage.data, "\n");
+   conn->errorMessage.len = strlen(conn->errorMessage.data);
    if (tmpsock >= 0)
    {
 #ifdef WIN32
@@ -1123,7 +1141,7 @@ pqPacketSend(PGconn *conn, const char *buf, size_t len)
  * ----------------
  */
 static int
-conninfo_parse(const char *conninfo, char *errorMessage)
+conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
 {
    char       *pname;
    char       *pval;
@@ -1132,13 +1150,13 @@ conninfo_parse(const char *conninfo, char *errorMessage)
    char       *cp;
    char       *cp2;
    PQconninfoOption *option;
-   char        errortmp[ERROR_MSG_LENGTH];
+   char        errortmp[INITIAL_EXPBUFFER_SIZE];
 
    conninfo_free();
 
    if ((buf = strdup(conninfo)) == NULL)
    {
-       strcpy(errorMessage,
+       printfPQExpBuffer(errorMessage,
          "FATAL: cannot allocate memory for copy of conninfo string\n");
        return -1;
    }
@@ -1176,9 +1194,9 @@ conninfo_parse(const char *conninfo, char *errorMessage)
        /* Check that there is a following '=' */
        if (*cp != '=')
        {
-           sprintf(errorMessage,
-           "ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n",
-                   pname);
+           printfPQExpBuffer(errorMessage,
+               "ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n",
+                             pname);
            free(buf);
            return -1;
        }
@@ -1223,7 +1241,7 @@ conninfo_parse(const char *conninfo, char *errorMessage)
            {
                if (*cp == '\0')
                {
-                   sprintf(errorMessage,
+                   printfPQExpBuffer(errorMessage,
                            "ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
                    free(buf);
                    return -1;
@@ -1257,9 +1275,9 @@ conninfo_parse(const char *conninfo, char *errorMessage)
        }
        if (option->keyword == NULL)
        {
-           sprintf(errorMessage,
-                   "ERROR: PQconnectdb() - unknown option '%s'\n",
-                   pname);
+           printfPQExpBuffer(errorMessage,
+                             "ERROR: PQconnectdb() - unknown option '%s'\n",
+                             pname);
            free(buf);
            return -1;
        }
@@ -1314,6 +1332,7 @@ conninfo_parse(const char *conninfo, char *errorMessage)
        if (!strcmp(option->keyword, "user"))
        {
            option->val = fe_getauthname(errortmp);
+           /* note any error message is thrown away */
            continue;
        }
 
@@ -1436,7 +1455,7 @@ PQerrorMessage(PGconn *conn)
 
    if (!conn)
        return noConn;
-   return conn->errorMessage;
+   return conn->errorMessage.data;
 }
 
 int
index 01613e0db84ca9797d78e2a168c884f534aa52b5..24fe9860ebbd65df2fe0b2523d4f491862f216c8 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.84 1999/07/19 06:25:39 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.85 1999/08/31 01:37:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,9 @@ const char *const pgresStatus[] = {
    ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
 
 
+static void pqCatenateResultError(PGresult *res, const char *msg);
+static void saveErrorResult(PGconn *conn);
+static PGresult *prepareAsyncResult(PGconn *conn);
 static int addTuple(PGresult *res, PGresAttValue *tup);
 static void parseInput(PGconn *conn);
 static void handleSendFailure(PGconn *conn);
@@ -158,7 +161,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
                /* non-error cases */
                break;
            default:
-               pqSetResultError(result, conn->errorMessage);
+               pqSetResultError(result, conn->errorMessage.data);
                break;
        }
    }
@@ -298,6 +301,25 @@ pqSetResultError(PGresult *res, const char *msg)
        res->errMsg = NULL;
 }
 
+/*
+ * pqCatenateResultError -
+ *     concatenate a new error message to the one already in a PGresult
+ */
+static void
+pqCatenateResultError(PGresult *res, const char *msg)
+{
+   PQExpBufferData  errorBuf;
+
+   if (!res || !msg)
+       return;
+   initPQExpBuffer(&errorBuf);
+   if (res->errMsg)
+       appendPQExpBufferStr(&errorBuf, res->errMsg);
+   appendPQExpBufferStr(&errorBuf, msg);
+   pqSetResultError(res, errorBuf.data);
+   termPQExpBuffer(&errorBuf);
+}
+
 /*
  * PQclear -
  *   free's the memory associated with a PGresult
@@ -338,6 +360,72 @@ pqClearAsyncResult(PGconn *conn)
    conn->curTuple = NULL;
 }
 
+/*
+ * This subroutine deletes any existing async result, sets conn->result
+ * to a PGresult with status PGRES_FATAL_ERROR, and stores the current
+ * contents of conn->errorMessage into that result.  It differs from a
+ * plain call on PQmakeEmptyPGresult() in that if there is already an
+ * async result with status PGRES_FATAL_ERROR, the current error message
+ * is APPENDED to the old error message instead of replacing it.  This
+ * behavior lets us report multiple error conditions properly, if necessary.
+ * (An example where this is needed is when the backend sends an 'E' message
+ * and immediately closes the connection --- we want to report both the
+ * backend error and the connection closure error.)
+ */
+static void
+saveErrorResult(PGconn *conn)
+{
+   /* If no old async result, just let PQmakeEmptyPGresult make one.
+    * Likewise if old result is not an error message.
+    */
+   if (conn->result == NULL ||
+       conn->result->resultStatus != PGRES_FATAL_ERROR ||
+       conn->result->errMsg == NULL)
+   {
+       pqClearAsyncResult(conn);
+       conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+   }
+   else
+   {
+       /* Else, concatenate error message to existing async result. */
+       pqCatenateResultError(conn->result, conn->errorMessage.data);
+   }
+}
+
+/*
+ * This subroutine prepares an async result object for return to the caller.
+ * If there is not already an async result object, build an error object
+ * using whatever is in conn->errorMessage.  In any case, clear the async
+ * result storage and make sure PQerrorMessage will agree with the result's
+ * error string.
+ */
+static PGresult *
+prepareAsyncResult(PGconn *conn)
+{
+   PGresult   *res;
+
+   /*
+    * conn->result is the PGresult to return.  If it is NULL
+    * (which probably shouldn't happen) we assume there is an
+    * appropriate error message in conn->errorMessage.
+    */
+   res = conn->result;
+   conn->result = NULL;        /* handing over ownership to caller */
+   conn->curTuple = NULL;      /* just in case */
+   if (!res)
+       res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+   else
+   {
+       /*
+        * Make sure PQerrorMessage agrees with result; it could
+        * be different if we have concatenated messages.
+        */
+       resetPQExpBuffer(&conn->errorMessage);
+       appendPQExpBufferStr(&conn->errorMessage,
+                            PQresultErrorMessage(res));
+   }
+   return res;
+}
 
 /*
  * addTuple
@@ -394,37 +482,33 @@ PQsendQuery(PGconn *conn, const char *query)
 {
    if (!conn)
        return 0;
+
+   /* clear the error string */
+   resetPQExpBuffer(&conn->errorMessage);
+
    if (!query)
    {
-       sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
-       return 0;
-   }
-   /* check to see if the query string is too long */
-   if (strlen(query) > MAX_MESSAGE_LEN - 2)
-   {
-       sprintf(conn->errorMessage, "PQsendQuery() -- query is too long.  "
-               "Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "PQsendQuery() -- query pointer is null.\n");
        return 0;
    }
 
    /* Don't try to send if we know there's no live connection. */
    if (conn->status != CONNECTION_OK)
    {
-       sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "
-               "to the backend.\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "PQsendQuery() -- There is no connection "
+                         "to the backend.\n");
        return 0;
    }
    /* Can't send while already busy, either. */
    if (conn->asyncStatus != PGASYNC_IDLE)
    {
-       sprintf(conn->errorMessage,
-               "PQsendQuery() -- another query already in progress.");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "PQsendQuery() -- another query already in progress.\n");
        return 0;
    }
 
-   /* clear the error string */
-   conn->errorMessage[0] = '\0';
-
    /* initialize async result-accumulation state */
    conn->result = NULL;
    conn->curTuple = NULL;
@@ -456,9 +540,6 @@ PQsendQuery(PGconn *conn, const char *query)
 static void
 handleSendFailure(PGconn *conn)
 {
-   /* Preserve the error message emitted by the failing output routine */
-   char * svErrMsg = strdup(conn->errorMessage);
-
    /*
     * Accept any available input data, ignoring errors.  Note that if
     * pqReadData decides the backend has closed the channel, it will
@@ -472,11 +553,6 @@ handleSendFailure(PGconn *conn)
     * state, only NOTICE and NOTIFY messages will be eaten.
     */
    parseInput(conn);
-
-   /* Restore error message generated by output routine, if any. */
-   if (*svErrMsg != '\0')
-       strcpy(conn->errorMessage, svErrMsg);
-   free(svErrMsg);
 }
 
 /*
@@ -514,6 +590,7 @@ static void
 parseInput(PGconn *conn)
 {
    char        id;
+   char        noticeWorkspace[128];
 
    /*
     * Loop to parse successive complete messages available in the buffer.
@@ -565,10 +642,10 @@ parseInput(PGconn *conn)
            {
                if (conn->asyncStatus == PGASYNC_IDLE)
                {
-                   sprintf(conn->errorMessage,
-                     "Backend message type 0x%02x arrived while idle\n",
+                   sprintf(noticeWorkspace,
+                           "Backend message type 0x%02x arrived while idle\n",
                            id);
-                   DONOTICE(conn, conn->errorMessage);
+                   DONOTICE(conn, noticeWorkspace);
                    /* Discard the unexpected message; good idea?? */
                    conn->inStart = conn->inEnd;
                }
@@ -577,21 +654,20 @@ parseInput(PGconn *conn)
            switch (id)
            {
                case 'C':       /* command complete */
+                   if (pqGets(&conn->workBuffer, conn))
+                       return;
                    if (conn->result == NULL)
                        conn->result = PQmakeEmptyPGresult(conn,
-                                                      PGRES_COMMAND_OK);
-                   if (pqGets(conn->result->cmdStatus, CMDSTATUS_LEN, conn))
-                       return;
+                                                          PGRES_COMMAND_OK);
+                   strncpy(conn->result->cmdStatus, conn->workBuffer.data,
+                           CMDSTATUS_LEN);
                    conn->asyncStatus = PGASYNC_READY;
                    break;
                case 'E':       /* error return */
-                   if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+                   if (pqGets(& conn->errorMessage, conn))
                        return;
-                   /* delete any partially constructed result */
-                   pqClearAsyncResult(conn);
-                   /* and build an error result holding the error message */
-                   conn->result = PQmakeEmptyPGresult(conn,
-                                                      PGRES_FATAL_ERROR);
+                   /* build an error result holding the error message */
+                   saveErrorResult(conn);
                    conn->asyncStatus = PGASYNC_READY;
                    break;
                case 'Z':       /* backend is ready for new query */
@@ -603,9 +679,10 @@ parseInput(PGconn *conn)
                        return;
                    if (id != '\0')
                    {
-                       sprintf(conn->errorMessage,
-                         "unexpected character %c following 'I'\n", id);
-                       DONOTICE(conn, conn->errorMessage);
+                       sprintf(noticeWorkspace,
+                               "unexpected character %c following 'I'\n",
+                               id);
+                       DONOTICE(conn, noticeWorkspace);
                    }
                    if (conn->result == NULL)
                        conn->result = PQmakeEmptyPGresult(conn,
@@ -625,7 +702,7 @@ parseInput(PGconn *conn)
                        return;
                    break;
                case 'P':       /* synchronous (normal) portal */
-                   if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+                   if (pqGets(&conn->workBuffer, conn))
                        return;
                    /* We pretty much ignore this message type... */
                    break;
@@ -660,9 +737,9 @@ parseInput(PGconn *conn)
                    }
                    else
                    {
-                       sprintf(conn->errorMessage,
-                            "Backend sent D message without prior T\n");
-                       DONOTICE(conn, conn->errorMessage);
+                       sprintf(noticeWorkspace,
+                               "Backend sent D message without prior T\n");
+                       DONOTICE(conn, noticeWorkspace);
                        /* Discard the unexpected message; good idea?? */
                        conn->inStart = conn->inEnd;
                        return;
@@ -677,9 +754,9 @@ parseInput(PGconn *conn)
                    }
                    else
                    {
-                       sprintf(conn->errorMessage,
-                            "Backend sent B message without prior T\n");
-                       DONOTICE(conn, conn->errorMessage);
+                       sprintf(noticeWorkspace,
+                               "Backend sent B message without prior T\n");
+                       DONOTICE(conn, noticeWorkspace);
                        /* Discard the unexpected message; good idea?? */
                        conn->inStart = conn->inEnd;
                        return;
@@ -692,18 +769,15 @@ parseInput(PGconn *conn)
                    conn->asyncStatus = PGASYNC_COPY_OUT;
                    break;
                default:
-                   sprintf(conn->errorMessage,
-                   "unknown protocol character '%c' read from backend.  "
-                   "(The protocol character is the first character the "
-                           "backend sends in response to a query it receives).\n",
-                           id);
+                   printfPQExpBuffer(&conn->errorMessage,
+                       "Unknown protocol character '%c' read from backend.  "
+                       "(The protocol character is the first character the "
+                       "backend sends in response to a query it receives).\n",
+                       id);
+                   /* build an error result holding the error message */
+                   saveErrorResult(conn);
                    /* Discard the unexpected message; good idea?? */
                    conn->inStart = conn->inEnd;
-                   /* delete any partially constructed result */
-                   pqClearAsyncResult(conn);
-                   /* and build an error result holding the error message */
-                   conn->result = PQmakeEmptyPGresult(conn,
-                                                      PGRES_FATAL_ERROR);
                    conn->asyncStatus = PGASYNC_READY;
                    return;
            }                   /* switch on protocol character */
@@ -753,12 +827,11 @@ getRowDescriptions(PGconn *conn)
    /* get type info */
    for (i = 0; i < nfields; i++)
    {
-       char        typName[MAX_MESSAGE_LEN];
        int         typid;
        int         typlen;
        int         atttypmod;
 
-       if (pqGets(typName, MAX_MESSAGE_LEN, conn) ||
+       if (pqGets(&conn->workBuffer, conn) ||
            pqGetInt(&typid, 4, conn) ||
            pqGetInt(&typlen, 2, conn) ||
            pqGetInt(&atttypmod, 4, conn))
@@ -777,7 +850,8 @@ getRowDescriptions(PGconn *conn)
         */
        if (typlen == 0xFFFF)
            typlen = -1;
-       result->attDescs[i].name = pqResultStrdup(result, typName);
+       result->attDescs[i].name = pqResultStrdup(result,
+                                                 conn->workBuffer.data);
        result->attDescs[i].typid = typid;
        result->attDescs[i].typlen = typlen;
        result->attDescs[i].atttypmod = atttypmod;
@@ -804,8 +878,9 @@ getAnotherTuple(PGconn *conn, int binary)
    PGresult   *result = conn->result;
    int         nfields = result->numAttributes;
    PGresAttValue *tup;
-   char        bitmap[MAX_FIELDS];     /* the backend sends us a bitmap
-                                        * of which attributes are null */
+   /* the backend sends us a bitmap of which attributes are null */
+   char        std_bitmap[64]; /* used unless it doesn't fit */
+   char       *bitmap = std_bitmap;
    int         i;
    int         nbytes;         /* the number of bytes in bitmap  */
    char        bmap;           /* One byte of the bitmap */
@@ -828,21 +903,12 @@ getAnotherTuple(PGconn *conn, int binary)
 
    /* Get the null-value bitmap */
    nbytes = (nfields + BYTELEN - 1) / BYTELEN;
-   if (nbytes >= MAX_FIELDS)
-   {
-       /* Replace partially constructed result with an error result */
-       pqClearAsyncResult(conn);
-       sprintf(conn->errorMessage,
-               "getAnotherTuple() -- null-values bitmap is too large\n");
-       conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-       conn->asyncStatus = PGASYNC_READY;
-       /* Discard the broken message */
-       conn->inStart = conn->inEnd;
-       return EOF;
-   }
+   /* malloc() only for unusually large field counts... */
+   if (nbytes > sizeof(std_bitmap))
+       bitmap = (char *) malloc(nbytes);
 
    if (pqGetnchar(bitmap, nbytes, conn))
-       return EOF;
+       goto EOFexit;
 
    /* Scan the fields */
    bitmap_index = 0;
@@ -861,7 +927,7 @@ getAnotherTuple(PGconn *conn, int binary)
        {
            /* get the value length (the first four bytes are for length) */
            if (pqGetInt(&vlen, 4, conn))
-               return EOF;
+               goto EOFexit;
            if (binary == 0)
                vlen = vlen - 4;
            if (vlen < 0)
@@ -876,7 +942,7 @@ getAnotherTuple(PGconn *conn, int binary)
            /* read in the value */
            if (vlen > 0)
                if (pqGetnchar((char *) (tup[i].value), vlen, conn))
-                   return EOF;
+                   goto EOFexit;
            /* we have to terminate this ourselves */
            tup[i].value[vlen] = '\0';
        }
@@ -897,17 +963,27 @@ getAnotherTuple(PGconn *conn, int binary)
        goto outOfMemory;
    /* and reset for a new message */
    conn->curTuple = NULL;
+
+   if (bitmap != std_bitmap)
+       free(bitmap);
    return 0;
 
 outOfMemory:
    /* Replace partially constructed result with an error result */
+   /* we do NOT use saveErrorResult() here, because of the likelihood
+    * that there's not enough memory to concatenate messages...
+    */
    pqClearAsyncResult(conn);
-   sprintf(conn->errorMessage,
-           "getAnotherTuple() -- out of memory for result\n");
+   printfPQExpBuffer(&conn->errorMessage,
+                     "getAnotherTuple() -- out of memory for result\n");
    conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
    conn->asyncStatus = PGASYNC_READY;
    /* Discard the failed message --- good idea? */
    conn->inStart = conn->inEnd;
+
+EOFexit:
+   if (bitmap != std_bitmap)
+       free(bitmap);
    return EOF;
 }
 
@@ -955,10 +1031,12 @@ PQgetResult(PGconn *conn)
        if (pqWait(TRUE, FALSE, conn) ||
            pqReadData(conn) < 0)
        {
-           pqClearAsyncResult(conn);
+           /* conn->errorMessage has been set by pqWait or pqReadData.
+            * We want to append it to any already-received error message.
+            */
+           saveErrorResult(conn);
            conn->asyncStatus = PGASYNC_IDLE;
-           /* conn->errorMessage has been set by pqWait or pqReadData. */
-           return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+           return prepareAsyncResult(conn);
        }
        /* Parse it. */
        parseInput(conn);
@@ -971,28 +1049,7 @@ PQgetResult(PGconn *conn)
            res = NULL;         /* query is complete */
            break;
        case PGASYNC_READY:
-
-           /*
-            * conn->result is the PGresult to return.  If it is NULL
-            * (which probably shouldn't happen) we assume there is an
-            * appropriate error message in conn->errorMessage.
-            */
-           res = conn->result;
-           conn->result = NULL;/* handing over ownership to caller */
-           conn->curTuple = NULL;      /* just in case */
-           if (!res)
-               res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-           else
-           {
-
-               /*
-                * Make sure PQerrorMessage agrees with result; it could
-                * be that we have done other operations that changed
-                * errorMessage since the result's error message was
-                * saved.
-                */
-               strcpy(conn->errorMessage, PQresultErrorMessage(res));
-           }
+           res = prepareAsyncResult(conn);
            /* Set the state back to BUSY, allowing parsing to proceed. */
            conn->asyncStatus = PGASYNC_BUSY;
            break;
@@ -1003,9 +1060,9 @@ PQgetResult(PGconn *conn)
            res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
            break;
        default:
-           sprintf(conn->errorMessage,
-                   "PQgetResult: Unexpected asyncStatus %d\n",
-                   (int) conn->asyncStatus);
+           printfPQExpBuffer(&conn->errorMessage,
+                             "PQgetResult: Unexpected asyncStatus %d\n",
+                             (int) conn->asyncStatus);
            res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
            break;
    }
@@ -1043,7 +1100,7 @@ PQexec(PGconn *conn, const char *query)
            result->resultStatus == PGRES_COPY_OUT)
        {
            PQclear(result);
-           sprintf(conn->errorMessage,
+           printfPQExpBuffer(&conn->errorMessage,
                "PQexec: you gotta get out of a COPY state yourself.\n");
            return NULL;
        }
@@ -1056,14 +1113,30 @@ PQexec(PGconn *conn, const char *query)
 
    /*
     * For backwards compatibility, return the last result if there are
-    * more than one.  We have to stop if we see copy in/out, however. We
-    * will resume parsing when application calls PQendcopy.
+    * more than one --- but merge error messages if we get more than one
+    * error result.
+    *
+    * We have to stop if we see copy in/out, however.
+    * We will resume parsing when application calls PQendcopy.
     */
    lastResult = NULL;
    while ((result = PQgetResult(conn)) != NULL)
    {
        if (lastResult)
-           PQclear(lastResult);
+       {
+           if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
+               result->resultStatus == PGRES_FATAL_ERROR)
+           {
+               pqCatenateResultError(lastResult, result->errMsg);
+               PQclear(result);
+               result = lastResult;
+               /* Make sure PQerrorMessage agrees with catenated result */
+               resetPQExpBuffer(&conn->errorMessage);
+               appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
+           }
+           else
+               PQclear(lastResult);
+       }
        lastResult = result;
        if (result->resultStatus == PGRES_COPY_IN ||
            result->resultStatus == PGRES_COPY_OUT)
@@ -1083,9 +1156,20 @@ PQexec(PGconn *conn, const char *query)
 static int
 getNotice(PGconn *conn)
 {
-   if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+   /* Since the Notice might be pretty long, we create a temporary
+    * PQExpBuffer rather than using conn->workBuffer.  workBuffer is
+    * intended for stuff that is expected to be short.
+    */
+   PQExpBufferData  noticeBuf;
+
+   initPQExpBuffer(¬iceBuf);
+   if (pqGets(¬iceBuf, conn))
+   {
+       termPQExpBuffer(¬iceBuf);
        return EOF;
-   DONOTICE(conn, conn->errorMessage);
+   }
+   DONOTICE(conn, noticeBuf.data);
+   termPQExpBuffer(¬iceBuf);
    return 0;
 }
 
@@ -1099,15 +1183,16 @@ getNotice(PGconn *conn)
 static int
 getNotify(PGconn *conn)
 {
-   PGnotify    tempNotify;
+   int         be_pid;
    PGnotify   *newNotify;
 
-   if (pqGetInt(&(tempNotify.be_pid), 4, conn))
+   if (pqGetInt(&be_pid, 4, conn))
        return EOF;
-   if (pqGets(tempNotify.relname, NAMEDATALEN, conn))
+   if (pqGets(&conn->workBuffer, conn))
        return EOF;
    newNotify = (PGnotify *) malloc(sizeof(PGnotify));
-   memcpy(newNotify, &tempNotify, sizeof(PGnotify));
+   strncpy(newNotify->relname, conn->workBuffer.data, NAMEDATALEN);
+   newNotify->be_pid = be_pid;
    DLAddTail(conn->notifyList, DLNewElem(newNotify));
    return 0;
 }
@@ -1342,8 +1427,8 @@ PQendcopy(PGconn *conn)
    if (conn->asyncStatus != PGASYNC_COPY_IN &&
        conn->asyncStatus != PGASYNC_COPY_OUT)
    {
-       sprintf(conn->errorMessage,
-            "PQendcopy() -- I don't think there's a copy in progress.");
+       printfPQExpBuffer(&conn->errorMessage,
+            "PQendcopy() -- I don't think there's a copy in progress.\n");
        return 1;
    }
 
@@ -1351,7 +1436,7 @@ PQendcopy(PGconn *conn)
 
    /* Return to active duty */
    conn->asyncStatus = PGASYNC_BUSY;
-   conn->errorMessage[0] = '\0';
+   resetPQExpBuffer(&conn->errorMessage);
 
    /* Wait for the completion response */
    result = PQgetResult(conn);
@@ -1370,8 +1455,8 @@ PQendcopy(PGconn *conn)
     */
    PQclear(result);
 
-   if (conn->errorMessage[0])
-       DONOTICE(conn, conn->errorMessage);
+   if (conn->errorMessage.len > 0)
+       DONOTICE(conn, conn->errorMessage.data);
 
    DONOTICE(conn, "PQendcopy: resetting connection\n");
 
@@ -1423,15 +1508,17 @@ PQfn(PGconn *conn,
    if (!conn)
        return NULL;
 
-   if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE)
+   /* clear the error string */
+   resetPQExpBuffer(&conn->errorMessage);
+
+   if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE ||
+       conn->result != NULL)
    {
-       sprintf(conn->errorMessage, "PQfn() -- connection in wrong state\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "PQfn() -- connection in wrong state\n");
        return NULL;
    }
 
-   /* clear the error string */
-   conn->errorMessage[0] = '\0';
-
    if (pqPuts("F ", conn) ||           /* function */
        pqPutInt(fnid, 4, conn) ||      /* function id */
        pqPutInt(nargs, 4, conn))       /* # of args */
@@ -1529,15 +1616,19 @@ PQfn(PGconn *conn,
                else
                {
                    /* The backend violates the protocol. */
-                   sprintf(conn->errorMessage,
-                           "FATAL: PQfn: protocol error: id=%x\n", id);
+                   printfPQExpBuffer(&conn->errorMessage,
+                                     "FATAL: PQfn: protocol error: id=0x%x\n",
+                                     id);
+                   saveErrorResult(conn);
                    conn->inStart = conn->inCursor;
-                   return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+                   return prepareAsyncResult(conn);
                }
                break;
            case 'E':           /* error return */
-               if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+               if (pqGets(&conn->errorMessage, conn))
                    continue;
+               /* build an error result holding the error message */
+               saveErrorResult(conn);
                status = PGRES_FATAL_ERROR;
                break;
            case 'A':           /* notify message */
@@ -1553,21 +1644,30 @@ PQfn(PGconn *conn,
            case 'Z':           /* backend is ready for new query */
                /* consume the message and exit */
                conn->inStart = conn->inCursor;
+               /* if we saved a result object (probably an error), use it */
+               if (conn->result)
+                   return prepareAsyncResult(conn);
                return PQmakeEmptyPGresult(conn, status);
            default:
                /* The backend violates the protocol. */
-               sprintf(conn->errorMessage,
-                       "FATAL: PQfn: protocol error: id=%x\n", id);
+               printfPQExpBuffer(&conn->errorMessage,
+                                 "FATAL: PQfn: protocol error: id=0x%x\n",
+                                 id);
+               saveErrorResult(conn);
                conn->inStart = conn->inCursor;
-               return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+               return prepareAsyncResult(conn);
        }
        /* Completed this message, keep going */
        conn->inStart = conn->inCursor;
        needInput = false;
    }
 
-   /* we fall out of the loop only upon failing to read data */
-   return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+   /* We fall out of the loop only upon failing to read data.
+    * conn->errorMessage has been set by pqWait or pqReadData.
+    * We want to append it to any already-received error message.
+    */
+   saveErrorResult(conn);
+   return prepareAsyncResult(conn);
 }
 
 
@@ -1630,16 +1730,18 @@ PQbinaryTuples(PGresult *res)
 static int
 check_field_number(const char *routineName, PGresult *res, int field_num)
 {
+   char noticeBuf[128];
+
    if (!res)
        return FALSE;           /* no way to display error message... */
    if (field_num < 0 || field_num >= res->numAttributes)
    {
        if (res->conn)
        {
-           sprintf(res->conn->errorMessage,
+           sprintf(noticeBuf,
                    "%s: ERROR! field number %d is out of range 0..%d\n",
                    routineName, field_num, res->numAttributes - 1);
-           DONOTICE(res->conn, res->conn->errorMessage);
+           DONOTICE(res->conn, noticeBuf);
        }
        return FALSE;
    }
@@ -1650,16 +1752,18 @@ static int
 check_tuple_field_number(const char *routineName, PGresult *res,
                         int tup_num, int field_num)
 {
+   char noticeBuf[128];
+
    if (!res)
        return FALSE;           /* no way to display error message... */
    if (tup_num < 0 || tup_num >= res->ntups)
    {
        if (res->conn)
        {
-           sprintf(res->conn->errorMessage,
+           sprintf(noticeBuf,
                    "%s: ERROR! tuple number %d is out of range 0..%d\n",
                    routineName, tup_num, res->ntups - 1);
-           DONOTICE(res->conn, res->conn->errorMessage);
+           DONOTICE(res->conn, noticeBuf);
        }
        return FALSE;
    }
@@ -1667,10 +1771,10 @@ check_tuple_field_number(const char *routineName, PGresult *res,
    {
        if (res->conn)
        {
-           sprintf(res->conn->errorMessage,
+           sprintf(noticeBuf,
                    "%s: ERROR! field number %d is out of range 0..%d\n",
                    routineName, field_num, res->numAttributes - 1);
-           DONOTICE(res->conn, res->conn->errorMessage);
+           DONOTICE(res->conn, noticeBuf);
        }
        return FALSE;
    }
@@ -1830,6 +1934,8 @@ PQoidStatus(PGresult *res)
 const char *
 PQcmdTuples(PGresult *res)
 {
+   char noticeBuf[128];
+
    if (!res)
        return "";
 
@@ -1843,10 +1949,10 @@ PQcmdTuples(PGresult *res)
        {
            if (res->conn)
            {
-               sprintf(res->conn->errorMessage,
+               sprintf(noticeBuf,
                        "PQcmdTuples (%s) -- bad input from server\n",
                        res->cmdStatus);
-               DONOTICE(res->conn, res->conn->errorMessage);
+               DONOTICE(res->conn, noticeBuf);
            }
            return "";
        }
@@ -1859,9 +1965,9 @@ PQcmdTuples(PGresult *res)
        {
            if (res->conn)
            {
-               sprintf(res->conn->errorMessage,
+               sprintf(noticeBuf,
                     "PQcmdTuples (INSERT) -- there's no # of tuples\n");
-               DONOTICE(res->conn, res->conn->errorMessage);
+               DONOTICE(res->conn, noticeBuf);
            }
            return "";
        }
index a105d0344b095b01554a2e719345d0705be23ee7..8793e24cf897bac7148c6cb50acacf5a8e219fb3 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.24 1999/07/19 06:25:39 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.25 1999/08/31 01:37:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -397,8 +397,9 @@ lo_import(PGconn *conn, char *filename)
 #endif
    if (fd < 0)
    {                           /* error */
-       sprintf(conn->errorMessage,
-               "lo_import: can't open unix file\"%s\"\n", filename);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "lo_import: can't open unix file\"%s\"\n",
+                         filename);
        return InvalidOid;
    }
 
@@ -408,16 +409,18 @@ lo_import(PGconn *conn, char *filename)
    lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
    if (lobjOid == InvalidOid)
    {
-       sprintf(conn->errorMessage,
-             "lo_import: can't create inv object for \"%s\"", filename);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "lo_import: can't create inv object for \"%s\"",
+                         filename);
        return InvalidOid;
    }
 
    lobj = lo_open(conn, lobjOid, INV_WRITE);
    if (lobj == -1)
    {
-       sprintf(conn->errorMessage,
-               "lo_import: could not open inv object oid %u", lobjOid);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "lo_import: could not open inv object oid %u",
+                         lobjOid);
        return InvalidOid;
    }
 
@@ -429,8 +432,9 @@ lo_import(PGconn *conn, char *filename)
        tmp = lo_write(conn, lobj, buf, nbytes);
        if (tmp < nbytes)
        {
-           sprintf(conn->errorMessage,
-                   "lo_import: error while reading \"%s\"", filename);
+           printfPQExpBuffer(&conn->errorMessage,
+                             "lo_import: error while reading \"%s\"",
+                             filename);
            return InvalidOid;
        }
    }
@@ -461,8 +465,8 @@ lo_export(PGconn *conn, Oid lobjId, char *filename)
    lobj = lo_open(conn, lobjId, INV_READ);
    if (lobj == -1)
    {
-       sprintf(conn->errorMessage,
-               "lo_export: can't open inv object %u", lobjId);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "lo_export: can't open inv object %u", lobjId);
        return -1;
    }
 
@@ -476,8 +480,9 @@ lo_export(PGconn *conn, Oid lobjId, char *filename)
 #endif
    if (fd < 0)
    {                           /* error */
-       sprintf(conn->errorMessage,
-               "lo_export: can't open unix file\"%s\"", filename);
+       printfPQExpBuffer(&conn->errorMessage,
+                         "lo_export: can't open unix file\"%s\"",
+                         filename);
        return 0;
    }
 
@@ -489,9 +494,9 @@ lo_export(PGconn *conn, Oid lobjId, char *filename)
        tmp = write(fd, buf, nbytes);
        if (tmp < nbytes)
        {
-           sprintf(conn->errorMessage,
-                   "lo_export: error while writing \"%s\"",
-                   filename);
+           printfPQExpBuffer(&conn->errorMessage,
+                             "lo_export: error while writing \"%s\"",
+                             filename);
            return -1;
        }
    }
@@ -527,8 +532,8 @@ lo_initialize(PGconn *conn)
    lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
    if (lobjfuncs == (PGlobjfuncs *) NULL)
    {
-       strcpy(conn->errorMessage,
-              "FATAL: malloc() failed in lo_initialize()\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "FATAL: malloc() failed in lo_initialize()\n");
        return -1;
    }
    MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs));
@@ -556,8 +561,8 @@ lo_initialize(PGconn *conn)
    {
        free(lobjfuncs);
        PQclear(res);
-       strcpy(conn->errorMessage,
-              "ERROR: SELECT didn't return data in lo_initialize()\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: SELECT didn't return data in lo_initialize()\n");
        return -1;
    }
 
@@ -596,57 +601,57 @@ lo_initialize(PGconn *conn)
     */
    if (lobjfuncs->fn_lo_open == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_open\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_open\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_close == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_close\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_close\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_creat == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_creat\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_creat\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_unlink == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_unlink\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_unlink\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_lseek == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_lseek\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_lseek\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_tell == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lo_tell\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lo_tell\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_read == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function loread\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function loread\n");
        free(lobjfuncs);
        return -1;
    }
    if (lobjfuncs->fn_lo_write == 0)
    {
-       strcpy(conn->errorMessage,
-              "ERROR: Cannot determine OID for function lowrite\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "ERROR: Cannot determine OID for function lowrite\n");
        free(lobjfuncs);
        return -1;
    }
index 63558ec33c79674458e079023cc00fb83d0d0db4..84149d12773f34569bd22cb7042b499c5502f581 100644 (file)
@@ -24,7 +24,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.28 1999/07/19 06:25:40 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.29 1999/08/31 01:37:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,12 +106,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;
@@ -126,18 +126,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;
 }
@@ -202,6 +199,7 @@ pqGetInt(int *result, int bytes, PGconn *conn)
 {
    uint16      tmp2;
    uint32      tmp4;
+   char        noticeBuf[64];
 
    switch (bytes)
    {
@@ -220,9 +218,9 @@ pqGetInt(int *result, int bytes, PGconn *conn)
            *result = (int) ntohl(tmp4);
            break;
        default:
-           sprintf(conn->errorMessage,
+           sprintf(noticeBuf,
                    "pqGetInt: int size %d not supported\n", bytes);
-           DONOTICE(conn, conn->errorMessage);
+           DONOTICE(conn, noticeBuf);
            return EOF;
    }
 
@@ -242,6 +240,7 @@ pqPutInt(int value, int bytes, PGconn *conn)
 {
    uint16      tmp2;
    uint32      tmp4;
+   char        noticeBuf[64];
 
    switch (bytes)
    {
@@ -256,9 +255,9 @@ pqPutInt(int value, int bytes, PGconn *conn)
                return EOF;
            break;
        default:
-           sprintf(conn->errorMessage,
+           sprintf(noticeBuf,
                    "pqPutInt: int size %d not supported\n", bytes);
-           DONOTICE(conn, conn->errorMessage);
+           DONOTICE(conn, noticeBuf);
            return EOF;
    }
 
@@ -287,9 +286,9 @@ pqReadReady(PGconn *conn)
    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));
+       printfPQExpBuffer(&conn->errorMessage,
+                         "pqReadReady() -- select() failed: errno=%d\n%s\n",
+                         errno, strerror(errno));
        return 0;
    }
    return FD_ISSET(conn->sock, &input_mask);
@@ -312,7 +311,8 @@ pqReadData(PGconn *conn)
 
    if (conn->sock < 0)
    {
-       strcpy(conn->errorMessage, "pqReadData() -- connection not open\n");
+       printfPQExpBuffer(&conn->errorMessage,
+                         "pqReadData() -- connection not open\n");
        return -1;
    }
 
@@ -333,9 +333,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);
@@ -369,9 +370,9 @@ tryAgain:
        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)
@@ -417,9 +418,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)
@@ -433,7 +434,7 @@ tryAgain2:
     * This means the connection has been closed.  Cope.
     */
 definitelyFailed:
-   sprintf(conn->errorMessage,
+   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");
@@ -459,7 +460,8 @@ 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;
    }
 
@@ -499,7 +501,7 @@ pqFlush(PGconn *conn)
 #ifdef ECONNRESET
                case ECONNRESET:
 #endif
-                   sprintf(conn->errorMessage,
+                   printfPQExpBuffer(&conn->errorMessage,
                            "pqFlush() -- backend closed the channel unexpectedly.\n"
                            "\tThis probably means the backend terminated abnormally"
                            " before or while processing the request.\n");
@@ -513,8 +515,8 @@ pqFlush(PGconn *conn)
                    return EOF;
 
                default:
-                   sprintf(conn->errorMessage,
-                     "pqFlush() --  couldn't send data: errno=%d\n%s\n",
+                   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;
@@ -552,7 +554,8 @@ 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;
    }
 
@@ -570,9 +573,9 @@ pqWait(int forRead, int forWrite, PGconn *conn)
        {
            if (errno == EINTR)
                continue;
-           sprintf(conn->errorMessage,
-                   "pqWait() -- select() failed: errno=%d\n%s\n",
-                   errno, strerror(errno));
+           printfPQExpBuffer(&conn->errorMessage,
+                             "pqWait() -- select() failed: errno=%d\n%s\n",
+                             errno, strerror(errno));
            return EOF;
        }
        /* On nonerror return, assume we're done */
index 9151463fece11ae6567ea80bfb92280f9cca2677..02f3455dcbcbe947f4071acc4410083dd096002e 100644 (file)
@@ -9,7 +9,7 @@
  * didn't really belong there.
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.26 1999/07/19 06:25:40 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.27 1999/08/31 01:37:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,7 +51,7 @@ static struct winsize
 
 
 static void do_field(PQprintOpt *po, PGresult *res,
-        const int i, const int j, char *buf, const int fs_len,
+        const int i, const int j, const int fs_len,
         char **fields,
         const int nFields, char **fieldNames,
         unsigned char *fieldNotNum, int *fieldMax,
@@ -103,7 +103,6 @@ PQprint(FILE *fout,
        int         usePipe = 0;
        pqsigfunc   oldsigpipehandler = NULL;
        char       *pagerenv;
-       char        buf[MAX_QUERY_SIZE + 1];
 
        nTups = PQntuples(res);
        if (!(fieldNames = (char **) calloc(nFields, sizeof(char *))))
@@ -254,7 +253,7 @@ PQprint(FILE *fout,
                    fprintf(fout, "-- RECORD %d --\n", i);
            }
            for (j = 0; j < nFields; j++)
-               do_field(po, res, i, j, buf, fs_len, fields, nFields,
+               do_field(po, res, i, j, fs_len, fields, nFields,
                         fieldNames, fieldNotNum,
                         fieldMax, fieldMaxLen, fout);
            if (po->html3 && po->expanded)
@@ -332,7 +331,7 @@ PQdisplayTuples(PGresult *res,
                j;
    int         nFields;
    int         nTuples;
-   int         fLength[MAX_FIELDS];
+   int        *fLength = NULL;
 
    if (fieldSep == NULL)
        fieldSep = DEFAULT_FIELD_SEP;
@@ -344,19 +343,19 @@ PQdisplayTuples(PGresult *res,
    if (fp == NULL)
        fp = stdout;
 
-   /* Zero the initial field lengths */
-   for (j = 0; j < nFields; j++)
-       fLength[j] = strlen(PQfname(res, j));
-   /* Find the max length of each field in the result */
+   /* Figure the field lengths to align to */
    /* will be somewhat time consuming for very large results */
    if (fillAlign)
    {
-       for (i = 0; i < nTuples; i++)
+       fLength = (int *) malloc(nFields * sizeof(int));
+       for (j = 0; j < nFields; j++)
        {
-           for (j = 0; j < nFields; j++)
+           fLength[j] = strlen(PQfname(res, j));
+           for (i = 0; i < nTuples; i++)
            {
-               if (PQgetlength(res, i, j) > fLength[j])
-                   fLength[j] = PQgetlength(res, i, j);
+               int flen = PQgetlength(res, i, j);
+               if (flen > fLength[j])
+                   fLength[j] = flen;
            }
        }
    }
@@ -401,6 +400,9 @@ PQdisplayTuples(PGresult *res,
                (PQntuples(res) == 1) ? "" : "s");
 
    fflush(fp);
+
+   if (fLength)
+       free(fLength);
 }
 
 
@@ -522,7 +524,7 @@ PQmblen(unsigned char *s)
 
 static void
 do_field(PQprintOpt *po, PGresult *res,
-        const int i, const int j, char *buf, const int fs_len,
+        const int i, const int j, const int fs_len,
         char **fields,
         const int nFields, char **fieldNames,
         unsigned char *fieldNotNum, int *fieldMax,
@@ -530,8 +532,7 @@ do_field(PQprintOpt *po, PGresult *res,
 {
 
    char       *pval,
-              *p,
-              *o;
+              *p;
    int         plen;
    bool        skipit;
 
@@ -553,62 +554,49 @@ do_field(PQprintOpt *po, PGresult *res,
 
    if (!skipit)
    {
-       char        ch = 0;
+       if (po->align && ! fieldNotNum[j])
+       {
+           /* Detect whether field contains non-numeric data */
+           char        ch = '0';
 
 #ifdef MULTIBYTE
-       int         len;
-
-       for (p = pval, o = buf; *p;
-            len = PQmblen(p), memcpy(o, p, len),
-            o += len, p += len)
+           for (p = pval; *p; p += PQmblen(p))
 #else
-       for (p = pval, o = buf; *p; *(o++) = *(p++))
+           for (p = pval; *p; p++)
 #endif
-       {
-           ch = *p;
-
+           {
+               ch = *p;
+               if (! ((ch >= '0' && ch <= '9') ||
+                      ch == '.' ||
+                      ch == 'E' ||
+                      ch == 'e' ||
+                      ch == ' ' ||
+                      ch == '-'))
+               {
+                   fieldNotNum[j] = 1;
+                   break;
+               }
+           }
            /*
-            * Consensus on pgsql-interfaces (as of Aug 1998) seems to be
-            * that the print functions ought not insert backslashes.  If
-            * you like them, you can re-enable this next bit.
+            * Above loop will believe E in first column is numeric; also, we
+            * insist on a digit in the last column for a numeric.  This test
+            * is still not bulletproof but it handles most cases.
             */
-#ifdef GRATUITOUS_BACKSLASHES
-           if ((fs_len == 1 && (ch == *(po->fieldSep))) ||
-               ch == '\\' || ch == '\n')
-               *(o++) = '\\';
-#endif
-           if (po->align &&
-               !((ch >= '0' && ch <= '9') ||
-                 ch == '.' ||
-                 ch == 'E' ||
-                 ch == 'e' ||
-                 ch == ' ' ||
-                 ch == '-'))
+           if (*pval == 'E' || *pval == 'e' ||
+               !(ch >= '0' && ch <= '9'))
                fieldNotNum[j] = 1;
        }
-       *o = '\0';
-
-       /*
-        * Above loop will believe E in first column is numeric; also, we
-        * insist on a digit in the last column for a numeric.  This test
-        * is still not bulletproof but it handles most cases.
-        */
-       if (po->align &&
-           (*pval == 'E' || *pval == 'e' ||
-            !(ch >= '0' && ch <= '9')))
-           fieldNotNum[j] = 1;
+
        if (!po->expanded && (po->align || po->html3))
        {
-           int         n = strlen(buf);
-
-           if (n > fieldMax[j])
-               fieldMax[j] = n;
-           if (!(fields[i * nFields + j] = (char *) malloc(n + 1)))
+           if (plen > fieldMax[j])
+               fieldMax[j] = plen;
+           if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
            {
                perror("malloc");
                exit(1);
            }
-           strcpy(fields[i * nFields + j], buf);
+           strcpy(fields[i * nFields + j], pval);
        }
        else
        {
@@ -620,23 +608,26 @@ do_field(PQprintOpt *po, PGresult *res,
                            "%s\n",
                            fieldNames[j],
                            fieldNotNum[j] ? "left" : "right",
-                           buf);
+                           pval);
                else
                {
                    if (po->align)
                        fprintf(fout,
                                "%-*s%s %s\n",
-                       fieldMaxLen - fs_len, fieldNames[j], po->fieldSep,
-                               buf);
+                               fieldMaxLen - fs_len, fieldNames[j],
+                               po->fieldSep,
+                               pval);
                    else
-                       fprintf(fout, "%s%s%s\n", fieldNames[j], po->fieldSep, buf);
+                       fprintf(fout,
+                               "%s%s%s\n",
+                               fieldNames[j], po->fieldSep, pval);
                }
            }
            else
            {
                if (!po->html3)
                {
-                   fputs(buf, fout);
+                   fputs(pval, fout);
            efield:
                    if ((j + 1) < nFields)
                        fputs(po->fieldSep, fout);
index f3b788dd9304a1e6728f99c9f10c8cbec9fd73fc..d22d403e016e70793ec19e2601cc182886178e56 100644 (file)
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.10 1999/07/13 20:00:37 momjian Exp $
+ * $Id: libpq-int.h,v 1.11 1999/08/31 01:37:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 /* We assume libpq-fe.h has already been included. */
 
-/* ----------------
- *     include stuff common to fe and be
- * ----------------
- */
+/* include stuff common to fe and be */
 #include "libpq/pqcomm.h"
 #include "lib/dllist.h"
+/* include stuff found in fe only */
+#include "pqexpbuffer.h"
+
 
 /* libpq supports this version of the frontend/backend protocol.
  *
@@ -45,8 +45,6 @@
  * POSTGRES backend dependent Constants.
  */
 
-/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/
-#define ERROR_MSG_LENGTH 4096
 #define CMDSTATUS_LEN 40
 
 /*
@@ -115,7 +113,7 @@ struct pg_result
    int         tupArrSize;     /* size of tuples array allocated */
    ExecStatusType resultStatus;
    char        cmdStatus[CMDSTATUS_LEN];       /* cmd status from the
-                                                * last insert query */
+                                                * last query */
    int         binary;         /* binary tuple values if binary == 1,
                                 * otherwise ASCII */
    PGconn     *conn;           /* connection we did the query on, if any */
@@ -217,8 +215,11 @@ struct pg_conn
    PGresult   *result;         /* result being constructed */
    PGresAttValue *curTuple;    /* tuple currently being read */
 
-   /* Message space.  Placed last for code-size reasons. */
-   char        errorMessage[ERROR_MSG_LENGTH];
+   /* Buffer for current error message */
+   PQExpBufferData errorMessage;   /* expansible string */
+
+   /* Buffer for receiving various parts of messages */
+   PQExpBufferData workBuffer; /* expansible string */
 };
 
 /* ----------------
@@ -249,7 +250,7 @@ extern void pqClearAsyncResult(PGconn *conn);
   * necessarily any error.
   */
 extern int pqGetc(char *result, PGconn *conn);
-extern int pqGets(char *s, int maxlen, PGconn *conn);
+extern int pqGets(PQExpBuffer buf, PGconn *conn);
 extern int pqPuts(const char *s, PGconn *conn);
 extern int pqGetnchar(char *s, int len, PGconn *conn);
 extern int pqPutnchar(const char *s, int len, PGconn *conn);
@@ -259,12 +260,6 @@ extern int pqReadData(PGconn *conn);
 extern int pqFlush(PGconn *conn);
 extern int pqWait(int forRead, int forWrite, PGconn *conn);
 
-/* max length of message to send  */
-#define MAX_MESSAGE_LEN MAX_QUERY_SIZE
-
-/* maximum number of fields in a tuple */
-#define MAX_FIELDS 512
-
 /* bits in a byte */
 #define BYTELEN 8
 
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
new file mode 100644 (file)
index 0000000..ddaca7e
--- /dev/null
@@ -0,0 +1,254 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqexpbuffer.c
+ *
+ * PQExpBuffer provides an indefinitely-extensible string data type.
+ * It can be used to buffer either ordinary C strings (null-terminated text)
+ * or arbitrary binary data.  All storage is allocated with malloc().
+ *
+ * This module is essentially the same as the backend's StringInfo data type,
+ * but it is intended for use in frontend libpq and client applications.
+ * Thus, it does not rely on palloc(), elog(), nor vsnprintf().
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.1 1999/08/31 01:37:37 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "pqexpbuffer.h"
+
+/*
+ * createPQExpBuffer
+ *
+ * Create an empty 'PQExpBufferData' & return a pointer to it.
+ */
+PQExpBuffer
+createPQExpBuffer(void)
+{
+   PQExpBuffer res;
+
+   res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
+   if (res != NULL)
+       initPQExpBuffer(res);
+
+   return res;
+}
+
+/*
+ * initPQExpBuffer
+ *
+ * Initialize a PQExpBufferData struct (with previously undefined contents)
+ * to describe an empty string.
+ */
+void
+initPQExpBuffer(PQExpBuffer str)
+{
+   str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
+   if (str->data == NULL)
+   {
+       str->maxlen = 0;
+       str->len = 0;
+   }
+   else
+   {
+       str->maxlen = INITIAL_EXPBUFFER_SIZE;
+       str->len = 0;
+       str->data[0] = '\0';
+   }
+}
+
+/*------------------------
+ * destroyPQExpBuffer(str);
+ *     free()s both the data buffer and the PQExpBufferData.
+ *     This is the inverse of createPQExpBuffer().
+ */
+void
+destroyPQExpBuffer(PQExpBuffer str)
+{
+   if (str)
+   {
+       termPQExpBuffer(str);
+       free(str);
+   }
+}
+
+/*------------------------
+ * termPQExpBuffer(str)
+ *     free()s the data buffer but not the PQExpBufferData itself.
+ *     This is the inverse of initPQExpBuffer().
+ */
+void
+termPQExpBuffer(PQExpBuffer str)
+{
+   if (str->data)
+   {
+       free(str->data);
+       str->data = NULL;
+   }
+}
+
+/*------------------------
+ * resetPQExpBuffer
+ *     Reset a PQExpBuffer to empty
+ */
+void
+resetPQExpBuffer(PQExpBuffer str)
+{
+   if (str)
+   {
+       str->len = 0;
+       if (str->data)
+           str->data[0] = '\0';
+   }
+}
+
+/*------------------------
+ * enlargePQExpBuffer
+ * Make sure there is enough space for 'needed' more bytes in the buffer
+ * ('needed' does not include the terminating null).
+ *
+ * Returns 1 if OK, 0 if failed to enlarge buffer.
+ */
+int
+enlargePQExpBuffer(PQExpBuffer str, int needed)
+{
+   int         newlen;
+   char       *newdata;
+
+   needed += str->len + 1;     /* total space required now */
+   if (needed <= str->maxlen)
+       return 1;               /* got enough space already */
+
+   /*
+    * We don't want to allocate just a little more space with each
+    * append; for efficiency, double the buffer size each time it
+    * overflows. Actually, we might need to more than double it if
+    * 'needed' is big...
+    */
+   newlen = str->maxlen ? (2 * str->maxlen) : 64;
+   while (needed > newlen)
+       newlen = 2 * newlen;
+
+   newdata = (char *) realloc(str->data, newlen);
+   if (newdata != NULL)
+   {
+       str->data = newdata;
+       str->maxlen = newlen;
+       return 1;
+   }
+   return 0;
+}
+
+/*------------------------
+ * printfPQExpBuffer
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and insert it into str.  More space is allocated to str if necessary.
+ * This is a convenience routine that does the same thing as
+ * resetPQExpBuffer() followed by appendPQExpBuffer().
+ *
+ * CAUTION: the frontend version of this routine WILL FAIL if the result of
+ * the sprintf formatting operation exceeds 1KB of data (but the size of the
+ * pre-existing string in the buffer doesn't matter).  We could make it
+ * support larger strings, but that requires vsnprintf() which is not
+ * universally available.  Currently there is no need for long strings to be
+ * formatted in the frontend.  We could support it, if necessary, by
+ * conditionally including a vsnprintf emulation.
+ */
+void
+printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
+{
+   va_list     args;
+   char        buffer[1024];
+
+   va_start(args, fmt);
+   vsprintf(buffer, fmt, args);
+   va_end(args);
+
+   resetPQExpBuffer(str);
+   appendPQExpBufferStr(str, buffer);
+}
+
+/*------------------------
+ * appendPQExpBuffer
+ *
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and append it to whatever is already in str.  More space is allocated
+ * to str if necessary.  This is sort of like a combination of sprintf and
+ * strcat.
+ *
+ * CAUTION: the frontend version of this routine WILL FAIL if the result of
+ * the sprintf formatting operation exceeds 1KB of data (but the size of the
+ * pre-existing string in the buffer doesn't matter).  We could make it
+ * support larger strings, but that requires vsnprintf() which is not
+ * universally available.  Currently there is no need for long strings to be
+ * formatted in the frontend.  We could support it, if necessary, by
+ * conditionally including a vsnprintf emulation.
+ */
+void
+appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
+{
+   va_list     args;
+   char        buffer[1024];
+
+   va_start(args, fmt);
+   vsprintf(buffer, fmt, args);
+   va_end(args);
+
+   appendPQExpBufferStr(str, buffer);
+}
+
+/*------------------------
+ * appendPQExpBufferStr
+ * Append the given string to a PQExpBuffer, allocating more space
+ * if necessary.
+ */
+void
+appendPQExpBufferStr(PQExpBuffer str, const char *data)
+{
+   appendBinaryPQExpBuffer(str, data, strlen(data));
+}
+
+/*------------------------
+ * appendPQExpBufferChar
+ * Append a single byte to str.
+ * Like appendPQExpBuffer(str, "%c", ch) but much faster.
+ */
+void
+appendPQExpBufferChar(PQExpBuffer str, char ch)
+{
+   /* Make more room if needed */
+   if (! enlargePQExpBuffer(str, 1))
+       return;
+
+   /* OK, append the character */
+   str->data[str->len] = ch;
+   str->len++;
+   str->data[str->len] = '\0';
+}
+
+/*
+ * appendBinaryPQExpBuffer
+ *
+ * Append arbitrary binary data to a PQExpBuffer, allocating more space
+ * if necessary.
+ */
+void
+appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, int datalen)
+{
+   /* Make more room if needed */
+   if (! enlargePQExpBuffer(str, datalen))
+       return;
+
+   /* OK, append the data */
+   memcpy(str->data + str->len, data, datalen);
+   str->len += datalen;
+
+   /*
+    * Keep a trailing null in place, even though it's probably useless
+    * for binary data...
+    */
+   str->data[str->len] = '\0';
+}
diff --git a/src/interfaces/libpq/pqexpbuffer.h b/src/interfaces/libpq/pqexpbuffer.h
new file mode 100644 (file)
index 0000000..5c2d2f1
--- /dev/null
@@ -0,0 +1,169 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqexpbuffer.h
+ *   Declarations/definitions for "PQExpBuffer" functions.
+ *
+ * PQExpBuffer provides an indefinitely-extensible string data type.
+ * It can be used to buffer either ordinary C strings (null-terminated text)
+ * or arbitrary binary data.  All storage is allocated with malloc().
+ *
+ * This module is essentially the same as the backend's StringInfo data type,
+ * but it is intended for use in frontend libpq and client applications.
+ * Thus, it does not rely on palloc(), elog(), nor vsnprintf().
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pqexpbuffer.h,v 1.1 1999/08/31 01:37:37 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQEXPBUFFER_H
+#define PQEXPBUFFER_H
+
+/*-------------------------
+ * PQExpBufferData holds information about an extensible string.
+ *     data    is the current buffer for the string (allocated with malloc).
+ *     len     is the current string length.  There is guaranteed to be
+ *             a terminating '\0' at data[len], although this is not very
+ *             useful when the string holds binary data rather than text.
+ *     maxlen  is the allocated size in bytes of 'data', i.e. the maximum
+ *             string size (including the terminating '\0' char) that we can
+ *             currently store in 'data' without having to reallocate
+ *             more space.  We must always have maxlen > len.
+ *-------------------------
+ */
+typedef struct PQExpBufferData
+{
+   char       *data;
+   int         len;
+   int         maxlen;
+} PQExpBufferData;
+
+typedef PQExpBufferData *PQExpBuffer;
+
+/*------------------------
+ * Initial size of the data buffer in a PQExpBuffer.
+ * NB: this must be large enough to hold error messages that might
+ * be returned by PQrequestCancel() or any routine in fe-auth.c.
+ *------------------------
+ */
+#define INITIAL_EXPBUFFER_SIZE  256
+
+/*------------------------
+ * There are two ways to create a PQExpBuffer object initially:
+ *
+ * PQExpBuffer stringptr = createPQExpBuffer();
+ *     Both the PQExpBufferData and the data buffer are malloc'd.
+ *
+ * PQExpBufferData string;
+ * initPQExpBuffer(&string);
+ *     The data buffer is malloc'd but the PQExpBufferData is presupplied.
+ *     This is appropriate if the PQExpBufferData is a field of another
+ *     struct.
+ *-------------------------
+ */
+
+/*------------------------
+ * createPQExpBuffer
+ * Create an empty 'PQExpBufferData' & return a pointer to it.
+ */
+extern PQExpBuffer createPQExpBuffer(void);
+
+/*------------------------
+ * initPQExpBuffer
+ * Initialize a PQExpBufferData struct (with previously undefined contents)
+ * to describe an empty string.
+ */
+extern void initPQExpBuffer(PQExpBuffer str);
+
+/*------------------------
+ * To destroy a PQExpBuffer, use either:
+ *
+ * destroyPQExpBuffer(str);
+ *     free()s both the data buffer and the PQExpBufferData.
+ *     This is the inverse of createPQExpBuffer().
+ *
+ * termPQExpBuffer(str)
+ *     free()s the data buffer but not the PQExpBufferData itself.
+ *     This is the inverse of initPQExpBuffer().
+ *
+ * NOTE: some routines build up a string using PQExpBuffer, and then
+ * release the PQExpBufferData but return the data string itself to their
+ * caller. At that point the data string looks like a plain malloc'd
+ * string.
+ */
+extern void destroyPQExpBuffer(PQExpBuffer str);
+extern void termPQExpBuffer(PQExpBuffer str);
+
+/*------------------------
+ * resetPQExpBuffer
+ *     Reset a PQExpBuffer to empty
+ */
+extern void resetPQExpBuffer(PQExpBuffer str);
+
+/*------------------------
+ * enlargePQExpBuffer
+ * Make sure there is enough space for 'needed' more bytes in the buffer
+ * ('needed' does not include the terminating null).
+ *
+ * Returns 1 if OK, 0 if failed to enlarge buffer.
+ */
+extern int enlargePQExpBuffer(PQExpBuffer str, int needed);
+
+/*------------------------
+ * printfPQExpBuffer
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and insert it into str.  More space is allocated to str if necessary.
+ * This is a convenience routine that does the same thing as
+ * resetPQExpBuffer() followed by appendPQExpBuffer().
+ *
+ * CAUTION: the frontend version of this routine WILL FAIL if the result of
+ * the sprintf formatting operation exceeds 1KB of data (but the size of the
+ * pre-existing string in the buffer doesn't matter).  We could make it
+ * support larger strings, but that requires vsnprintf() which is not
+ * universally available.  Currently there is no need for long strings to be
+ * formatted in the frontend.  We could support it, if necessary, by
+ * conditionally including a vsnprintf emulation.
+ */
+extern void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...);
+
+/*------------------------
+ * appendPQExpBuffer
+ * Format text data under the control of fmt (an sprintf-like format string)
+ * and append it to whatever is already in str.  More space is allocated
+ * to str if necessary.  This is sort of like a combination of sprintf and
+ * strcat.
+ *
+ * CAUTION: the frontend version of this routine WILL FAIL if the result of
+ * the sprintf formatting operation exceeds 1KB of data (but the size of the
+ * pre-existing string in the buffer doesn't matter).  We could make it
+ * support larger strings, but that requires vsnprintf() which is not
+ * universally available.  Currently there is no need for long strings to be
+ * formatted in the frontend.  We could support it, if necessary, by
+ * conditionally including a vsnprintf emulation.
+ */
+extern void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...);
+
+/*------------------------
+ * appendPQExpBufferStr
+ * Append the given string to a PQExpBuffer, allocating more space
+ * if necessary.
+ */
+extern void appendPQExpBufferStr(PQExpBuffer str, const char *data);
+
+/*------------------------
+ * appendPQExpBufferChar
+ * Append a single byte to str.
+ * Like appendPQExpBuffer(str, "%c", ch) but much faster.
+ */
+extern void appendPQExpBufferChar(PQExpBuffer str, char ch);
+
+/*------------------------
+ * appendBinaryPQExpBuffer
+ * Append arbitrary binary data to a PQExpBuffer, allocating more space
+ * if necessary.
+ */
+extern void appendBinaryPQExpBuffer(PQExpBuffer str,
+                      const char *data, int datalen);
+
+#endif  /* PQEXPBUFFER_H */
index 1d351df7e8f100becdb18531e5ff0ddeb8271acb..eda99d815ba9522aa33e4f29064a8c9c11453919 100644 (file)
@@ -29,6 +29,7 @@ CLEAN :
    -@erase "$(INTDIR)\fe-lobj.obj"
    -@erase "$(INTDIR)\fe-misc.obj"
    -@erase "$(INTDIR)\fe-print.obj"
+   -@erase "$(INTDIR)\pqexpbuffer.obj"
    -@erase "$(OUTDIR)\libpqdll.obj"
    -@erase "$(OUTDIR)\libpq.lib"
    -@erase "$(OUTDIR)\libpq.dll"
@@ -70,7 +71,8 @@ LIB32_OBJS= \
    "$(INTDIR)\fe-exec.obj" \
    "$(INTDIR)\fe-lobj.obj" \
    "$(INTDIR)\fe-misc.obj" \
-   "$(INTDIR)\fe-print.obj"
+   "$(INTDIR)\fe-print.obj" \
+   "$(INTDIR)\pqexpbuffer.obj"
 
 !IFDEF MULTIBYTE
 LIB32_OBJS = $(LIB32_OBJS) "$(INTDIR)\common.obj" "$(INTDIR)\wchar.obj" "$(INTDIR)\conv.obj" "$(INTDIR)\big5.obj"