📄 fe-connect.c
字号:
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 && conn->allow_ssl_try /* redundant? */ && !conn->wait_ssl_try) /* redundant? */ { /* only retry once */ conn->allow_ssl_try = false; /* Must drop the old connection */ pqsecure_close(conn); closesocket(conn->sock); conn->sock = -1; conn->status = CONNECTION_NEEDED; goto keep_going; }#endif goto error_return; } /* 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; } /* Get the password salt if there is one. */ if (areq == AUTH_REQ_MD5) { if (pqGetnchar(conn->md5Salt, sizeof(conn->md5Salt), conn)) { /* We'll come back when there are more data */ return PGRES_POLLING_READING; } } if (areq == AUTH_REQ_CRYPT) { if (pqGetnchar(conn->cryptSalt, sizeof(conn->cryptSalt), conn)) { /* We'll come back when there are more data */ return PGRES_POLLING_READING; } } /* * OK, we successfully read the message; mark data consumed */ conn->inStart = conn->inCursor; /* Respond to the request if necessary. */ /* * 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 (pg_fe_sendauth(areq, conn, conn->pghost, conn->pgpass, conn->errorMessage.data) != STATUS_OK) { conn->errorMessage.len = strlen(conn->errorMessage.data); goto error_return; } conn->errorMessage.len = strlen(conn->errorMessage.data); /* * 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) { /* We are done with authentication exchange */ conn->status = CONNECTION_AUTH_OK; /* * Set asyncStatus so that PQsetResult will think that * what comes back next is the result of a query. See * below. */ conn->asyncStatus = PGASYNC_BUSY; } /* Look to see if we have more data yet. */ goto keep_going; } 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) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected message from server 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; } /* We can release the address list now. */ pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); conn->addrlist = NULL; conn->addr_cur = NULL; /* Fire up post-connection housekeeping if needed */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { conn->status = CONNECTION_SETENV; conn->setenv_state = SETENV_STATE_OPTION_SEND; conn->next_eo = EnvironmentOptions; return PGRES_POLLING_WRITING; } /* Otherwise, we are open for business! */ conn->status = CONNECTION_OK; return PGRES_POLLING_OK; } 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; } /* We are open for business! */ conn->status = CONNECTION_OK; return PGRES_POLLING_OK; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "invalid connection state %c, " "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;}/* * makeEmptyPGconn * - create a PGconn data structure with (as yet) no interesting data */static PGconn *makeEmptyPGconn(void){ PGconn *conn;#ifdef WIN32 /* * Make sure socket support is up and running. Even though this is done in * libpqdll.c, that is only for MSVC and BCC builds and doesn't work for * static builds at all, so we have to do it in the main code too. */ WSADATA wsaData; if (WSAStartup(MAKEWORD(1, 1), &wsaData)) return NULL; WSASetLastError(0);#endif conn = (PGconn *) malloc(sizeof(PGconn)); if (conn == NULL) return conn; /* Zero all pointers and booleans */ MemSet(conn, 0, sizeof(PGconn)); conn->noticeHooks.noticeRec = defaultNoticeReceiver; conn->noticeHooks.noticeProc = defaultNoticeProcessor; conn->status = CONNECTION_BAD; conn->asyncStatus = PGASYNC_IDLE; conn->xactStatus = PQTRANS_IDLE; conn->setenv_state = SETENV_STATE_IDLE; conn->client_encoding = PG_SQL_ASCII; conn->std_strings = false; /* unless server says differently */ conn->verbosity = PQERRORS_DEFAULT; conn->sock = -1;#ifdef USE_SSL conn->allow_ssl_try = true; conn->wait_ssl_try = false;#endif /* * We try to send at least 8K at a time, which is the usual size of pipe * buffers on Unix systems. That way, when we are sending a large amount * of data, we avoid incurring extra kernel context swaps for partial * bufferloads. The output buffer is initially made 16K in size, and we * try to dump it after accumulating 8K. * * With the same goal of minimizing context swaps, the input buffer will * be enlarged anytime it has less than 8K free, so we initially allocate * twice that. */ conn->inBufSize = 16 * 1024; conn->inBuffer = (char *) malloc(conn->inBufSize); conn->outBufSize = 16 * 1024; conn->outBuffer = (char *) malloc(conn->outBufSize); conn->nonblocking = FALSE; initPQExpBuffer(&conn->errorMessage); initPQExpBuffer(&conn->workBuffer); if (conn->inBuffer == NULL || conn->outBuffer == NULL || conn->errorMessage.data == NULL || conn->workBuffer.data == NULL) { /* out of memory already :-( */ freePGconn(conn); conn = NULL; } return conn;}/* * freePGconn * - free the PGconn data structure * * When changing/adding to this function, see also closePGconn! */static voidfreePGconn(PGconn *conn){ PGnotify *notify; pgParameterStatus *pstatus;#ifdef WIN32 WSACleanup();#endif if (!conn) return; pqClearAsyncResult(conn); /* deallocate result and curTuple */ if (conn->sock >= 0) { pqsecure_close(conn); closesocket(conn->sock); } if (conn->pghost) free(conn->pghost); if (conn->pghostaddr) free(conn->pghostaddr); if (conn->pgport) free(conn->pgport); if (conn->pgunixsocket) free(conn->pgunixsocket); if (conn->pgtty) free(conn->pgtty); if (conn->connect_timeout) free(conn->connect_timeout); if (conn->pgoptions) free(conn->pgoptions); if (conn->dbName) free(conn->dbName); if (conn->pguser) free(conn->pguser); if (conn->pgpass) free(conn->pgpass); if (conn->sslmode) free(conn->sslmode);#ifdef KRB5 if (conn->krbsrvname) free(conn->krbsrvname);#endif /* Note that conn->Pfdebug is not ours to close or free */ pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); notify = conn->notifyHead; while (notify != NULL) { PGnotify *prev = notify; notify = notify->next; free(prev); } pstatus = conn->pstatus; while (pstatus != NULL) { pgParameterStatus *prev = pstatus; pstatus = pstatus->next; free(prev); } if (conn->lobjfuncs) free(conn->lobjfuncs); if (conn->inBuffer) free(conn->inBuffer); if (conn->outBuffer) free(conn->outBuffer); termPQExpBuffer(&conn->errorMessage); termPQExpBuffer(&conn->workBuffer); free(conn);}/* * closePGconn * - properly close a connection to the backend * * Release all transient state, but NOT the connection parameters. */static voidclosePGconn(PGconn *conn){ PGnotify *notify; pgParameterStatus *pstatus; /* * Note that the protocol doesn't allow us to send Terminate messages * during the startup phase. */ if (conn->sock >= 0 && conn->status == CONNECTION_OK) { /* * Try to send "close connection" message to backend. Ignore any * error. */ pqPutMsgStart('X', false, conn); pqPutMsgEnd(conn); pqFlush(conn); } /* * must reset the blocking status so a possible reconnect will work don't * call PQsetnonblocking() because it will fail if it's unable to flush * the connection. */ conn->nonblocking = FALSE; /* * Close the connection, reset all transient state, flush I/O buffers. */ if (conn->sock >= 0) { pqsecure_close(conn); closesocket(conn->sock); } conn->sock = -1; conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just * absent */ conn->asyncStatus = PGASYNC_IDLE; pqClearAsyncResult(conn); /* deallocate result and curTuple */ pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); conn->addrlist = NULL; conn->addr_cur = NULL; notify = conn->notifyHead; while (notify != NULL) { PGnotify *prev = notify; notify = notify->next; free(prev); } conn->notifyHead = NULL; pstatus = conn->pstatus; while (pstatus != NULL) { pgParameterStatus *prev = pstatus; pstatus = pstatus->next; free(prev); } conn->pstatus = NULL; if (conn->lobjfuncs) free(conn->lobjfuncs); conn->lobjfuncs = NULL; conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0;}/* * PQfinish: properly close a connection to the backend. Also frees * the PGconn data structure so it shouldn't be re-used after this. */voidPQfinish(PGconn *conn){ if (conn) { closePGconn(conn); freePGconn(conn); }}/* * PQreset: resets the connection to the backend by closing the * existing connection and creating a new one. */voidPQreset(PGconn *conn){ if (conn) { closePGconn(conn); if (connectDBStart(conn)) (void) connectDBComplete(conn); }}/* * PQresetStart: * resets the connection to the backend * closes the existing connection and makes a new one * Returns 1 on success, 0 on failure. */intPQresetStart(PGconn *conn){ if (conn) { closePGconn(conn); return connectDBStart(conn); } return 0;}/* * PQresetPoll: * resets the connection to the backend * closes the existing connection and makes a new one */PostgresPollingStatusTypePQresetPoll(PGconn *conn){ if (conn) return PQconnectPoll(conn); return PGRES_POLLING_FAILED;}/* * PQcancelGet: get a PGcancel structure corresponding to a connection.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -