connectDBStart函数最终会调用PQconnectPoll函数,处理CONNECTION_NEEDED状态的代码在 PQconnectPoll函数中,以便在异步启动过程中如果需要再次重新执行它可以很容易地重新执行。 但是,我们必须在这里运行一次,因为调用者希望从该例程成功返回,这意味着我们处于 PGRES_POLLING_WRITING 连接状态。上面的说明是下面代码的注释的翻译。

->status = CONNECTION_NEEDED;
/* The code for processing CONNECTION_NEEDED state is in PQconnectPoll(),
* so that it can easily be re-executed if needed again during the
* asynchronous startup process. However, we must run it once here,
* because callers expect a success return from this routine to mean that
* we are in PGRES_POLLING_WRITING connection state. */
if (PQconnectPoll(conn) == PGRES_POLLING_WRITING) return 1;
connect_errReturn:

连接句柄PGcon中的status是如下ConnStatusType类型的,这也是libpq暴露给调用者进行连接状态判断的枚举类型。

typedef enum{
CONNECTION_OK,
CONNECTION_BAD,
/* Non-blocking mode only below here */
/* The existence of these should never be relied upon - they should only be used for user feedback or similar purposes. 永远不应依赖这些的存在——它们只能用于用户反馈或类似目的 */
CONNECTION_STARTED, /* Waiting for connection to be made. */
CONNECTION_MADE, /* Connection OK; waiting to send. */
CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the postmaster. */
CONNECTION_AUTH_OK, /* Received authentication; waiting for backend startup. */
CONNECTION_SETENV, /* Negotiating environment. */
CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
CONNECTION_NEEDED, /* Internal state: connect() needed */
CONNECTION_CHECK_WRITABLE, /* Check if we could make a writable connection. */
CONNECTION_CONSUME, /* Wait for any pending message and consume them. */
CONNECTION_GSS_STARTUP /* Negotiating GSSAPI. */
} ConnStatusType;

PQconnectPoll函数用于轮询异步连接。此函数和 PQconnectStart 旨在允许在不因为远程 I/O阻塞的上情况下让客户程序的情况下建立连接。(也就是异步连接) 然而,有一些警告:
o 如果您调用 PQtrace,请确保您跟踪的流对象不会阻塞。
o 如果你没有为远程主机提供 IP 地址(即你提供了一个主机名),那么 PQconnectStart 将阻塞 gethostbyname。 如果使用 Unix 套接字(即既不提供主机名也不提供主机地址),你会很好。
o 如果您的后端想要使用 Kerberos 身份验证,那么您必须同时提供主机名和主机地址,否则此函数可能会阻塞 gethostname。
如下为PQconnectPoll函数返回的PostgresPollingStatusType状态。

typedef enum{
PGRES_POLLING_FAILED = 0,
PGRES_POLLING_READING, /* These two indicate that one may */
PGRES_POLLING_WRITING, /* use select before polling again. */
PGRES_POLLING_OK,
PGRES_POLLING_ACTIVE /* unused; keep for awhile for backwards compatibility */
} PostgresPollingStatusType;

PQconnectPoll函数主要就是根据连接句柄PGcon中的status ConnStatusType类型,处理连接后返回PostgresPollingStatusType。

