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