Update frontend libpq to remove limits on query lengths,

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...
This commit is contained in:
Tom Lane 1999-08-31 01:37:37 +00:00
parent 130e372b5d
commit ab5cafa5d3
11 changed files with 938 additions and 391 deletions

View 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

View 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 $
*
*-------------------------------------------------------------------------
*/

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.101 1999/07/19 06:25:38 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.102 1999/08/31 01:37:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -44,7 +44,7 @@ static ConnStatusType connectDB(PGconn *conn);
static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static int conninfo_parse(const char *conninfo, char *errorMessage);
static int conninfo_parse(const char *conninfo, PQExpBuffer errorMessage);
static char *conninfo_getval(char *keyword);
static void conninfo_free(void);
static void defaultNoticeProcessor(void *arg, const char *message);
@ -178,7 +178,7 @@ PQconnectdb(const char *conninfo)
* Parse the conninfo string and save settings in conn structure
* ----------
*/
if (conninfo_parse(conninfo, conn->errorMessage) < 0)
if (conninfo_parse(conninfo, &conn->errorMessage) < 0)
{
conn->status = CONNECTION_BAD;
conninfo_free();
@ -226,9 +226,11 @@ PQconnectdb(const char *conninfo)
PQconninfoOption *
PQconndefaults(void)
{
char errorMessage[ERROR_MSG_LENGTH];
PQExpBufferData errorBuf;
conninfo_parse("", errorMessage);
initPQExpBuffer(&errorBuf);
conninfo_parse("", &errorBuf);
termPQExpBuffer(&errorBuf);
return PQconninfoOptions;
}
@ -328,13 +330,17 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
else if ((tmp = getenv("PGUSER")) != NULL)
conn->pguser = strdup(tmp);
else
conn->pguser = fe_getauthname(conn->errorMessage);
{
/* fe-auth.c has not been fixed to support PQExpBuffers, so: */
conn->pguser = fe_getauthname(conn->errorMessage.data);
conn->errorMessage.len = strlen(conn->errorMessage.data);
}
if (conn->pguser == NULL)
{
error = TRUE;
sprintf(conn->errorMessage,
"FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
printfPQExpBuffer(&conn->errorMessage,
"FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
}
if (pwd)
@ -469,8 +475,8 @@ update_db_info(PGconn *conn)
conn->pghost = NULL;
if (strcmp(old + offset, "localhost") != 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- non-tcp access only possible on localhost\n");
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- non-tcp access only possible on localhost\n");
return 1;
}
}
@ -533,9 +539,9 @@ connectDB(PGconn *conn)
hp = gethostbyname(conn->pghost);
if ((hp == NULL) || (hp->h_addrtype != AF_INET))
{
(void) sprintf(conn->errorMessage,
"connectDB() -- unknown hostname: %s\n",
conn->pghost);
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- unknown hostname: %s\n",
conn->pghost);
goto connect_errReturn;
}
family = AF_INET;
@ -567,21 +573,21 @@ connectDB(PGconn *conn)
/* Connect to the server */
if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- socket() failed: errno=%d\n%s\n",
errno, strerror(errno));
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- socket() failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- connect() failed: %s\n"
"Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n",
strerror(errno),
(family == AF_INET) ? " (with -i)" : "",
conn->pghost ? conn->pghost : "localhost",
(family == AF_INET) ? "TCP/IP port" : "Unix socket",
conn->pgport);
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- connect() failed: %s\n"
"Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n",
strerror(errno),
(family == AF_INET) ? " (with -i)" : "",
conn->pghost ? conn->pghost : "localhost",
(family == AF_INET) ? "TCP/IP port" : "Unix socket",
conn->pgport);
goto connect_errReturn;
}
@ -596,9 +602,9 @@ connectDB(PGconn *conn)
if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)
#endif
{
(void) sprintf(conn->errorMessage,
"connectDB() -- fcntl() failed: errno=%d\n%s\n",
errno, strerror(errno));
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- fcntl() failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
@ -609,8 +615,8 @@ connectDB(PGconn *conn)
pe = getprotobyname("TCP");
if (pe == NULL)
{
(void) sprintf(conn->errorMessage,
"connectDB(): getprotobyname failed\n");
printfPQExpBuffer(&conn->errorMessage,
"connectDB(): getprotobyname failed\n");
goto connect_errReturn;
}
if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY,
@ -620,9 +626,9 @@ connectDB(PGconn *conn)
&on,
sizeof(on)) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- setsockopt failed: errno=%d\n%s\n",
errno, strerror(errno));
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- setsockopt failed: errno=%d\n%s\n",
errno, strerror(errno));
#ifdef WIN32
printf("Winsock error: %i\n", WSAGetLastError());
#endif
@ -634,9 +640,9 @@ connectDB(PGconn *conn)
laddrlen = sizeof(conn->laddr);
if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- getsockname() failed: errno=%d\n%s\n",
errno, strerror(errno));
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- getsockname() failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
@ -648,9 +654,9 @@ connectDB(PGconn *conn)
if (pqPacketSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK)
{
sprintf(conn->errorMessage,
"connectDB() -- couldn't send startup packet: errno=%d\n%s\n",
errno, strerror(errno));
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- couldn't send startup packet: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
@ -681,7 +687,7 @@ connectDB(PGconn *conn)
/* Handle errors. */
if (beresp == 'E')
{
if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn))
if (pqGets(&conn->errorMessage, conn))
continue;
goto connect_errReturn;
}
@ -689,8 +695,8 @@ connectDB(PGconn *conn)
/* Otherwise it should be an authentication request. */
if (beresp != 'R')
{
(void) sprintf(conn->errorMessage,
"connectDB() -- expected authentication request\n");
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- expected authentication request\n");
goto connect_errReturn;
}
@ -709,9 +715,14 @@ connectDB(PGconn *conn)
conn->inStart = conn->inCursor;
/* Respond to the request if necessary. */
/* fe-auth.c has not been fixed to support PQExpBuffers, so: */
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
conn->errorMessage) != STATUS_OK)
conn->errorMessage.data) != STATUS_OK)
{
conn->errorMessage.len = strlen(conn->errorMessage.data);
goto connect_errReturn;
}
if (pqFlush(conn))
goto connect_errReturn;
@ -737,36 +748,12 @@ connectDB(PGconn *conn)
if (res)
{
if (res->resultStatus != PGRES_FATAL_ERROR)
sprintf(conn->errorMessage,
"connectDB() -- unexpected message during startup\n");
printfPQExpBuffer(&conn->errorMessage,
"connectDB() -- unexpected message during startup\n");
PQclear(res);
goto connect_errReturn;
}
/*
* Given the new protocol that sends a ReadyForQuery message after
* successful backend startup, it should no longer be necessary to
* send an empty query to test for startup.
*/
#ifdef NOT_USED
/*
* Send a blank query to make sure everything works; in particular,
* that the database exists.
*/
res = PQexec(conn, " ");
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
{
/* PQexec has put error message in conn->errorMessage */
closePGconn(conn);
PQclear(res);
goto connect_errReturn;
}
PQclear(res);
#endif
/*
* Post-connection housekeeping. Send environment variables to server
*/
@ -870,11 +857,27 @@ makeEmptyPGconn(void)
conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList();
conn->sock = -1;
conn->inBufSize = PQ_BUFFER_SIZE;
/*
* The output buffer size is set to 8K, which is the usual size of pipe
* buffers on Unix systems. That way, when we are sending a large
* amount of data, we avoid incurring extra kernel context swaps for
* partial bufferloads. Note that we currently don't ever enlarge
* the output buffer.
*
* With the same goal of minimizing context swaps, the input buffer will
* be enlarged anytime it has less than 8K free, so we initially allocate
* twice that.
*/
conn->inBufSize = 16 * 1024;
conn->inBuffer = (char *) malloc(conn->inBufSize);
conn->outBufSize = PQ_BUFFER_SIZE;
conn->outBufSize = 8 * 1024;
conn->outBuffer = (char *) malloc(conn->outBufSize);
if (conn->inBuffer == NULL || conn->outBuffer == NULL)
initPQExpBuffer(&conn->errorMessage);
initPQExpBuffer(&conn->workBuffer);
if (conn->inBuffer == NULL ||
conn->outBuffer == NULL ||
conn->errorMessage.data == NULL ||
conn->workBuffer.data == NULL)
{
freePGconn(conn);
conn = NULL;
@ -922,6 +925,8 @@ freePGconn(PGconn *conn)
free(conn->inBuffer);
if (conn->outBuffer)
free(conn->outBuffer);
termPQExpBuffer(&conn->errorMessage);
termPQExpBuffer(&conn->workBuffer);
free(conn);
}
@ -1002,16 +1007,24 @@ PQreset(PGconn *conn)
* PQrequestCancel: attempt to request cancellation of the current operation.
*
* The return value is TRUE if the cancel request was successfully
* dispatched, FALSE if not (in which case errorMessage is set).
* dispatched, FALSE if not (in which case conn->errorMessage is set).
* Note: successful dispatch is no guarantee that there will be any effect at
* the backend. The application must read the operation result as usual.
*
* XXX it was a bad idea to have the error message returned in
* conn->errorMessage, since it could overwrite a message already there.
* Would be better to return it in a char array passed by the caller.
*
* CAUTION: we want this routine to be safely callable from a signal handler
* (for example, an application might want to call it in a SIGINT handler).
* This means we cannot use any C library routine that might be non-reentrant.
* malloc/free are often non-reentrant, and anything that might call them is
* just as dangerous. We avoid sprintf here for that reason. Building up
* error messages with strcpy/strcat is tedious but should be quite safe.
*
* NOTE: this routine must not generate any error message longer than
* INITIAL_EXPBUFFER_SIZE (currently 256), since we dare not try to
* expand conn->errorMessage!
*/
int
@ -1030,8 +1043,9 @@ PQrequestCancel(PGconn *conn)
if (conn->sock < 0)
{
strcpy(conn->errorMessage,
strcpy(conn->errorMessage.data,
"PQrequestCancel() -- connection is not open\n");
conn->errorMessage.len = strlen(conn->errorMessage.data);
return FALSE;
}
@ -1041,12 +1055,14 @@ PQrequestCancel(PGconn *conn)
*/
if ((tmpsock = socket(conn->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
{
strcpy(conn->errorMessage, "PQrequestCancel() -- socket() failed: ");
strcpy(conn->errorMessage.data,
"PQrequestCancel() -- socket() failed: ");
goto cancel_errReturn;
}
if (connect(tmpsock, &conn->raddr.sa, conn->raddr_len) < 0)
{
strcpy(conn->errorMessage, "PQrequestCancel() -- connect() failed: ");
strcpy(conn->errorMessage.data,
"PQrequestCancel() -- connect() failed: ");
goto cancel_errReturn;
}
@ -1063,7 +1079,8 @@ PQrequestCancel(PGconn *conn)
if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
{
strcpy(conn->errorMessage, "PQrequestCancel() -- send() failed: ");
strcpy(conn->errorMessage.data,
"PQrequestCancel() -- send() failed: ");
goto cancel_errReturn;
}
@ -1077,8 +1094,9 @@ PQrequestCancel(PGconn *conn)
return TRUE;
cancel_errReturn:
strcat(conn->errorMessage, strerror(errno));
strcat(conn->errorMessage, "\n");
strcat(conn->errorMessage.data, strerror(errno));
strcat(conn->errorMessage.data, "\n");
conn->errorMessage.len = strlen(conn->errorMessage.data);
if (tmpsock >= 0)
{
#ifdef WIN32
@ -1123,7 +1141,7 @@ pqPacketSend(PGconn *conn, const char *buf, size_t len)
* ----------------
*/
static int
conninfo_parse(const char *conninfo, char *errorMessage)
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
{
char *pname;
char *pval;
@ -1132,13 +1150,13 @@ conninfo_parse(const char *conninfo, char *errorMessage)
char *cp;
char *cp2;
PQconninfoOption *option;
char errortmp[ERROR_MSG_LENGTH];
char errortmp[INITIAL_EXPBUFFER_SIZE];
conninfo_free();
if ((buf = strdup(conninfo)) == NULL)
{
strcpy(errorMessage,
printfPQExpBuffer(errorMessage,
"FATAL: cannot allocate memory for copy of conninfo string\n");
return -1;
}
@ -1176,9 +1194,9 @@ conninfo_parse(const char *conninfo, char *errorMessage)
/* Check that there is a following '=' */
if (*cp != '=')
{
sprintf(errorMessage,
"ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n",
pname);
printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n",
pname);
free(buf);
return -1;
}
@ -1223,7 +1241,7 @@ conninfo_parse(const char *conninfo, char *errorMessage)
{
if (*cp == '\0')
{
sprintf(errorMessage,
printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
free(buf);
return -1;
@ -1257,9 +1275,9 @@ conninfo_parse(const char *conninfo, char *errorMessage)
}
if (option->keyword == NULL)
{
sprintf(errorMessage,
"ERROR: PQconnectdb() - unknown option '%s'\n",
pname);
printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - unknown option '%s'\n",
pname);
free(buf);
return -1;
}
@ -1314,6 +1332,7 @@ conninfo_parse(const char *conninfo, char *errorMessage)
if (!strcmp(option->keyword, "user"))
{
option->val = fe_getauthname(errortmp);
/* note any error message is thrown away */
continue;
}
@ -1436,7 +1455,7 @@ PQerrorMessage(PGconn *conn)
if (!conn)
return noConn;
return conn->errorMessage;
return conn->errorMessage.data;
}
int

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.84 1999/07/19 06:25:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.85 1999/08/31 01:37:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,6 +42,9 @@ const char *const pgresStatus[] = {
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
static void pqCatenateResultError(PGresult *res, const char *msg);
static void saveErrorResult(PGconn *conn);
static PGresult *prepareAsyncResult(PGconn *conn);
static int addTuple(PGresult *res, PGresAttValue *tup);
static void parseInput(PGconn *conn);
static void handleSendFailure(PGconn *conn);
@ -158,7 +161,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
/* non-error cases */
break;
default:
pqSetResultError(result, conn->errorMessage);
pqSetResultError(result, conn->errorMessage.data);
break;
}
}
@ -298,6 +301,25 @@ pqSetResultError(PGresult *res, const char *msg)
res->errMsg = NULL;
}
/*
* pqCatenateResultError -
* concatenate a new error message to the one already in a PGresult
*/
static void
pqCatenateResultError(PGresult *res, const char *msg)
{
PQExpBufferData errorBuf;
if (!res || !msg)
return;
initPQExpBuffer(&errorBuf);
if (res->errMsg)
appendPQExpBufferStr(&errorBuf, res->errMsg);
appendPQExpBufferStr(&errorBuf, msg);
pqSetResultError(res, errorBuf.data);
termPQExpBuffer(&errorBuf);
}
/*
* PQclear -
* free's the memory associated with a PGresult
@ -338,6 +360,72 @@ pqClearAsyncResult(PGconn *conn)
conn->curTuple = NULL;
}
/*
* This subroutine deletes any existing async result, sets conn->result
* to a PGresult with status PGRES_FATAL_ERROR, and stores the current
* contents of conn->errorMessage into that result. It differs from a
* plain call on PQmakeEmptyPGresult() in that if there is already an
* async result with status PGRES_FATAL_ERROR, the current error message
* is APPENDED to the old error message instead of replacing it. This
* behavior lets us report multiple error conditions properly, if necessary.
* (An example where this is needed is when the backend sends an 'E' message
* and immediately closes the connection --- we want to report both the
* backend error and the connection closure error.)
*/
static void
saveErrorResult(PGconn *conn)
{
/* If no old async result, just let PQmakeEmptyPGresult make one.
* Likewise if old result is not an error message.
*/
if (conn->result == NULL ||
conn->result->resultStatus != PGRES_FATAL_ERROR ||
conn->result->errMsg == NULL)
{
pqClearAsyncResult(conn);
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
}
else
{
/* Else, concatenate error message to existing async result. */
pqCatenateResultError(conn->result, conn->errorMessage.data);
}
}
/*
* This subroutine prepares an async result object for return to the caller.
* If there is not already an async result object, build an error object
* using whatever is in conn->errorMessage. In any case, clear the async
* result storage and make sure PQerrorMessage will agree with the result's
* error string.
*/
static PGresult *
prepareAsyncResult(PGconn *conn)
{
PGresult *res;
/*
* conn->result is the PGresult to return. If it is NULL
* (which probably shouldn't happen) we assume there is an
* appropriate error message in conn->errorMessage.
*/
res = conn->result;
conn->result = NULL; /* handing over ownership to caller */
conn->curTuple = NULL; /* just in case */
if (!res)
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
else
{
/*
* Make sure PQerrorMessage agrees with result; it could
* be different if we have concatenated messages.
*/
resetPQExpBuffer(&conn->errorMessage);
appendPQExpBufferStr(&conn->errorMessage,
PQresultErrorMessage(res));
}
return res;
}
/*
* addTuple
@ -394,37 +482,33 @@ PQsendQuery(PGconn *conn, const char *query)
{
if (!conn)
return 0;
/* clear the error string */
resetPQExpBuffer(&conn->errorMessage);
if (!query)
{
sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
return 0;
}
/* check to see if the query string is too long */
if (strlen(query) > MAX_MESSAGE_LEN - 2)
{
sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
printfPQExpBuffer(&conn->errorMessage,
"PQsendQuery() -- query pointer is null.\n");
return 0;
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK)
{
sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "
"to the backend.\n");
printfPQExpBuffer(&conn->errorMessage,
"PQsendQuery() -- There is no connection "
"to the backend.\n");
return 0;
}
/* Can't send while already busy, either. */
if (conn->asyncStatus != PGASYNC_IDLE)
{
sprintf(conn->errorMessage,
"PQsendQuery() -- another query already in progress.");
printfPQExpBuffer(&conn->errorMessage,
"PQsendQuery() -- another query already in progress.\n");
return 0;
}
/* clear the error string */
conn->errorMessage[0] = '\0';
/* initialize async result-accumulation state */
conn->result = NULL;
conn->curTuple = NULL;
@ -456,9 +540,6 @@ PQsendQuery(PGconn *conn, const char *query)
static void
handleSendFailure(PGconn *conn)
{
/* Preserve the error message emitted by the failing output routine */
char * svErrMsg = strdup(conn->errorMessage);
/*
* Accept any available input data, ignoring errors. Note that if
* pqReadData decides the backend has closed the channel, it will
@ -472,11 +553,6 @@ handleSendFailure(PGconn *conn)
* state, only NOTICE and NOTIFY messages will be eaten.
*/
parseInput(conn);
/* Restore error message generated by output routine, if any. */
if (*svErrMsg != '\0')
strcpy(conn->errorMessage, svErrMsg);
free(svErrMsg);
}
/*
@ -514,6 +590,7 @@ static void
parseInput(PGconn *conn)
{
char id;
char noticeWorkspace[128];
/*
* Loop to parse successive complete messages available in the buffer.
@ -565,10 +642,10 @@ parseInput(PGconn *conn)
{
if (conn->asyncStatus == PGASYNC_IDLE)
{
sprintf(conn->errorMessage,
"Backend message type 0x%02x arrived while idle\n",
sprintf(noticeWorkspace,
"Backend message type 0x%02x arrived while idle\n",
id);
DONOTICE(conn, conn->errorMessage);
DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
}
@ -577,21 +654,20 @@ parseInput(PGconn *conn)
switch (id)
{
case 'C': /* command complete */
if (pqGets(&conn->workBuffer, conn))
return;
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK);
if (pqGets(conn->result->cmdStatus, CMDSTATUS_LEN, conn))
return;
PGRES_COMMAND_OK);
strncpy(conn->result->cmdStatus, conn->workBuffer.data,
CMDSTATUS_LEN);
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
if (pqGets(& conn->errorMessage, conn))
return;
/* delete any partially constructed result */
pqClearAsyncResult(conn);
/* and build an error result holding the error message */
conn->result = PQmakeEmptyPGresult(conn,
PGRES_FATAL_ERROR);
/* build an error result holding the error message */
saveErrorResult(conn);
conn->asyncStatus = PGASYNC_READY;
break;
case 'Z': /* backend is ready for new query */
@ -603,9 +679,10 @@ parseInput(PGconn *conn)
return;
if (id != '\0')
{
sprintf(conn->errorMessage,
"unexpected character %c following 'I'\n", id);
DONOTICE(conn, conn->errorMessage);
sprintf(noticeWorkspace,
"unexpected character %c following 'I'\n",
id);
DONOTICE(conn, noticeWorkspace);
}
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
@ -625,7 +702,7 @@ parseInput(PGconn *conn)
return;
break;
case 'P': /* synchronous (normal) portal */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
if (pqGets(&conn->workBuffer, conn))
return;
/* We pretty much ignore this message type... */
break;
@ -660,9 +737,9 @@ parseInput(PGconn *conn)
}
else
{
sprintf(conn->errorMessage,
"Backend sent D message without prior T\n");
DONOTICE(conn, conn->errorMessage);
sprintf(noticeWorkspace,
"Backend sent D message without prior T\n");
DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
@ -677,9 +754,9 @@ parseInput(PGconn *conn)
}
else
{
sprintf(conn->errorMessage,
"Backend sent B message without prior T\n");
DONOTICE(conn, conn->errorMessage);
sprintf(noticeWorkspace,
"Backend sent B message without prior T\n");
DONOTICE(conn, noticeWorkspace);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
@ -692,18 +769,15 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_COPY_OUT;
break;
default:
sprintf(conn->errorMessage,
"unknown protocol character '%c' read from backend. "
"(The protocol character is the first character the "
"backend sends in response to a query it receives).\n",
id);
printfPQExpBuffer(&conn->errorMessage,
"Unknown protocol character '%c' read from backend. "
"(The protocol character is the first character the "
"backend sends in response to a query it receives).\n",
id);
/* build an error result holding the error message */
saveErrorResult(conn);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
/* delete any partially constructed result */
pqClearAsyncResult(conn);
/* and build an error result holding the error message */
conn->result = PQmakeEmptyPGresult(conn,
PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
return;
} /* switch on protocol character */
@ -753,12 +827,11 @@ getRowDescriptions(PGconn *conn)
/* get type info */
for (i = 0; i < nfields; i++)
{
char typName[MAX_MESSAGE_LEN];
int typid;
int typlen;
int atttypmod;
if (pqGets(typName, MAX_MESSAGE_LEN, conn) ||
if (pqGets(&conn->workBuffer, conn) ||
pqGetInt(&typid, 4, conn) ||
pqGetInt(&typlen, 2, conn) ||
pqGetInt(&atttypmod, 4, conn))
@ -777,7 +850,8 @@ getRowDescriptions(PGconn *conn)
*/
if (typlen == 0xFFFF)
typlen = -1;
result->attDescs[i].name = pqResultStrdup(result, typName);
result->attDescs[i].name = pqResultStrdup(result,
conn->workBuffer.data);
result->attDescs[i].typid = typid;
result->attDescs[i].typlen = typlen;
result->attDescs[i].atttypmod = atttypmod;
@ -804,8 +878,9 @@ getAnotherTuple(PGconn *conn, int binary)
PGresult *result = conn->result;
int nfields = result->numAttributes;
PGresAttValue *tup;
char bitmap[MAX_FIELDS]; /* the backend sends us a bitmap
* of which attributes are null */
/* the backend sends us a bitmap of which attributes are null */
char std_bitmap[64]; /* used unless it doesn't fit */
char *bitmap = std_bitmap;
int i;
int nbytes; /* the number of bytes in bitmap */
char bmap; /* One byte of the bitmap */
@ -828,21 +903,12 @@ getAnotherTuple(PGconn *conn, int binary)
/* Get the null-value bitmap */
nbytes = (nfields + BYTELEN - 1) / BYTELEN;
if (nbytes >= MAX_FIELDS)
{
/* Replace partially constructed result with an error result */
pqClearAsyncResult(conn);
sprintf(conn->errorMessage,
"getAnotherTuple() -- null-values bitmap is too large\n");
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
/* Discard the broken message */
conn->inStart = conn->inEnd;
return EOF;
}
/* malloc() only for unusually large field counts... */
if (nbytes > sizeof(std_bitmap))
bitmap = (char *) malloc(nbytes);
if (pqGetnchar(bitmap, nbytes, conn))
return EOF;
goto EOFexit;
/* Scan the fields */
bitmap_index = 0;
@ -861,7 +927,7 @@ getAnotherTuple(PGconn *conn, int binary)
{
/* get the value length (the first four bytes are for length) */
if (pqGetInt(&vlen, 4, conn))
return EOF;
goto EOFexit;
if (binary == 0)
vlen = vlen - 4;
if (vlen < 0)
@ -876,7 +942,7 @@ getAnotherTuple(PGconn *conn, int binary)
/* read in the value */
if (vlen > 0)
if (pqGetnchar((char *) (tup[i].value), vlen, conn))
return EOF;
goto EOFexit;
/* we have to terminate this ourselves */
tup[i].value[vlen] = '\0';
}
@ -897,17 +963,27 @@ getAnotherTuple(PGconn *conn, int binary)
goto outOfMemory;
/* and reset for a new message */
conn->curTuple = NULL;
if (bitmap != std_bitmap)
free(bitmap);
return 0;
outOfMemory:
/* Replace partially constructed result with an error result */
/* we do NOT use saveErrorResult() here, because of the likelihood
* that there's not enough memory to concatenate messages...
*/
pqClearAsyncResult(conn);
sprintf(conn->errorMessage,
"getAnotherTuple() -- out of memory for result\n");
printfPQExpBuffer(&conn->errorMessage,
"getAnotherTuple() -- out of memory for result\n");
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
/* Discard the failed message --- good idea? */
conn->inStart = conn->inEnd;
EOFexit:
if (bitmap != std_bitmap)
free(bitmap);
return EOF;
}
@ -955,10 +1031,12 @@ PQgetResult(PGconn *conn)
if (pqWait(TRUE, FALSE, conn) ||
pqReadData(conn) < 0)
{
pqClearAsyncResult(conn);
/* conn->errorMessage has been set by pqWait or pqReadData.
* We want to append it to any already-received error message.
*/
saveErrorResult(conn);
conn->asyncStatus = PGASYNC_IDLE;
/* conn->errorMessage has been set by pqWait or pqReadData. */
return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
return prepareAsyncResult(conn);
}
/* Parse it. */
parseInput(conn);
@ -971,28 +1049,7 @@ PQgetResult(PGconn *conn)
res = NULL; /* query is complete */
break;
case PGASYNC_READY:
/*
* conn->result is the PGresult to return. If it is NULL
* (which probably shouldn't happen) we assume there is an
* appropriate error message in conn->errorMessage.
*/
res = conn->result;
conn->result = NULL;/* handing over ownership to caller */
conn->curTuple = NULL; /* just in case */
if (!res)
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
else
{
/*
* Make sure PQerrorMessage agrees with result; it could
* be that we have done other operations that changed
* errorMessage since the result's error message was
* saved.
*/
strcpy(conn->errorMessage, PQresultErrorMessage(res));
}
res = prepareAsyncResult(conn);
/* Set the state back to BUSY, allowing parsing to proceed. */
conn->asyncStatus = PGASYNC_BUSY;
break;
@ -1003,9 +1060,9 @@ PQgetResult(PGconn *conn)
res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
break;
default:
sprintf(conn->errorMessage,
"PQgetResult: Unexpected asyncStatus %d\n",
(int) conn->asyncStatus);
printfPQExpBuffer(&conn->errorMessage,
"PQgetResult: Unexpected asyncStatus %d\n",
(int) conn->asyncStatus);
res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
break;
}
@ -1043,7 +1100,7 @@ PQexec(PGconn *conn, const char *query)
result->resultStatus == PGRES_COPY_OUT)
{
PQclear(result);
sprintf(conn->errorMessage,
printfPQExpBuffer(&conn->errorMessage,
"PQexec: you gotta get out of a COPY state yourself.\n");
return NULL;
}
@ -1056,14 +1113,30 @@ PQexec(PGconn *conn, const char *query)
/*
* For backwards compatibility, return the last result if there are
* more than one. We have to stop if we see copy in/out, however. We
* will resume parsing when application calls PQendcopy.
* more than one --- but merge error messages if we get more than one
* error result.
*
* We have to stop if we see copy in/out, however.
* We will resume parsing when application calls PQendcopy.
*/
lastResult = NULL;
while ((result = PQgetResult(conn)) != NULL)
{
if (lastResult)
PQclear(lastResult);
{
if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
result->resultStatus == PGRES_FATAL_ERROR)
{
pqCatenateResultError(lastResult, result->errMsg);
PQclear(result);
result = lastResult;
/* Make sure PQerrorMessage agrees with catenated result */
resetPQExpBuffer(&conn->errorMessage);
appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
}
else
PQclear(lastResult);
}
lastResult = result;
if (result->resultStatus == PGRES_COPY_IN ||
result->resultStatus == PGRES_COPY_OUT)
@ -1083,9 +1156,20 @@ PQexec(PGconn *conn, const char *query)
static int
getNotice(PGconn *conn)
{
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
/* Since the Notice might be pretty long, we create a temporary
* PQExpBuffer rather than using conn->workBuffer. workBuffer is
* intended for stuff that is expected to be short.
*/
PQExpBufferData noticeBuf;
initPQExpBuffer(&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 "";
}

View 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;
}

View 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 */

View 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);

View 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 $
*
*-------------------------------------------------------------------------
*/
@ -21,12 +21,12 @@
/* 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

View File

@ -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';
}

View File

@ -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 */

View 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"