# 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 $
#
#-------------------------------------------------------------------------
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
@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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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();
PQconninfoOption *
PQconndefaults(void)
{
- char errorMessage[ERROR_MSG_LENGTH];
+ PQExpBufferData errorBuf;
- conninfo_parse("", errorMessage);
+ initPQExpBuffer(&errorBuf);
+ conninfo_parse("", &errorBuf);
+ termPQExpBuffer(&errorBuf);
return PQconninfoOptions;
}
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)
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;
}
}
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;
/* 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;
}
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;
}
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,
&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
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;
}
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;
}
/* Handle errors. */
if (beresp == 'E')
{
- if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn))
+ if (pqGets(&conn->errorMessage, conn))
continue;
goto connect_errReturn;
}
/* 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;
}
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;
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
*/
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;
free(conn->inBuffer);
if (conn->outBuffer)
free(conn->outBuffer);
+ termPQExpBuffer(&conn->errorMessage);
+ termPQExpBuffer(&conn->workBuffer);
free(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
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;
}
*/
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;
}
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;
}
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
* ----------------
*/
static int
-conninfo_parse(const char *conninfo, char *errorMessage)
+conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
{
char *pname;
char *pval;
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;
}
/* 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;
}
{
if (*cp == '\0')
{
- sprintf(errorMessage,
+ printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
free(buf);
return -1;
}
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;
}
if (!strcmp(option->keyword, "user"))
{
option->val = fe_getauthname(errortmp);
+ /* note any error message is thrown away */
continue;
}
if (!conn)
return noConn;
- return conn->errorMessage;
+ return conn->errorMessage.data;
}
int
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
((*(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);
/* non-error cases */
break;
default:
- pqSetResultError(result, conn->errorMessage);
+ pqSetResultError(result, conn->errorMessage.data);
break;
}
}
}
/*
+ * 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
*/
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
{
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;
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
* 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);
}
/*
parseInput(PGconn *conn)
{
char id;
+ char noticeWorkspace[128];
/*
* Loop to parse successive complete messages available in the buffer.
{
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;
}
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 */
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,
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;
}
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;
}
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;
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 */
/* 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))
*/
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;
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 */
/* 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;
{
/* 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)
/* 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';
}
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;
}
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);
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;
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;
}
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;
}
/*
* 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)
static int
getNotice(PGconn *conn)
{
- if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
+ /* Since the Notice might be pretty long, we create a temporary
+ * PQExpBuffer rather than using conn->workBuffer. workBuffer is
+ * intended for stuff that is expected to be short.
+ */
+ PQExpBufferData noticeBuf;
+
+ initPQExpBuffer(¬iceBuf);
+ if (pqGets(¬iceBuf, conn))
+ {
+ termPQExpBuffer(¬iceBuf);
return EOF;
- DONOTICE(conn, conn->errorMessage);
+ }
+ DONOTICE(conn, noticeBuf.data);
+ termPQExpBuffer(¬iceBuf);
return 0;
}
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;
}
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;
}
/* Return to active duty */
conn->asyncStatus = PGASYNC_BUSY;
- conn->errorMessage[0] = '\0';
+ resetPQExpBuffer(&conn->errorMessage);
/* Wait for the completion response */
result = PQgetResult(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");
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 */
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 */
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);
}
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;
}
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;
}
{
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;
}
const char *
PQcmdTuples(PGresult *res)
{
+ char noticeBuf[128];
+
if (!res)
return "";
{
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 "";
}
{
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 "";
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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;
}
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;
}
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;
}
}
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;
}
#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;
}
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;
}
}
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));
{
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;
}
*/
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* --------------------------------------------------------------------- */
/* pqGets:
get a null-terminated string from the connection,
- and store it in a buffer of size maxlen bytes.
- If the incoming string is >= maxlen bytes, all of it is read,
+ and store it in an expansible PQExpBuffer.
+ If we run out of memory, all of the string is still read,
but the excess characters are silently discarded.
*/
int
-pqGets(char *s, int maxlen, PGconn *conn)
+pqGets(PQExpBuffer buf, PGconn *conn)
{
/* Copy conn data to locals for faster search loop */
char *inBuffer = conn->inBuffer;
return EOF;
slen = inCursor - conn->inCursor;
- if (slen < maxlen)
- strcpy(s, inBuffer + conn->inCursor);
- else
- {
- strncpy(s, inBuffer + conn->inCursor, maxlen - 1);
- s[maxlen - 1] = '\0';
- }
+
+ resetPQExpBuffer(buf);
+ appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);
conn->inCursor = ++inCursor;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> \"%s\"\n", s);
+ fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
+ buf->data);
return 0;
}
{
uint16 tmp2;
uint32 tmp4;
+ char noticeBuf[64];
switch (bytes)
{
*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;
}
{
uint16 tmp2;
uint32 tmp4;
+ char noticeBuf[64];
switch (bytes)
{
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;
}
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);
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqReadData() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadData() -- connection not open\n");
return -1;
}
* enlarge the buffer in case a single message exceeds the initial
* buffer size. We enlarge before filling the buffer entirely so as
* to avoid asking the kernel for a partial packet. The magic constant
- * here should be at least one TCP packet.
+ * here should be large enough for a TCP packet or Unix pipe
+ * bufferload. 8K is the usual pipe buffer size, so...
*/
- if (conn->inBufSize - conn->inEnd < 2000)
+ if (conn->inBufSize - conn->inEnd < 8192)
{
int newSize = conn->inBufSize * 2;
char *newBuf = (char *) realloc(conn->inBuffer, newSize);
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)
if (errno == ECONNRESET)
goto definitelyFailed;
#endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadData() -- read() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
return -1;
}
if (nread > 0)
* This means the connection has been closed. Cope.
*/
definitelyFailed:
- sprintf(conn->errorMessage,
+ 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");
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqFlush() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqFlush() -- connection not open\n");
return EOF;
}
#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");
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;
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqWait() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqWait() -- connection not open\n");
return EOF;
}
{
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
int usePipe = 0;
pqsigfunc oldsigpipehandler = NULL;
char *pagerenv;
- char buf[MAX_QUERY_SIZE + 1];
nTups = PQntuples(res);
if (!(fieldNames = (char **) calloc(nFields, sizeof(char *))))
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)
j;
int nFields;
int nTuples;
- int fLength[MAX_FIELDS];
+ int *fLength = NULL;
if (fieldSep == NULL)
fieldSep = DEFAULT_FIELD_SEP;
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;
}
}
}
(PQntuples(res) == 1) ? "" : "s");
fflush(fp);
+
+ if (fLength)
+ free(fLength);
}
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,
{
char *pval,
- *p,
- *o;
+ *p;
int plen;
bool skipit;
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
{
"<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);
*
* 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.
*
* 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
/*
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 */
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 */
};
/* ----------------
* 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);
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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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';
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
-@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"
"$(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"