OSDN Git Service

Update frontend libpq to remove limits on query lengths,
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 31 Aug 1999 01:37:37 +0000 (01:37 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 73f333c..ec955dc 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 f72f76b..574d78c 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 4edb4e8..4e86a8d 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,37 +748,13 @@ 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 01613e0..24fe986 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;
                }
        }
@@ -299,6 +302,25 @@ pqSetResultError(PGresult *res, const char *msg)
 }
 
 /*
+ * 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(&noticeBuf);
+       if (pqGets(&noticeBuf, conn))
+       {
+               termPQExpBuffer(&noticeBuf);
                return EOF;
-       DONOTICE(conn, conn->errorMessage);
+       }
+       DONOTICE(conn, noticeBuf.data);
+       termPQExpBuffer(&noticeBuf);
        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 a105d03..8793e24 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 63558ec..84149d1 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 9151463..02f3455 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,
                                                        "<td align=%s>%s</td></tr>\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 f3b788d..d22d403 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 1d351df..eda99d8 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"