PostgresPollingStatusType PQconnectPoll(PGconn *conn) {
bool reset_connection_state_machine = false;
bool need_new_connection = false;
PGresult *res;
char sebuf[PG_STRERROR_R_BUFLEN];
int optval;
PQExpBufferData savedMessage;

if (conn == NULL) return PGRES_POLLING_FAILED; // 这个情况肯定就是失败
switch (conn->status) { /* Get the new data */
/* We really shouldn't have been polled in these two cases, but we can handle it. 在这两种情况下我们真的不应该被轮询,但我们可以处理它 */
case CONNECTION_BAD: return PGRES_POLLING_FAILED; // 这个情况肯定就是失败
case CONNECTION_OK: return PGRES_POLLING_OK;

以下CONNECTION_AWAITING_RESPONSE、CONNECTION_AUTH_OK为连接读状态,调用pqReadData函数,如果为零,则返回PGRES_POLLING_READING状态。

case CONNECTION_AWAITING_RESPONSE: /* These are reading states */
case CONNECTION_AUTH_OK: { /* Load waiting data */
int n = pqReadData(conn); // pqReadData函数如果返回0表示没有数据可以获取,但是也没有检测到错误
if (n < 0) goto error_return;
if (n == 0) return PGRES_POLLING_READING;
break;
}

以下CONNECTION_STARTED、CONNECTION_MADE为连接写状态

case CONNECTION_STARTED: /* These are writing states, so we just proceed. */
case CONNECTION_MADE: break;

以下为连接协商状态

/* We allow pqSetenvPoll to decide whether to proceed. */
case CONNECTION_SETENV: break;
/* Special cases: proceed without waiting. */
case CONNECTION_SSL_STARTUP:
case CONNECTION_NEEDED:
case CONNECTION_CHECK_WRITABLE:
case CONNECTION_CONSUME:
case CONNECTION_GSS_STARTUP: break;
default:
appendPQExpBufferStr(&conn->errorMessage,libpq_gettext("invalid connection state, probably indicative of memory corruption\n"));
goto error_return;
}

尝试下一个网络地址或者主机。

keep_going:  /* We will come back to here until there is nothing left to do. 我们会回到这里直到无事可做 */  
if (conn->try_next_addr){ /* Time to advance to next address, or next host if no more addresses? */
if (conn->addr_cur && conn->addr_cur->ai_next) {
conn->addr_cur = conn->addr_cur->ai_next;
reset_connection_state_machine = true;
} else
conn->try_next_host = true;
conn->try_next_addr = false;
}
if (conn->try_next_host) { /* Time to advance to next connhost[] entry? 是时候进入下一个connhost[]条目了 */
pg_conn_host *ch;
struct addrinfo hint;
int thisport, ret;
char portstr[MAXPGPATH];
if (conn->whichhost + 1 >= conn->nconnhost) {
/* Oops, no more hosts. An appropriate error message is already set up, so just set the right status. 哎呀,没有更多的主机了。 已经设置了适当的错误消息,因此只需设置正确的状态 */
goto error_return;
}
conn->whichhost++;
release_conn_addrinfo(conn); /* Drop any address info for previous host */

/* Look up info for the new host. On failure, log the problem in conn->errorMessage, then loop around to try the next host. (Note we don't clear try_next_host until we've succeeded.) */
ch = &conn->connhost[conn->whichhost];
MemSet(&hint, 0, sizeof(hint)); /* Initialize hint structure */
hint.ai_socktype = SOCK_STREAM;
conn->addrlist_family = hint.ai_family = AF_UNSPEC;
/* Figure out the port number we're going to use. */
if (ch->port == NULL || ch->port[0] == '\0') thisport = DEF_PGPORT;
else {
if (!parse_int_param(ch->port, &thisport, conn, "port")) goto error_return;
if (thisport < 1 || thisport > 65535) {
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("invalid port number: \"%s\"\n"), ch->port);
goto keep_going;
}
}
snprintf(portstr, sizeof(portstr), "%d", thisport);
switch (ch->type) { /* Use pg_getaddrinfo_all() to resolve the address */
case CHT_HOST_NAME:
ret = pg_getaddrinfo_all(ch->host, portstr, &hint, &conn->addrlist);
if (ret || !conn->addrlist) {
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("could not translate host name \"%s\" to address: %s\n"),ch->host, gai_strerror(ret));
goto keep_going;
}
break;
case CHT_HOST_ADDRESS:
hint.ai_flags = AI_NUMERICHOST;
ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint, &conn->addrlist);
if (ret || !conn->addrlist) {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not parse network address \"%s\": %s\n"), ch->hostaddr, gai_strerror(ret));
goto keep_going;
}
break;
case CHT_UNIX_SOCKET:
#ifdef HAVE_UNIX_SOCKETS
conn->addrlist_family = hint.ai_family = AF_UNIX;
UNIXSOCK_PATH(portstr, thisport, ch->host);
if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN) {
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),portstr,(int) (UNIXSOCK_PATH_BUFLEN - 1));
goto keep_going;
}
/* NULL hostname tells pg_getaddrinfo_all to parse the service name as a Unix-domain socket path. NULL hostname 告诉 pg_getaddrinfo_all 将服务名称解析为 Unix 域套接字路径 */
ret = pg_getaddrinfo_all(NULL, portstr, &hint, &conn->addrlist);
if (ret || !conn->addrlist){
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),portstr, gai_strerror(ret));
goto keep_going;
}
#else
Assert(false);
#endif
break;
}
/* OK, scan this addrlist for a working server address */
conn->addr_cur = conn->addrlist;
reset_connection_state_machine = true;
conn->try_next_host = false;
}

重置连接状态机,(重新)为一组到单个服务器地址的连接尝试初始化我们的连接控制变量。 这些变量必须在各个连接尝试中保持不变,但是当我们开始考虑新服务器时,我们必须重新设置它们。

if (reset_connection_state_machine) {  /* Reset connection state machine? 重置连接状态机 */
/* (Re) initialize our connection control variables for a set of
* connection attempts to a single server address. These variables
* must persist across individual connection attempts, but we must
* reset them when we start to consider a new server. */
conn->pversion = PG_PROTOCOL(3, 0);
conn->send_appname = true;
#ifdef USE_SSL
/* initialize these values based on SSL mode */
conn->allow_ssl_try = (conn->sslmode[0] != 'd'); /* "disable" */
conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
#endif
#ifdef ENABLE_GSS
conn->try_gss = (conn->gssencmode[0] != 'd'); /* "disable" */
#endif
reset_connection_state_machine = false;
need_new_connection = true;
}

强制建立新连接(可能与以前相同的服务器)

/* Force a new connection (perhaps to the same server as before)? 强制建立新连接(可能与以前相同的服务器) */
if (need_new_connection) {
pqDropConnection(conn, true); /* Drop any existing connection */
pqDropServerData(conn); /* Reset all state obtained from old server */
conn->asyncStatus = PGASYNC_IDLE; /* Drop any PGresult we might have, too */
conn->xactStatus = PQTRANS_IDLE;
pqClearAsyncResult(conn);
conn->status = CONNECTION_NEEDED; /* Reset conn->status to put the state machine in the right state */
need_new_connection = false;
}

尝试为此连接推进状态机,为CONNECTION_NEEDED状态进行处理

