diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b9207128c35..290236e8b65 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -323,23 +323,12 @@ PostgresPollingStatusType *PQconnectPoll(PQconn *conn)
- CONNECTION_AWAITING_RESPONSE: Waiting for a response from the backend.
+ CONNECTION_AWAITING_RESPONSE: Waiting for a response from the postmaster.
- CONNECTION_AUTH_RESPONSE: Got an authentication response; about to deal
- with it.
-
-
-
-
- CONNECTION_ERROR_RESPONSE: Got an error response; about to deal with it.
-
-
-
-
- CONNECTION_AUTH_OK: Received authentication; waiting for ReadyForQuery etc.
+ CONNECTION_AUTH_OK: Received authentication; waiting for backend startup.
@@ -373,7 +362,7 @@ PostgresPollingStatusType *PQconnectPoll(PQconn *conn)
Note that if PQconnectStart returns a non-NULL pointer, you must call
- PQfinish upon that, when you are finished with it, in order to dispose of
+ PQfinish when you are finished with it, in order to dispose of
the structure and any associated memory blocks. This must be done even if a
call to PQconnectStart or PQconnectPoll failed.
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a5380822ace..1d2426e7982 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.108 1999/12/02 00:26:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.109 2000/01/14 05:33:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -218,15 +218,15 @@ static struct EnvironmentOptions
* You should call PQfinish (if conn is not NULL) regardless of whether this
* call succeeded.
*
- * ---------------- */
-
+ * ----------------
+ */
PGconn *
PQconnectdb(const char *conninfo)
{
PGconn *conn = PQconnectStart(conninfo);
-
- (void)(!conn || (conn->status == CONNECTION_BAD) ||
- !connectDBComplete(conn));
+
+ if (conn && conn->status != CONNECTION_BAD)
+ (void) connectDBComplete(conn);
return conn;
}
@@ -250,8 +250,8 @@ PQconnectdb(const char *conninfo)
*
* See PQconnectPoll for more info.
*
- * ---------------- */
-
+ * ----------------
+ */
PGconn *
PQconnectStart(const char *conninfo)
{
@@ -306,7 +306,7 @@ PQconnectStart(const char *conninfo)
*/
if (!connectDBStart(conn))
{
- /* Just in case we failed to set it in connectDBStart */
+ /* Just in case we failed to set it in connectDBStart */
conn->status = CONNECTION_BAD;
}
@@ -374,15 +374,13 @@ PQconndefaults(void)
* ----------------
*/
PGconn *
-PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, const char *login, const char *pwd)
+PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
+ const char *pgtty, const char *dbName, const char *login,
+ const char *pwd)
{
PGconn *conn;
- char *tmp;
-
- /* An error message from some service we call. */
- bool error = FALSE;
-
- /* We encountered an error that prevents successful completion */
+ char *tmp; /* An error message from some service we call. */
+ bool error = FALSE; /* We encountered an error. */
int i;
conn = makeEmptyPGconn();
@@ -484,7 +482,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
}
else
{
- (void)(!connectDBStart(conn) || !connectDBComplete(conn));
+ if (connectDBStart(conn))
+ (void) connectDBComplete(conn);
}
return conn;
@@ -676,8 +675,6 @@ connectDBStart(PGconn *conn)
#ifdef USE_SSL
StartupPacket np; /* Used to negotiate SSL connection */
char SSLok;
- static int allow_ssl_try = 1; /* Allowed to do SSL negotiation */
- int tried_ssl = 0; /* Set if SSL negotiation was tried */
#endif
if (!conn)
@@ -797,12 +794,11 @@ connectDBStart(PGconn *conn)
#endif
#ifdef USE_SSL
- /* This needs to be done before we set into nonblocking, since SSL negotiation
- * does not like that mode */
+ /* This needs to be done before we set into nonblocking, since SSL
+ * negotiation does not like that mode */
/* Attempt to negotiate SSL usage */
- if (allow_ssl_try) {
- tried_ssl = 1;
+ if (conn->allow_ssl_try) {
memset((char *)&np, 0, sizeof(np));
np.protoVersion = htonl(NEGOTIATE_SSL_CODE);
if (pqPacketSend(conn, (char *) &np, sizeof(StartupPacket)) != STATUS_OK)
@@ -812,9 +808,9 @@ connectDBStart(PGconn *conn)
errno, strerror(errno));
goto connect_errReturn;
}
- /* Now receive the backends response */
+ /* Now receive the postmasters response */
if (recv(conn->sock, &SSLok, 1, 0) != 1) {
- sprintf(conn->errorMessage, "PQconnectDB() -- couldn't read backend response: errno=%d\n%s\n",
+ sprintf(conn->errorMessage, "PQconnectDB() -- couldn't read postmaster response: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
@@ -845,9 +841,9 @@ connectDBStart(PGconn *conn)
else if (SSLok == 'E') {
/* Received error - probably protocol mismatch */
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "Backend reports error, attempting fallback to pre-6.6.\n");
+ fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-6.6.\n");
close(conn->sock);
- allow_ssl_try = 0;
+ conn->allow_ssl_try = FALSE;
return connectDBStart(conn);
}
else if (SSLok != 'N') {
@@ -856,8 +852,6 @@ connectDBStart(PGconn *conn)
goto connect_errReturn;
}
}
- else
- allow_ssl_try = 1; /* We'll allow an attempt to use SSL next time */
#endif
/* ----------
@@ -884,8 +878,8 @@ connectDBStart(PGconn *conn)
/* Something's gone wrong */
printfPQExpBuffer(&conn->errorMessage,
"connectDBStart() -- connect() failed: %s\n"
- "Is the postmaster running%s at '%s' "
- "and accepting connections on %s '%s'?\n",
+ "\tIs the postmaster running%s at '%s'\n"
+ "\tand accepting connections on %s '%s'?\n",
strerror(errno),
(family == AF_INET) ? " (with -i)" : "",
conn->pghost ? conn->pghost : "localhost",
@@ -938,33 +932,31 @@ static int
connectDBComplete(PGconn *conn)
{
PostgresPollingStatusType flag;
- int r = 0, w = 1;
- do
- {
- if(pqWait(r, w, conn))
- {
- conn->status = CONNECTION_BAD;
- return 0;
- }
-
- again:
- switch(flag = PQconnectPoll(conn))
+ for (;;) {
+ flag = PQconnectPoll(conn);
+ switch (flag)
{
case PGRES_POLLING_ACTIVE:
- goto again;
+ break;
case PGRES_POLLING_OK:
- break;
+ return 1; /* success! */
case PGRES_POLLING_READING:
- r = 1;
- w = 0;
+ if (pqWait(1, 0, conn))
+ {
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
break;
case PGRES_POLLING_WRITING:
- r = 0;
- w = 1;
+ if (pqWait(0, 1, conn))
+ {
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
break;
default:
@@ -972,12 +964,9 @@ connectDBComplete(PGconn *conn)
conn->status = CONNECTION_BAD;
return 0;
}
- } while (flag != PGRES_POLLING_OK);
-
- return 1;
+ }
}
-
/* ----------------
* PQconnectPoll
*
@@ -993,17 +982,18 @@ connectDBComplete(PGconn *conn)
* there are a number of caveats:
*
* o If you call PQtrace, ensure that the stream object into which you trace
- will not block.
+ * will not block.
* o If you do not supply an IP address for the remote host (i.e. you
* supply a host name instead) then this function will block on
- * gethostbyname. You will be fine if using UDP (i.e. by supplying
- * neither a host name nor a host address).
+ * gethostbyname. You will be fine if using Unix sockets (i.e. by
+ * supplying neither a host name nor a host address).
* o If your backend wants to use Kerberos authentication then you must
* supply both a host name and a host address, otherwise this function
* may block on gethostname.
* o This function will block if compiled with USE_SSL.
*
- * ---------------- */
+ * ----------------
+ */
PostgresPollingStatusType
PQconnectPoll(PGconn *conn)
{
@@ -1024,8 +1014,6 @@ PQconnectPoll(PGconn *conn)
/* These are reading states */
case CONNECTION_AWAITING_RESPONSE:
- case CONNECTION_AUTH_RESPONSE:
- case CONNECTION_ERROR_RESPONSE:
case CONNECTION_AUTH_OK:
{
/* Load waiting data */
@@ -1083,10 +1071,21 @@ PQconnectPoll(PGconn *conn)
}
else if (optval != 0)
{
+ /*
+ * When using a nonblocking connect, we will typically see
+ * connect failures at this point, so provide a friendly
+ * error message.
+ */
printfPQExpBuffer(&conn->errorMessage,
- "PQconnectPoll() -- "
- "socket has error condition %d: %s.\n",
- optval, strerror(optval));
+ "PQconnectPoll() -- connect() failed: %s\n"
+ "\tIs the postmaster running%s at '%s'\n"
+ "\tand accepting connections on %s '%s'?\n",
+ strerror(optval),
+ (conn->raddr.sa.sa_family == AF_INET) ? " (with -i)" : "",
+ conn->pghost ? conn->pghost : "localhost",
+ (conn->raddr.sa.sa_family == AF_INET) ?
+ "TCP/IP port" : "Unix socket",
+ conn->pgport);
goto error_return;
}
@@ -1124,7 +1123,11 @@ PQconnectPoll(PGconn *conn)
if (conn->pgoptions)
strncpy(sp.options, conn->pgoptions, SM_OPTIONS);
- /* Send the startup packet. */
+ /* Send the startup packet.
+ *
+ * Theoretically, this could block, but it really shouldn't
+ * since we only got here if the socket is write-ready.
+ */
if (pqPacketSend(conn, (char *) &sp,
sizeof(StartupPacket)) != STATUS_OK)
@@ -1141,16 +1144,19 @@ PQconnectPoll(PGconn *conn)
return PGRES_POLLING_READING;
}
-
/*
- * Handle the authentication exchange: wait for backend messages
+ * Handle the authentication exchange: wait for postmaster messages
* and respond as necessary.
*/
case CONNECTION_AWAITING_RESPONSE:
{
char beresp;
+ AuthRequest areq;
- /* Scan the message */
+ /* Scan the message from current point (note that if we find
+ * the message is incomplete, we will return without advancing
+ * inStart, and resume here next time).
+ */
conn->inCursor = conn->inStart;
if (pqGetc(&beresp, conn))
@@ -1162,28 +1168,29 @@ PQconnectPoll(PGconn *conn)
/* Handle errors. */
if (beresp == 'E')
{
- conn->status = CONNECTION_ERROR_RESPONSE;
- goto keep_going;
+ if (pqGets(&conn->errorMessage, conn))
+ {
+ /* We'll come back when there are more data */
+ return PGRES_POLLING_READING;
+ }
+ /* OK, we read the message; mark data consumed */
+ conn->inStart = conn->inCursor;
+ /* The postmaster typically won't end its message with a
+ * newline, so add one to conform to libpq conventions.
+ */
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
+ goto error_return;
}
/* Otherwise it should be an authentication request. */
if (beresp != 'R')
{
printfPQExpBuffer(&conn->errorMessage,
- "PQconnectPoll() -- expected authentication "
- "request\n");
+ "PQconnectPoll() -- expected "
+ "authentication request\n");
goto error_return;
}
- /* Got an authentication request, so that's OK */
- conn->status = CONNECTION_AUTH_RESPONSE;
- goto keep_going;
- }
-
- case CONNECTION_AUTH_RESPONSE:
- {
- AuthRequest areq;
-
/* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn))
{
@@ -1205,7 +1212,7 @@ PQconnectPoll(PGconn *conn)
conn->inStart = conn->inCursor;
/* Respond to the request if necessary. */
- /* Note that conn->pghost must be non-NULL if we are going
+ /* Note that conn->pghost must be non-NULL if we are going to
* avoid the Kerberos code doing a hostname look-up. */
/* XXX fe-auth.c has not been fixed to support PQExpBuffers, so: */
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
@@ -1216,10 +1223,10 @@ PQconnectPoll(PGconn *conn)
}
conn->errorMessage.len = strlen(conn->errorMessage.data);
- /* This function has a section near the end that looks like it
- * should block. I think that it will be OK though, since the
- * socket is non-blocking, and thus the data should get out
- * as quickly as possible. */
+ /* Just make sure that any data sent by fe_sendauth is flushed
+ * out. Although this theoretically could block, it really
+ * shouldn't since we don't send large auth responses.
+ */
if (pqFlush(conn))
goto error_return;
@@ -1230,21 +1237,12 @@ PQconnectPoll(PGconn *conn)
/* Set asyncStatus so that PQsetResult will think that what
* comes back next is the result of a query. See below. */
conn->asyncStatus = PGASYNC_BUSY;
- goto keep_going;
}
- conn->status = CONNECTION_AWAITING_RESPONSE;
- return PGRES_POLLING_READING;
+ /* Look to see if we have more data yet. */
+ goto keep_going;
}
- case CONNECTION_ERROR_RESPONSE:
- if (pqGets(&conn->errorMessage, conn))
- {
- /* We'll come back when there are more data */
- return PGRES_POLLING_READING;
- }
- goto error_return;
-
case CONNECTION_AUTH_OK:
{
/* ----------
@@ -1254,14 +1252,12 @@ PQconnectPoll(PGconn *conn)
* messages indicating nonfatal warnings are also allowed by
* the protocol, as is a BackendKeyData message.) Easiest way
* to handle this is to let PQgetResult() read the messages. We
- * just have to fake it out about the state of the connection.
+ * just have to fake it out about the state of the connection,
+ * by setting asyncStatus = PGASYNC_BUSY (done above).
*----------
*/
- if (!PQconsumeInput(conn))
- goto error_return;
-
- if(PQisBusy(conn))
+ if (PQisBusy(conn))
return PGRES_POLLING_READING;
res = PQgetResult(conn);
@@ -1273,13 +1269,21 @@ PQconnectPoll(PGconn *conn)
printfPQExpBuffer(&conn->errorMessage,
"PQconnectPoll() -- unexpected message "
"during startup\n");
+ /* if the resultStatus is FATAL, then conn->errorMessage
+ * already has a copy of the error; needn't copy it back.
+ * But add a newline if it's not there already, since
+ * postmaster error messages may not have one.
+ */
+ if (conn->errorMessage.len <= 0 ||
+ conn->errorMessage.data[conn->errorMessage.len-1] != '\n')
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
PQclear(res);
goto error_return;
}
/*
- * Post-connection housekeeping. Send environment variables
- * to server.
+ * Post-connection housekeeping. Prepare to send environment
+ * variables to server.
*/
if ((conn->setenv_handle = PQsetenvStart(conn)) == NULL)
@@ -1292,10 +1296,10 @@ PQconnectPoll(PGconn *conn)
case CONNECTION_SETENV:
/* We pretend that the connection is OK for the duration of
- theses queries. */
+ these queries. */
conn->status = CONNECTION_OK;
- switch(PQsetenvPoll(conn->setenv_handle))
+ switch (PQsetenvPoll(conn->setenv_handle))
{
case PGRES_POLLING_OK: /* Success */
conn->status = CONNECTION_OK;
@@ -1318,8 +1322,7 @@ PQconnectPoll(PGconn *conn)
default:
printfPQExpBuffer(&conn->errorMessage,
"PQconnectPoll() -- unknown connection state - "
- "probably indicative of memory corruption!\n",
- sizeof(conn->errorMessage));
+ "probably indicative of memory corruption!\n");
goto error_return;
}
@@ -1378,12 +1381,13 @@ PQsetenvStart(PGconn *conn)
* Polls the process of passing the values of a standard set of environment
* variables to the backend.
*
- * ---------------- */
+ * ----------------
+ */
PostgresPollingStatusType
PQsetenvPoll(PGsetenvHandle handle)
{
#ifdef MULTIBYTE
- const char envname[] = "PGCLIENTENCODING";
+ static const char envname[] = "PGCLIENTENCODING";
#endif
if (!handle || handle->state == SETENV_STATE_FAILED)
@@ -1451,9 +1455,6 @@ PQsetenvPoll(PGsetenvHandle handle)
{
const char *encoding = 0;
- if (!PQconsumeInput(handle->conn))
- goto error_return;
-
if (PQisBusy(handle->conn))
return PGRES_POLLING_READING;
@@ -1535,9 +1536,6 @@ PQsetenvPoll(PGsetenvHandle handle)
case SETENV_STATE_OPTION_WAIT:
{
- if (!PQconsumeInput(handle->conn))
- goto error_return;
-
if (PQisBusy(handle->conn))
return PGRES_POLLING_READING;
@@ -1593,7 +1591,8 @@ PQsetenvPoll(PGsetenvHandle handle)
* Aborts the process of passing the values of a standard set of environment
* variables to the backend.
*
- * ---------------- */
+ * ----------------
+ */
void
PQsetenvAbort(PGsetenvHandle handle)
{
@@ -1619,54 +1618,51 @@ PQsetenvAbort(PGsetenvHandle handle)
* compatibility problems caused by giving it a return value, especially as
* this function has not been documented previously.
*
- * ---------------- */
+ * ----------------
+ */
int
PQsetenv(PGconn *conn)
{
- PostgresPollingStatusType flag;
PGsetenvHandle handle;
- int r = 0, w = 1;
-
- if((handle = PQsetenvStart(conn)) == NULL)
+ PostgresPollingStatusType flag;
+
+ if ((handle = PQsetenvStart(conn)) == NULL)
return 0;
- do
- {
- if(pqWait(r, w, conn))
- {
- /* XXX This is not a good sign - perhaps we should mark the
- connection as bad here... */
- return 0;
- }
-
- again:
- switch(flag = PQsetenvPoll(handle))
+ for (;;) {
+ flag = PQsetenvPoll(handle);
+ switch (flag)
{
case PGRES_POLLING_ACTIVE:
- goto again;
+ break;
case PGRES_POLLING_OK:
- break;
+ return 1; /* success! */
case PGRES_POLLING_READING:
- r = 1;
- w = 0;
+ if (pqWait(1, 0, conn))
+ {
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
break;
-
+
case PGRES_POLLING_WRITING:
- r = 0;
- w = 1;
+ if (pqWait(0, 1, conn))
+ {
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
break;
-
- default: /* Failed */
+
+ default:
+ /* Just in case we failed to set it in PQconnectPoll */
+ conn->status = CONNECTION_BAD;
return 0;
}
- } while (flag != PGRES_POLLING_OK);
-
- return 1;
+ }
}
-
/*
* makeEmptyPGconn
* - create a PGconn data structure with (as yet) no interesting data
@@ -1679,7 +1675,7 @@ makeEmptyPGconn(void)
if (conn == NULL)
return conn;
- /* Zero all pointers */
+ /* Zero all pointers and booleans */
MemSet((char *) conn, 0, sizeof(PGconn));
conn->noticeHook = defaultNoticeProcessor;
@@ -1687,6 +1683,10 @@ makeEmptyPGconn(void)
conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList();
conn->sock = -1;
+#ifdef USE_SSL
+ conn->allow_ssl_try = TRUE;
+#endif
+
/*
* 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
@@ -1709,6 +1709,7 @@ makeEmptyPGconn(void)
conn->errorMessage.data == NULL ||
conn->workBuffer.data == NULL)
{
+ /* out of memory already :-( */
freePGconn(conn);
conn = NULL;
}
@@ -1841,10 +1842,9 @@ PQreset(PGconn *conn)
{
closePGconn(conn);
- (void)(!connectDBStart(conn) || !connectDBComplete(conn));
+ if (connectDBStart(conn))
+ (void) connectDBComplete(conn);
}
-
- return;
}
@@ -1863,7 +1863,7 @@ PQresetStart(PGconn *conn)
return connectDBStart(conn);
}
- return 1;
+ return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 5a25c40121f..719d64d5a6a 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-fe.h,v 1.53 1999/11/30 03:08:19 momjian Exp $
+ * $Id: libpq-fe.h,v 1.54 2000/01/14 05:33:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,16 +41,9 @@ extern "C"
CONNECTION_STARTED, /* Waiting for connection to be made. */
CONNECTION_MADE, /* Connection OK; waiting to send. */
CONNECTION_AWAITING_RESPONSE, /* Waiting for a response
- from the backend. */
- CONNECTION_AUTH_RESPONSE, /* Got an authentication
- response; about to deal
- with it. */
- CONNECTION_ERROR_RESPONSE, /* Got an error
- response; about to deal
- with it. */
+ from the postmaster. */
CONNECTION_AUTH_OK, /* Received authentication;
- waiting for ReadyForQuery
- etc. */
+ waiting for backend startup. */
CONNECTION_SETENV /* Negotiating environment. */
} ConnStatusType;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 64566d6c743..779371fbda2 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -11,7 +11,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.14 1999/11/30 03:08:19 momjian Exp $
+ * $Id: libpq-int.h,v 1.15 2000/01/14 05:33:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,10 +27,9 @@
/* include stuff found in fe only */
#include "pqexpbuffer.h"
-
#ifdef USE_SSL
-#include "openssl/ssl.h"
-#include "openssl/err.h"
+#include
+#include
#endif
/* libpq supports this version of the frontend/backend protocol.
@@ -63,7 +62,7 @@
/* Subsidiary-storage management structure for PGresult.
* See space management routines in fe-exec.c for details.
* Note that space[k] refers to the k'th byte starting from the physical
- * head of the block.
+ * head of the block --- it's a union, not a struct!
*/
typedef union pgresult_data PGresult_data;
@@ -228,7 +227,8 @@ struct pg_conn
PGsetenvHandle setenv_handle;
#ifdef USE_SSL
- SSL *ssl;
+ bool allow_ssl_try; /* Allowed to try SSL negotiation */
+ SSL *ssl; /* SSL status, if have SSL connection */
#endif
/* Buffer for current error message */