Add support for multiple error messages from libpq, by simply appending them

after each other (since we already add a newline on each, this makes them
multiline).

Previously a new error would just overwrite the old one, so for example any
error caused when trying to connect with SSL enabled would be overwritten
by the error message form the non-SSL connection when using sslmode=prefer.
This commit is contained in:
Magnus Hagander 2008-10-27 09:42:31 +00:00
parent 0fec77ae88
commit f3a0688ace
5 changed files with 54 additions and 36 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.265 2008/09/22 14:21:44 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.266 2008/10/27 09:42:31 mha Exp $ -->
<chapter id="libpq"> <chapter id="libpq">
<title><application>libpq</application> - C Library</title> <title><application>libpq</application> - C Library</title>
@ -1137,10 +1137,11 @@ PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
Nearly all <application>libpq</> functions will set a message for Nearly all <application>libpq</> functions will set a message for
<function>PQerrorMessage</function> if they fail. Note that by <function>PQerrorMessage</function> if they fail. Note that by
<application>libpq</application> convention, a nonempty <application>libpq</application> convention, a nonempty
<function>PQerrorMessage</function> result will include a trailing <function>PQerrorMessage</function> result can be multiple lines,
newline. The caller should not free the result directly. It will and will include a trailing newline. The caller should not free
be freed when the associated <structname>PGconn</> handle is passed the result directly. It will be freed when the associated
to <function>PQfinish</function>. The result string should not be <structname>PGconn</> handle is passed to
<function>PQfinish</function>. The result string should not be
expected to remain the same across operations on the expected to remain the same across operations on the
<literal>PGconn</> structure. <literal>PGconn</> structure.
</para> </para>

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.363 2008/10/23 16:17:19 mha Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.364 2008/10/27 09:42:31 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -699,7 +699,7 @@ connectNoDelay(PGconn *conn)
{ {
char sebuf[256]; char sebuf[256];
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not set socket to TCP no delay mode: %s\n"), libpq_gettext("could not set socket to TCP no delay mode: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0; return 0;
@ -729,7 +729,7 @@ connectFailureMessage(PGconn *conn, int errorno)
NULL, 0, NULL, 0,
service, sizeof(service), service, sizeof(service),
NI_NUMERICSERV); NI_NUMERICSERV);
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not connect to server: %s\n" libpq_gettext("could not connect to server: %s\n"
"\tIs the server running locally and accepting\n" "\tIs the server running locally and accepting\n"
"\tconnections on Unix domain socket \"%s\"?\n"), "\tconnections on Unix domain socket \"%s\"?\n"),
@ -739,7 +739,7 @@ connectFailureMessage(PGconn *conn, int errorno)
else else
#endif /* HAVE_UNIX_SOCKETS */ #endif /* HAVE_UNIX_SOCKETS */
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not connect to server: %s\n" libpq_gettext("could not connect to server: %s\n"
"\tIs the server running on host \"%s\" and accepting\n" "\tIs the server running on host \"%s\" and accepting\n"
"\tTCP/IP connections on port %s?\n"), "\tTCP/IP connections on port %s?\n"),
@ -829,11 +829,11 @@ connectDBStart(PGconn *conn)
if (ret || !addrs) if (ret || !addrs)
{ {
if (node) if (node)
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate host name \"%s\" to address: %s\n"), libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
node, gai_strerror(ret)); node, gai_strerror(ret));
else else
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"), libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
portstr, gai_strerror(ret)); portstr, gai_strerror(ret));
if (addrs) if (addrs)
@ -924,6 +924,8 @@ connectDBComplete(PGconn *conn)
switch (flag) switch (flag)
{ {
case PGRES_POLLING_OK: case PGRES_POLLING_OK:
/* Reset stored error messages since we now have a working connection */
resetPQExpBuffer(&conn->errorMessage);
return 1; /* success! */ return 1; /* success! */
case PGRES_POLLING_READING: case PGRES_POLLING_READING:
@ -1033,7 +1035,7 @@ PQconnectPoll(PGconn *conn)
break; break;
default: default:
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext( libpq_gettext(
"invalid connection state, " "invalid connection state, "
"probably indicative of memory corruption\n" "probably indicative of memory corruption\n"
@ -1077,7 +1079,7 @@ keep_going: /* We will come back to here until there is
conn->addr_cur = addr_cur->ai_next; conn->addr_cur = addr_cur->ai_next;
continue; continue;
} }
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create socket: %s\n"), libpq_gettext("could not create socket: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
break; break;
@ -1100,7 +1102,7 @@ keep_going: /* We will come back to here until there is
} }
if (!pg_set_noblock(conn->sock)) if (!pg_set_noblock(conn->sock))
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not set socket to non-blocking mode: %s\n"), libpq_gettext("could not set socket to non-blocking mode: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
closesocket(conn->sock); closesocket(conn->sock);
@ -1112,7 +1114,7 @@ keep_going: /* We will come back to here until there is
#ifdef F_SETFD #ifdef F_SETFD
if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not set socket to close-on-exec mode: %s\n"), libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
closesocket(conn->sock); closesocket(conn->sock);
@ -1199,7 +1201,7 @@ keep_going: /* We will come back to here until there is
if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
(char *) &optval, &optlen) == -1) (char *) &optval, &optlen) == -1)
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not get socket error status: %s\n"), libpq_gettext("could not get socket error status: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return; goto error_return;
@ -1237,7 +1239,7 @@ keep_going: /* We will come back to here until there is
(struct sockaddr *) & conn->laddr.addr, (struct sockaddr *) & conn->laddr.addr,
&conn->laddr.salen) < 0) &conn->laddr.salen) < 0)
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not get client address from socket: %s\n"), libpq_gettext("could not get client address from socket: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return; goto error_return;
@ -1281,7 +1283,7 @@ keep_going: /* We will come back to here until there is
pv = htonl(NEGOTIATE_SSL_CODE); pv = htonl(NEGOTIATE_SSL_CODE);
if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send SSL negotiation packet: %s\n"), libpq_gettext("could not send SSL negotiation packet: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return; goto error_return;
@ -1303,6 +1305,7 @@ keep_going: /* We will come back to here until there is
EnvironmentOptions); EnvironmentOptions);
if (!startpacket) if (!startpacket)
{ {
/* will not appendbuffer here, since it's likely to also run out of memory */
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n")); libpq_gettext("out of memory\n"));
goto error_return; goto error_return;
@ -1316,7 +1319,7 @@ keep_going: /* We will come back to here until there is
*/ */
if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK) if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send startup packet: %s\n"), libpq_gettext("could not send startup packet: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
free(startpacket); free(startpacket);
@ -1381,7 +1384,7 @@ keep_going: /* We will come back to here until there is
if (conn->sslmode[0] == 'r') /* "require" */ if (conn->sslmode[0] == 'r') /* "require" */
{ {
/* Require SSL, but server does not want it */ /* Require SSL, but server does not want it */
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n")); libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return; goto error_return;
} }
@ -1398,7 +1401,7 @@ keep_going: /* We will come back to here until there is
if (conn->sslmode[0] == 'r') /* "require" */ if (conn->sslmode[0] == 'r') /* "require" */
{ {
/* Require SSL, but server is too old */ /* Require SSL, but server is too old */
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n")); libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return; goto error_return;
} }
@ -1414,7 +1417,7 @@ keep_going: /* We will come back to here until there is
} }
else else
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("received invalid response to SSL negotiation: %c\n"), libpq_gettext("received invalid response to SSL negotiation: %c\n"),
SSLok); SSLok);
goto error_return; goto error_return;
@ -1489,7 +1492,7 @@ keep_going: /* We will come back to here until there is
*/ */
if (!(beresp == 'R' || beresp == 'E')) if (!(beresp == 'R' || beresp == 'E'))
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext( libpq_gettext(
"expected authentication request from " "expected authentication request from "
"server, but received %c\n"), "server, but received %c\n"),
@ -1522,7 +1525,7 @@ keep_going: /* We will come back to here until there is
*/ */
if (beresp == 'R' && (msgLength < 8 || msgLength > 2000)) if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
{ {
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext( libpq_gettext(
"expected authentication request from " "expected authentication request from "
"server, but received %c\n"), "server, but received %c\n"),
@ -1534,7 +1537,7 @@ keep_going: /* We will come back to here until there is
{ {
/* Handle error from a pre-3.0 server */ /* Handle error from a pre-3.0 server */
conn->inCursor = conn->inStart + 1; /* reread data */ conn->inCursor = conn->inStart + 1; /* reread data */
if (pqGets(&conn->errorMessage, conn)) if (pqGets_append(&conn->errorMessage, conn))
{ {
/* We'll come back when there is more data */ /* We'll come back when there is more data */
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
@ -1601,7 +1604,7 @@ keep_going: /* We will come back to here until there is
} }
else else
{ {
if (pqGets(&conn->errorMessage, conn)) if (pqGets_append(&conn->errorMessage, conn))
{ {
/* We'll come back when there is more data */ /* We'll come back when there is more data */
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
@ -1788,7 +1791,7 @@ keep_going: /* We will come back to here until there is
if (res) if (res)
{ {
if (res->resultStatus != PGRES_FATAL_ERROR) if (res->resultStatus != PGRES_FATAL_ERROR)
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("unexpected message from server during startup\n")); libpq_gettext("unexpected message from server during startup\n"));
/* /*
@ -1855,7 +1858,7 @@ keep_going: /* We will come back to here until there is
return PGRES_POLLING_OK; return PGRES_POLLING_OK;
default: default:
printfPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext( libpq_gettext(
"invalid connection state %c, " "invalid connection state %c, "
"probably indicative of memory corruption\n" "probably indicative of memory corruption\n"

View File

@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.135 2008/08/20 11:53:45 mha Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.136 2008/10/27 09:42:31 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -106,14 +106,14 @@ pqPutc(char c, PGconn *conn)
/* /*
* pqGets: * pqGets[_append]:
* get a null-terminated string from the connection, * get a null-terminated string from the connection,
* and store it in an expansible PQExpBuffer. * and store it in an expansible PQExpBuffer.
* If we run out of memory, all of the string is still read, * If we run out of memory, all of the string is still read,
* but the excess characters are silently discarded. * but the excess characters are silently discarded.
*/ */
int static int
pqGets(PQExpBuffer buf, PGconn *conn) pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
{ {
/* Copy conn data to locals for faster search loop */ /* Copy conn data to locals for faster search loop */
char *inBuffer = conn->inBuffer; char *inBuffer = conn->inBuffer;
@ -129,7 +129,9 @@ pqGets(PQExpBuffer buf, PGconn *conn)
slen = inCursor - conn->inCursor; slen = inCursor - conn->inCursor;
resetPQExpBuffer(buf); if (resetbuffer)
resetPQExpBuffer(buf);
appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen); appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);
conn->inCursor = ++inCursor; conn->inCursor = ++inCursor;
@ -141,6 +143,18 @@ pqGets(PQExpBuffer buf, PGconn *conn)
return 0; return 0;
} }
int
pqGets(PQExpBuffer buf, PGconn *conn)
{
return pqGets_internal(buf, conn, true);
}
int
pqGets_append(PQExpBuffer buf, PGconn *conn)
{
return pqGets_internal(buf, conn, false);
}
/* /*
* pqPuts: write a null-terminated string to the current message * pqPuts: write a null-terminated string to the current message

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.35 2008/05/29 22:02:44 tgl Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.36 2008/10/27 09:42:31 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -853,7 +853,6 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
goto fail; goto fail;
pqClearAsyncResult(conn); pqClearAsyncResult(conn);
conn->result = res; conn->result = res;
resetPQExpBuffer(&conn->errorMessage);
appendPQExpBufferStr(&conn->errorMessage, workBuf.data); appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
} }
else else

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.134 2008/09/22 14:21:44 tgl Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.135 2008/10/27 09:42:31 mha Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -519,6 +519,7 @@ extern int pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn);
extern int pqGetc(char *result, PGconn *conn); extern int pqGetc(char *result, PGconn *conn);
extern int pqPutc(char c, PGconn *conn); extern int pqPutc(char c, PGconn *conn);
extern int pqGets(PQExpBuffer buf, PGconn *conn); extern int pqGets(PQExpBuffer buf, PGconn *conn);
extern int pqGets_append(PQExpBuffer buf, PGconn *conn);
extern int pqPuts(const char *s, PGconn *conn); extern int pqPuts(const char *s, PGconn *conn);
extern int pqGetnchar(char *s, size_t len, PGconn *conn); extern int pqGetnchar(char *s, size_t len, PGconn *conn);
extern int pqPutnchar(const char *s, size_t len, PGconn *conn); extern int pqPutnchar(const char *s, size_t len, PGconn *conn);