/* Now try to advance the state machine for this connection */
switch (conn->status) {
case CONNECTION_NEEDED: {
/* Try to initiate a connection to one of the addresses returned by pg_getaddrinfo_all(). conn->addr_cur is the next one to try. The extra level of braces here is historical. It's not worth reindenting this whole switch case to remove 'em. 尝试启动与 pg_getaddrinfo_all() 返回的地址之一的连接。 conn->addr_cur 是下一个尝试。 这里额外的大括号是历史性的。 重新缩进整个开关盒以删除它们是不值得的。 */
{
struct addrinfo *addr_cur = conn->addr_cur;
char host_addr[NI_MAXHOST];
/* Advance to next possible host, if we've tried all of the addresses for the current host. 如果我们已经尝试了当前主机的所有地址,则前进到下一个可能的主机。 */
if (addr_cur == NULL) {
conn->try_next_host = true;
goto keep_going;
}
/* Remember current address for possible error msg */
memcpy(&conn->raddr.addr, addr_cur->ai_addr, addr_cur->ai_addrlen);
conn->raddr.salen = addr_cur->ai_addrlen;
if (conn->connip != NULL) { /* set connip */
free(conn->connip);
conn->connip = NULL;
}
getHostaddr(conn, host_addr, NI_MAXHOST);
if (strlen(host_addr) > 0) conn->connip = strdup(host_addr);
/* purposely ignore strdup failure; not a big problem if it fails anyway. */
conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
if (conn->sock == PGINVALID_SOCKET) {
/* Silently ignore socket() failure if we have more addresses to try; this reduces useless chatter in cases where the address list includes both IPv4 and IPv6 but kernel only accepts one family. 如果我们有更多的地址可以尝试,默默地忽略 socket() 失败; 在地址列表同时包含 IPv4 和 IPv6 但内核只接受一个系列的情况下,这减少了无用的喋喋不休 */
if (addr_cur->ai_next != NULL || conn->whichhost + 1 < conn->nconnhost) {
conn->try_next_addr = true;
goto keep_going;
}
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create socket: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}
/* Select socket options: no delay of outgoing data for TCP sockets, nonblock mode, close-on-exec. Try the next address if any of this fails. 选择套接字选项:TCP 套接字的传出数据无延迟、非阻塞模式、执行时关闭。 如果其中任何一个失败,请尝试下一个地址 */
if (!IS_AF_UNIX(addr_cur->ai_family)) {
if (!connectNoDelay(conn)) { /* error message already created */
conn->try_next_addr = true;
goto keep_going;
}
}
if (!pg_set_noblock(conn->sock)) { // fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) == -1 设置为非阻塞状态
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to nonblocking mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
conn->try_next_addr = true;
goto keep_going;
}
#ifdef F_SETFD
if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1) {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to close-on-exec mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
conn->try_next_addr = true;
goto keep_going;
}
#endif /* F_SETFD */
if (!IS_AF_UNIX(addr_cur->ai_family)) {
int on = 1;
int usekeepalives = useKeepalives(conn);
int err = 0;
if (usekeepalives < 0) {
appendPQExpBufferStr(&conn->errorMessage, libpq_gettext("keepalives parameter must be an integer\n"));
err = 1;
} else if (usekeepalives == 0) { /* Do nothing */ }
else if (setsockopt(conn->sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("setsockopt(%s) failed: %s\n"), "SO_KEEPALIVE", SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
err = 1;
}else if (!setKeepalivesIdle(conn) || !setKeepalivesInterval(conn) || !setKeepalivesCount(conn)) err = 1;
else if (!setTCPUserTimeout(conn)) err = 1;
if (err) {
conn->try_next_addr = true;
goto keep_going;
}
}
/*----------
* We have three methods of blocking SIGPIPE during send() calls to this socket:
* 在 send() 调用此套接字期间,我们有三种阻塞 SIGPIPE 的方法
* - setsockopt(sock, SO_NOSIGPIPE)
* - send(sock, ..., MSG_NOSIGNAL)
* - setting the signal mask to SIG_IGN during send()
* The third method requires three syscalls per send, so we prefer either of the first two, but they are less portable. The state is tracked in the following members of PGconn: 第三种方法每次发送需要三个系统调用,因此我们更喜欢前两种中的任何一种,但它们的可移植性较差。 在 PGconn 的以下成员中跟踪状态
* conn->sigpipe_so - we have set up SO_NOSIGPIPE
* conn->sigpipe_flag - we're specifying MSG_NOSIGNAL
* 如果我们可以使用 SO_NOSIGPIPE,那么在这里设置 sigpipe_so 就完成了。 否则,设置 sigpipe_flag 以便我们在发送时尝试 MSG_NOSIGNAL。 如果我们收到 MSG_NOSIGNAL 错误,我们将清除该标志并恢复为信号屏蔽
* If we can use SO_NOSIGPIPE, then set sigpipe_so here and we're done. Otherwise, set sigpipe_flag so that we will try MSG_NOSIGNAL on sends. If we get an error with MSG_NOSIGNAL, we'll clear that flag and revert to signal masking.
*---------- */
conn->sigpipe_so = false;
#ifdef MSG_NOSIGNAL
conn->sigpipe_flag = true;
#else
conn->sigpipe_flag = false;
#endif /* MSG_NOSIGNAL */
#ifdef SO_NOSIGPIPE
optval = 1;
if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, (char *) &optval, sizeof(optval)) == 0) {
conn->sigpipe_so = true;
conn->sigpipe_flag = false;
}
#endif /* SO_NOSIGPIPE */

/* Start/make connection. This should not block, since we are in nonblock mode. If it does, well, too bad. 开始/建立连接。 这不应该阻塞,因为我们处于非阻塞模式。 如果是这样,好吧,太糟糕了 */
if (connect(conn->sock, addr_cur->ai_addr, addr_cur->ai_addrlen) < 0) {
if (SOCK_ERRNO == EINPROGRESS || SOCK_ERRNO == EINTR) {
/* This is fine - we're in non-blocking mode, and the connection is in progress. Tell caller to wait for write-ready on socket. 这很好 - 我们处于非阻塞模式,并且连接正在进行中。 告诉调用者等待套接字上的写入就绪 */
conn->status = CONNECTION_STARTED;
return PGRES_POLLING_WRITING;
} /* otherwise, trouble */
} else {
/* Hm, we're connected already --- seems the "nonblock connection" wasn't. Advance the state machine and go do the next stuff. 嗯,我们已经连接了 --- 似乎“非阻塞连接”不是。 推进状态机并去做下一件事情 */
conn->status = CONNECTION_STARTED;
goto keep_going;
}
/* This connection failed. Add the error report to conn->errorMessage, then try the next address if any. 此连接失败。 将错误报告添加到conn->errorMessage,然后尝试下一个地址(如果有) */
connectFailureMessage(conn, SOCK_ERRNO);
conn->try_next_addr = true;
goto keep_going;
}
}

