diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index b9207128c350255c3ac1610c58e3103e06dd9c02..290236e8b6556727bb48ec5310d1f0058727b923 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 a5380822ace81f152d5f56846d576eee4bd7887f..1d2426e798246740d1f08ea8ead04161cc873e79 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) - return 0; + PostgresPollingStatusType flag; - do - { - if(pqWait(r, w, conn)) - { - /* XXX This is not a good sign - perhaps we should mark the - connection as bad here... */ - return 0; - } + if ((handle = PQsetenvStart(conn)) == NULL) + 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 5a25c40121f199cc642f52da0d6361a700286bf5..719d64d5a6a581b09c88fe42c89033151943483f 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 64566d6c743f5273e24f8757c81e79d5a109e532..779371fbda2dc72c05aa1f8dffd7c490d29c116e 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 */