尝试为此连接推进状态机,为CONNECTION_STARTED状态进行处理

case CONNECTION_STARTED:{
ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
/* Write ready, since we've made it here, so the connection has been made ... or has failed. 写好,既然我们已经做到了,所以连接已经建立……或者失败了 */
/* Now check (using getsockopt) that there is not an error state waiting for us on the socket. 现在检查(使用getsockopt)套接字上没有等待我们的错误状态 */
if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &optval, &optlen) == -1){
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("could not get socket error status: %s\n"),SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}else if (optval != 0){
/* When using a nonblocking connect, we will typically see connect failures at this point, so provide a friendly error message. 使用非阻塞连接时,我们通常会在此时看到连接失败,因此请提供友好的错误消息 */
connectFailureMessage(conn, optval);
/* Try the next address if any, just as in the case where connect() returned failure immediately.*/
conn->try_next_addr = true;
goto keep_going;
}
conn->laddr.salen = sizeof(conn->laddr.addr); /* Fill in the client address */
if (getsockname(conn->sock,(struct sockaddr *) &conn->laddr.addr,&conn->laddr.salen) < 0) {
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("could not get client address from socket: %s\n"),SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}
/* Make sure we can write before advancing to next step. 确保我们可以在进行下一步之前编写。 */
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
}

尝试为此连接推进状态机,为CONNECTION_MADE状态进行处理,这里启动认证请求,构建启动包。

case CONNECTION_MADE: {
char *startpacket;
int packetlen;
#ifdef HAVE_UNIX_SOCKETS
/* Implement requirepeer check, if requested and it's a Unix-domain socket. */
if (conn->requirepeer && conn->requirepeer[0] && IS_AF_UNIX(conn->raddr.addr.ss_family)) {
char pwdbuf[BUFSIZ];
struct passwd pass_buf; struct passwd *pass; int passerr;
uid_t uid; gid_t gid; errno = 0;
if (getpeereid(conn->sock, &uid, &gid) != 0) {
/* Provide special error message if getpeereid is a stub */
if (errno == ENOSYS) appendPQExpBufferStr(&conn->errorMessage, libpq_gettext("requirepeer parameter is not supported on this platform\n"));
else appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get peer credentials: %s\n"), strerror_r(errno, sebuf, sizeof(sebuf)));
goto error_return;
}
passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
if (pass == NULL) {
if (passerr != 0) appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not look up local user ID %d: %s\n"), (int) uid, strerror_r(passerr, sebuf, sizeof(sebuf)));
else appendPQExpBuffer(&conn->errorMessage, libpq_gettext("local user with ID %d does not exist\n"), (int) uid);
goto error_return;
}
if (strcmp(pass->pw_name, conn->requirepeer) != 0){
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"),conn->requirepeer, pass->pw_name);
goto error_return;
}
}
#endif /* HAVE_UNIX_SOCKETS */
if (IS_AF_UNIX(conn->raddr.addr.ss_family)){
/* Don't request SSL or GSSAPI over Unix sockets */
#ifdef USE_SSL
conn->allow_ssl_try = false;
#endif
#ifdef ENABLE_GSS
conn->try_gss = false;
#endif
}
#ifdef ENABLE_GSS
/* If GSSAPI is enabled and we have a credential cache, try to set it up before sending startup messages. If it's already operating, don't try SSL and instead just build the startup packet. 如果启用了 GSSAPI 并且我们有凭据缓存,请尝试在发送启动消息之前对其进行设置。 如果它已经在运行,请不要尝试 SSL,而只需构建启动数据包。 */
if (conn->try_gss && !conn->gctx) conn->try_gss = pg_GSS_have_cred_cache(&conn->gcred);
if (conn->try_gss && !conn->gctx) {
ProtocolVersion pv = pg_hton32(NEGOTIATE_GSS_CODE);
if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not send GSSAPI negotiation packet: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}
conn->status = CONNECTION_GSS_STARTUP; /* Ok, wait for response */
return PGRES_POLLING_READING;
}else if (!conn->gctx && conn->gssencmode[0] == 'r') {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"));
goto error_return;
}
#endif

#ifdef USE_SSL

/* If SSL is enabled and we haven't already got encryption of some sort running, request SSL instead of sending the startup message. */
if (conn->allow_ssl_try && !conn->wait_ssl_try && !conn->ssl_in_use
#ifdef ENABLE_GSS
&& !conn->gssenc
#endif
){
ProtocolVersion pv;
/* Send the SSL request packet. Theoretically, this could block, but it really shouldn't since we only got here if the socket is write-ready. 发送 SSL 请求数据包。 从理论上讲,这可能会阻塞,但实际上不应该,因为我们只有在套接字准备好写入时才到达这里。 */
pv = pg_hton32(NEGOTIATE_SSL_CODE);
if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) {
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("could not send SSL negotiation packet: %s\n"),SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}
conn->status = CONNECTION_SSL_STARTUP; /* Ok, wait for response */
return PGRES_POLLING_READING;
}
#endif /* USE_SSL */

/* Build the startup packet. 构建启动包 */
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
startpacket = pqBuildStartupPacket3(conn, &packetlen, EnvironmentOptions);
else
startpacket = pqBuildStartupPacket2(conn, &packetlen, EnvironmentOptions);
if (!startpacket) {
/* will not appendbuffer here, since it's likely to also run out of memory 不会在这里追加缓冲区,因为它也可能会耗尽内存 */
printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
goto error_return;
}

/* 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, 0, startpacket, packetlen) != STATUS_OK) {
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not send startup packet: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
free(startpacket);
goto error_return;
}
free(startpacket);
conn->status = CONNECTION_AWAITING_RESPONSE;
return PGRES_POLLING_READING;
}

尝试为此连接推进状态机,为CONNECTION_SSL_STARTUP状态进行处理SSL协商。

/* Handle SSL negotiation: wait for postmaster messages and respond as necessary. 处理 SSL 协商:等待 postmaster 消息并根据需要做出响应。 */
case CONNECTION_SSL_STARTUP: {
#ifdef USE_SSL
PostgresPollingStatusType pollres;
/* On first time through, get the postmaster's response to our SSL negotiation packet. 在第一次通过时,获取postmaster对我们的 SSL 协商数据包的响应。 */
if (!conn->ssl_in_use) {
/* We use pqReadData here since it has the logic to distinguish no-data-yet from connection closure. Since conn->ssl isn't set, a plain recv() will occur. */
char SSLok;
int rdresult;
rdresult = pqReadData(conn);
if (rdresult < 0){
goto error_return; /* errorMessage is already filled in */
}
if (rdresult == 0){
return PGRES_POLLING_READING; /* caller failed to wait for data */
}
if (pqGetc(&SSLok, conn) < 0){
return PGRES_POLLING_READING; /* should not happen really */
}
if (SSLok == 'S') {
conn->inStart = conn->inCursor; /* mark byte consumed */
if (pqsecure_initialize(conn) != 0) /* Set up global SSL state if required */
goto error_return;
}else if (SSLok == 'N'){
conn->inStart = conn->inCursor; /* mark byte consumed */
/* OK to do without SSL? */
if (conn->sslmode[0] == 'r' || /* "require" */ conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */{
/* Require SSL, but server does not want it */
appendPQExpBufferStr(&conn->errorMessage, libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return;
}
conn->allow_ssl_try = false; /* Otherwise, proceed with normal startup */
conn->status = CONNECTION_MADE; /* We can proceed using this connection */
return PGRES_POLLING_WRITING;
}else if (SSLok == 'E'){
/* Server failure of some sort, such as failure to fork a backend process. We need to process and report the error message, which might be formatted according to either protocol 2 or protocol 3. Rather than duplicate the code for that, we flip into AWAITING_RESPONSE state and let the code there deal with it. Note we have *not* consumed the "E" byte here. 某种服务器故障,例如无法派生后端进程。 我们需要处理和报告错误消息,它可能根据协议 2 或协议 3 进行格式化。与其复制代码,我们切换到 AWAITING_RESPONSE 状态并让那里的代码处理它。 请注意,我们*没有*在这里消耗了“E”字节。 */
conn->status = CONNECTION_AWAITING_RESPONSE;
goto keep_going;
}else{
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("received invalid response to SSL negotiation: %c\n"),SSLok);
goto error_return;
}
}

/* Begin or continue the SSL negotiation process. */
pollres = pqsecure_open_client(conn);
if (pollres == PGRES_POLLING_OK) {
conn->status = CONNECTION_MADE; /* SSL handshake done, ready to send startup packet */
return PGRES_POLLING_WRITING;
}
if (pollres == PGRES_POLLING_FAILED){
/* Failed ... if sslmode is "prefer" then do a non-SSL retry */
if (conn->sslmode[0] == 'p' /* "prefer" */ && conn->allow_ssl_try /* redundant? */ && !conn->wait_ssl_try) /* redundant? */ {
conn->allow_ssl_try = false; /* only retry once */
need_new_connection = true;
goto keep_going;
}
goto error_return; /* Else it's a hard failure */
}
/* Else, return POLLING_READING or POLLING_WRITING status */
return pollres;
#else /* !USE_SSL */
/* can't get here */
goto error_return;
#endif /* USE_SSL */
}

尝试为此连接推进状态机,为CONNECTION_SSL_STARTUP状态进行处理GSS协商。

case CONNECTION_GSS_STARTUP:{
#ifdef ENABLE_GSS
PostgresPollingStatusType pollres;
/* If we haven't yet, get the postmaster's response to our negotiation packet */
if (conn->try_gss && !conn->gctx) {
char gss_ok;
int rdresult = pqReadData(conn);
if (rdresult < 0) goto error_return; /* pqReadData fills in error message */
else if (rdresult == 0) return PGRES_POLLING_READING; /* caller failed to wait for data */
if (pqGetc(&gss_ok, conn) < 0) return PGRES_POLLING_READING; /* shouldn't happen... */
if (gss_ok == 'E'){
/* Server failure of some sort. Assume it's a protocol version support failure, and let's see if we can't recover (if it's not, we'll get a better error message on retry). Server gets fussy if we don't hang up the socket, though. 某种服务器故障。 假设它是协议版本支持失败,让我们看看我们是否无法恢复(如果不是,我们将在重试时得到更好的错误消息)。 但是,如果我们不挂断套接字,服务器就会变得很挑剔。 */
conn->try_gss = false;
need_new_connection = true;
goto keep_going;
}
conn->inStart = conn->inCursor; /* mark byte consumed */
if (gss_ok == 'N') {
/* Server doesn't want GSSAPI; fall back if we can */
if (conn->gssencmode[0] == 'r'){
appendPQExpBufferStr(&conn->errorMessage,libpq_gettext("server doesn't support GSSAPI encryption, but it was required\n"));
goto error_return;
}
conn->try_gss = false;
/* We can proceed using this connection */
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
}else if (gss_ok != 'G'){
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("received invalid response to GSSAPI negotiation: %c\n"), gss_ok);
goto error_return;
}
}

/* Begin or continue GSSAPI negotiation */
pollres = pqsecure_open_gss(conn);
if (pollres == PGRES_POLLING_OK) {
conn->status = CONNECTION_MADE; /* All set for startup packet */
return PGRES_POLLING_WRITING;
}else if (pollres == PGRES_POLLING_FAILED && conn->gssencmode[0] == 'p'){
/* We failed, but we can retry on "prefer". Have to drop the current connection to do so, though. */
conn->try_gss = false;
need_new_connection = true;
goto keep_going;
}
return pollres;
#else /* !ENABLE_GSS */
/* unreachable */
goto error_return;
#endif /* ENABLE_GSS */
}

尝试为此连接推进状态机,为CONNECTION_AWAITING_RESPONSE状态进行认证交互。

/* Handle authentication exchange: wait for postmaster messages and respond as necessary. */
case CONNECTION_AWAITING_RESPONSE: {
char beresp;
int msgLength; int avail; int res;
AuthRequest areq;
/* 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). 从当前点扫描消息(注意,如果发现消息不完整,我们将不前进 inStart 就返回,下次在这里继续) */
conn->inCursor = conn->inStart;
if (pqGetc(&beresp, conn)) { /* Read type byte */
return PGRES_POLLING_READING; /* We'll come back when there is more data */
}
/* Validate message type: we expect only an authentication request or an error here. Anything else probably means it's not Postgres on the other end at all. 验证消息类型:我们希望这里只有一个身份验证请求或一个错误。 其他任何事情都可能意味着它根本不是另一端的 Postgres */
if (!(beresp == 'R' || beresp == 'E')){
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("expected authentication request from server, but received %c\n"), beresp);
goto error_return;
}
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3){
if (pqGetInt(&msgLength, 4, conn)){ /* Read message length word */
return PGRES_POLLING_READING; /* We'll come back when there is more data */
}
}else{
msgLength = 8; /* Set phony message length to disable checks below */
}

/* Try to validate message length before using it. Authentication requests can't be very large, although GSS auth requests may not be that small. Errors can be a little larger, but not huge. If we see a large apparent length in an error, it means we're really talking to a pre-3.0-protocol server; cope. */
if (beresp == 'R' && (msgLength < 8 || msgLength > 2000)){
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("expected authentication request from server, but received %c\n"), beresp);
goto error_return;
}
if (beresp == 'E' && (msgLength < 8 || msgLength > 30000)){
/* Handle error from a pre-3.0 server */
conn->inCursor = conn->inStart + 1; /* reread data */
if (pqGets_append(&conn->errorMessage, conn)){
/* We'll come back when there is 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. postmaster通常不会以换行符结束它的消息,所以添加一个以符合 libpq 约定 */
appendPQExpBufferChar(&conn->errorMessage, '\n');
/* If we tried to open the connection in 3.0 protocol, fall back to 2.0 protocol. 如果我们尝试在 3.0 协议中打开连接,请回退到 2.0 协议 */
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3){
conn->pversion = PG_PROTOCOL(2, 0);
need_new_connection = true;
goto keep_going;
}
goto error_return;
}

/* Can't process if message body isn't all here yet.
* (In protocol 2.0 case, we are assuming messages carry at least 4 bytes of data.) */
msgLength -= 4;
avail = conn->inEnd - conn->inCursor;
if (avail < msgLength){
/* Before returning, try to enlarge the input buffer if needed to hold the whole message; see notes in pqParseInput3. */
if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, conn))
goto error_return;
return PGRES_POLLING_READING; /* We'll come back when there is more data */
}

if (beresp == 'E') { /* Handle errors. */
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3){
if (pqGetErrorNotice3(conn, true)){
return PGRES_POLLING_READING; /* We'll come back when there is more data */
}
}else{
if (pqGets_append(&conn->errorMessage, conn)){
return PGRES_POLLING_READING; /* We'll come back when there is more data */
}
}
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
/* Check to see if we should mention pgpassfile */
pgpassfileWarning(conn);
#ifdef ENABLE_GSS

/* If gssencmode is "prefer" and we're using GSSAPI, retry without it. */
if (conn->gssenc && conn->gssencmode[0] == 'p') {
/* only retry once */
conn->try_gss = false;
need_new_connection = true;
goto keep_going;
}
#endif

#ifdef USE_SSL

/* if sslmode is "allow" and we haven't tried an SSL connection already, then retry with an SSL connection */
if (conn->sslmode[0] == 'a' /* "allow" */&& !conn->ssl_in_use&& conn->allow_ssl_try&& conn->wait_ssl_try){
/* only retry once */
conn->wait_ssl_try = false;
need_new_connection = true;
goto keep_going;
}
/* if sslmode is "prefer" and we're in an SSL connection, then do a non-SSL retry */
if (conn->sslmode[0] == 'p' /* "prefer" */&& conn->ssl_in_use&& conn->allow_ssl_try /* redundant? */&& !conn->wait_ssl_try) /* redundant? */{
/* only retry once */
conn->allow_ssl_try = false;
need_new_connection = true;
goto keep_going;
}
#endif
goto error_return;
}

conn->auth_req_received = true; /* It is an authentication request. */
/* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn)){
/* We'll come back when there are more data */
return PGRES_POLLING_READING;
}
msgLength -= 4;

/*
* Ensure the password salt is in the input buffer, if it's an
* MD5 request. All the other authentication methods that
* contain extra data in the authentication request are only
* supported in protocol version 3, in which case we already
* read the whole message above.
*/
if (areq == AUTH_REQ_MD5 && PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
msgLength += 4;

avail = conn->inEnd - conn->inCursor;
if (avail < 4)
{
/*
* Before returning, try to enlarge the input buffer
* if needed to hold the whole message; see notes in
* pqParseInput3.
*/
if (pqCheckInBufferSpace(conn->inCursor + (size_t) 4,
conn))
goto error_return;
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
}

/* Process the rest of the authentication request message, and
* respond to it if necessary.
* Note that conn->pghost must be non-NULL if we are going to
* avoid the Kerberos code doing a hostname look-up. */
res = pg_fe_sendauth(areq, msgLength, conn);
conn->errorMessage.len = strlen(conn->errorMessage.data);
/* OK, we have processed the message; mark data consumed */
conn->inStart = conn->inCursor;
if (res != STATUS_OK) goto error_return;
/* Just make sure that any data sent by pg_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;
if (areq == AUTH_REQ_OK){
conn->status = CONNECTION_AUTH_OK; /* We are done with authentication exchange */
/* Set asyncStatus so that PQgetResult will think that what comes back next is the result of a query. See below. */
conn->asyncStatus = PGASYNC_BUSY;
}
goto keep_going; /* Look to see if we have more data yet. */
}

尝试为此连接推进状态机,为CONNECTION_AUTH_OK状态进行处理。现在我们希望听到来自后端的消息。 ReadyForQuery 消息表明启动成功,但我们也可能会收到表明失败的错误消息。 (协议也允许指示非致命警告的通知消息,ParameterStatus 和 BackendKeyData 消息也是如此。)处理此问题的最简单方法是让 PQgetResult() 读取消息。 我们只需要通过设置 asyncStatus = PGASYNC_BUSY 来伪造连接状态(在上面完成)

case CONNECTION_AUTH_OK:
{
/* Now we expect to hear from the backend. A ReadyForQuery
* message indicates that startup is successful, but we might
* also get an Error message indicating failure. (Notice
* messages indicating nonfatal warnings are also allowed by
* the protocol, as are ParameterStatus and BackendKeyData
* messages.) 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, by setting
* asyncStatus = PGASYNC_BUSY (done above). */
if (PQisBusy(conn)) return PGRES_POLLING_READING;
res = PQgetResult(conn);
/* NULL return indicating we have gone to IDLE state is expected */
if (res){
if (res->resultStatus != PGRES_FATAL_ERROR)
appendPQExpBufferStr(&conn->errorMessage, libpq_gettext("unexpected message from server during startup\n"));
else if (conn->send_appname && (conn->appname || conn->fbappname)){
/* If we tried to send application_name, check to see
* if the error is about that --- pre-9.0 servers will
* reject it at this stage of the process. If so,
* close the connection and retry without sending
* application_name. We could possibly get a false
* SQLSTATE match here and retry uselessly, but there
* seems no great harm in that; we'll just get the
* same error again if it's unrelated. */ 如果我们尝试发送 application_name,请检查错误是否与此有关 --- 9.0 之前的服务器将在该过程的这个阶段拒绝它。 如果是这样,请关闭连接并重试而不发送 application_name。 我们可能会在这里得到一个错误的 SQLSTATE 匹配并无用地重试,但这似乎没有太大的危害; 如果它不相关,我们将再次收到相同的错误。
const char *sqlstate;
sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
if (sqlstate &&strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0){
PQclear(res);
conn->send_appname = false;
need_new_connection = true;
goto keep_going;
}
}

/* 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;
}

/* Fire up post-connection housekeeping if needed */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
conn->status = CONNECTION_SETENV;
conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND;
conn->next_eo = EnvironmentOptions;
return PGRES_POLLING_WRITING;
}

/* If a read-write connection is required, see if we have one.
* Servers before 7.4 lack the transaction_read_only GUC, but
* by the same token they don't have any read-only mode, so we
* may just skip the test in that case. 如果需要读写连接,看看我们有没有。 7.4 之前的服务器缺少 transaction_read_only GUC,但同样的,它们没有任何只读模式,所以在这种情况下我们可以跳过测试。*/
if (conn->sversion >= 70400 && conn->target_session_attrs != NULL && strcmp(conn->target_session_attrs, "read-write") == 0) {
/* Save existing error messages across the PQsendQuery attempt. This is necessary because PQsendQuery is going to reset conn->errorMessage, so we would lose error messages related to previous hosts we have tried and failed to connect to. */
if (!saveErrorMessage(conn, &savedMessage)) goto error_return;
conn->status = CONNECTION_OK;
if (!PQsendQuery(conn, "SHOW transaction_read_only")) {
restoreErrorMessage(conn, &savedMessage);
goto error_return;
}
conn->status = CONNECTION_CHECK_WRITABLE;
restoreErrorMessage(conn, &savedMessage);
return PGRES_POLLING_READING;
}
release_conn_addrinfo(conn); /* We can release the address list now. */
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
}

尝试为此连接推进状态机,为CONNECTION_SETENV状态进行处理。进行连接后内务处理(仅在协议 2.0 中需要)。 我们假设在这些查询期间连接正常。

case CONNECTION_SETENV:
/* Do post-connection housekeeping (only needed in protocol 2.0). We pretend that the connection is OK for the duration of these queries. */
conn->status = CONNECTION_OK;
switch (pqSetenvPoll(conn)){
case PGRES_POLLING_OK: /* Success */
break;
case PGRES_POLLING_READING: /* Still going */
conn->status = CONNECTION_SETENV;
return PGRES_POLLING_READING;
case PGRES_POLLING_WRITING: /* Still going */
conn->status = CONNECTION_SETENV;
return PGRES_POLLING_WRITING;
default:
goto error_return;
}

/* If a read-write connection is required, see if we have one. (This should match the stanza in the CONNECTION_AUTH_OK case above.)
* Servers before 7.4 lack the transaction_read_only GUC, but by the same token they don't have any read-only mode, so we may just skip the test in that case. */ 如果需要读写连接,看看我们有没有。 (这应该与上面 CONNECTION_AUTH_OK 案例中的节匹配。)7.4 之前的服务器缺少 transaction_read_only GUC,但出于同样的原因,它们没有任何只读模式,因此在这种情况下我们可以跳过测试。
if (conn->sversion >= 70400 &&conn->target_session_attrs != NULL &&strcmp(conn->target_session_attrs, "read-write") == 0){
if (!saveErrorMessage(conn, &savedMessage))goto error_return;
conn->status = CONNECTION_OK;
if (!PQsendQuery(conn, "SHOW transaction_read_only")){
restoreErrorMessage(conn, &savedMessage);
goto error_return;
}
conn->status = CONNECTION_CHECK_WRITABLE;
restoreErrorMessage(conn, &savedMessage);
return PGRES_POLLING_READING;
}
release_conn_addrinfo(conn); /* We can release the address list now. */
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;

尝试为此连接推进状态机,为CONNECTION_CONSUME状态进行处理。

case CONNECTION_CONSUME:{
conn->status = CONNECTION_OK;
if (!PQconsumeInput(conn)) goto error_return;
if (PQisBusy(conn)){
conn->status = CONNECTION_CONSUME;
return PGRES_POLLING_READING;
}
/* Call PQgetResult() again to consume NULL result. */
res = PQgetResult(conn);
if (res != NULL) {
PQclear(res);
conn->status = CONNECTION_CONSUME;
goto keep_going;
}
release_conn_addrinfo(conn); /* We can release the address list now. */
conn->status = CONNECTION_OK; /* We are open for business! */
return PGRES_POLLING_OK;
}

尝试为此连接推进状态机,为CONNECTION_CHECK_WRITABLE状态进行处理。

case CONNECTION_CHECK_WRITABLE:{
const char *displayed_host;
const char *displayed_port;
if (!saveErrorMessage(conn, &savedMessage)) goto error_return;
conn->status = CONNECTION_OK;
if (!PQconsumeInput(conn)){
restoreErrorMessage(conn, &savedMessage);
goto error_return;
}
if (PQisBusy(conn)){
conn->status = CONNECTION_CHECK_WRITABLE;
restoreErrorMessage(conn, &savedMessage);
return PGRES_POLLING_READING;
}
res = PQgetResult(conn);
if (res && (PQresultStatus(res) == PGRES_TUPLES_OK) &&PQntuples(res) == 1){
char *val;
val = PQgetvalue(res, 0, 0);
if (strncmp(val, "on", 2) == 0){
PQclear(res); /* Not writable; fail this connection. */
restoreErrorMessage(conn, &savedMessage);
/* Append error report to conn->errorMessage. */
if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) displayed_host = conn->connhost[conn->whichhost].hostaddr;
else displayed_host = conn->connhost[conn->whichhost].host;
displayed_port = conn->connhost[conn->whichhost].port;
if (displayed_port == NULL || displayed_port[0] == '\0') displayed_port = DEF_PGPORT_STR;
appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not make a writable connection to server \"%s:%s\"\n"), displayed_host, displayed_port);
/* Close connection politely. */
conn->status = CONNECTION_OK;
sendTerminateConn(conn);
/* Try next host if any, but we don't want to consider additional addresses for this host. */
conn->try_next_host = true;
goto keep_going;
}
/* Session is read-write, so we're good. */
PQclear(res);
termPQExpBuffer(&savedMessage);
/* Finish reading any remaining messages before being considered as ready. */
conn->status = CONNECTION_CONSUME;
goto keep_going;
}

/* Something went wrong with "SHOW transaction_read_only". We should try next addresses. */
if (res) PQclear(res);
restoreErrorMessage(conn, &savedMessage);
/* Append error report to conn->errorMessage. */
if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) displayed_host = conn->connhost[conn->whichhost].hostaddr;
else displayed_host = conn->connhost[conn->whichhost].host;
displayed_port = conn->connhost[conn->whichhost].port;
if (displayed_port == NULL || displayed_port[0] == '\0') displayed_port = DEF_PGPORT_STR;
appendPQExpBuffer(&conn->errorMessage,libpq_gettext("test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"),displayed_host, displayed_port);
/* Close connection politely. */
conn->status = CONNECTION_OK;
sendTerminateConn(conn);
/* Try next address */
conn->try_next_addr = true;
goto keep_going;
}

default:
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid connection state %d, "
"probably indicative of memory corruption\n"),
conn->status);
goto error_return;
}

/* Unreachable */
error_return:
/* We used to close the socket at this point, but that makes it awkward
* for those above us if they wish to remove this socket from their own
* records (an fd_set for example). We'll just have this socket closed
* when PQfinish is called (which is compulsory even after an error, since
* the connection structure must be freed).
*/
conn->status = CONNECTION_BAD;
return PGRES_POLLING_FAILED;
}