fe-connect.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,647 行 · 第 1/5 页
C
2,647 行
* 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 > 100)) { printfPQExpBuffer(&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(&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. */ appendPQExpBufferChar(&conn->errorMessage, '\n'); /* * If we tried to open the connection in 3.0 protocol, * fall back to 2.0 protocol. */ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) { conn->pversion = PG_PROTOCOL(2, 0); /* Must drop the old connection */ pqsecure_close(conn); closesocket(conn->sock); conn->sock = -1; conn->status = CONNECTION_NEEDED; 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 + msgLength, conn)) goto error_return; /* We'll come back when there is more data */ return PGRES_POLLING_READING; } /* Handle errors. */ if (beresp == 'E') { if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) { if (pqGetErrorNotice3(conn, true)) { /* We'll come back when there is more data */ return PGRES_POLLING_READING; } } else { if (pqGets(&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;#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 == NULL && conn->allow_ssl_try && conn->wait_ssl_try) { /* only retry once */ conn->wait_ssl_try = false; /* Must drop the old connection */ closesocket(conn->sock); conn->sock = -1; conn->status = CONNECTION_NEEDED; 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 (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 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. */ 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 /* needed to use the static libpq under windows as well */ WSADATA wsaData; if (WSAStartup(MAKEWORD(1, 1), &wsaData)) return (PGconn *) NULL; WSASetLastError(0);#endif conn = (PGconn *) malloc(sizeof(PGconn)); if (conn == NULL) return conn; /* Zero all pointers and booleans */ MemSet((char *) 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->verbosity = PQERRORS_DEFAULT; conn->notifyList = DLNewList(); 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 */static voidfreePGconn(PGconn *conn){ pgParameterStatus *pstatus; 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); /* Note that conn->Pfdebug is not ours to close or free */ if (conn->notifyList) DLFreeList(conn->notifyList); freeaddrinfo_all(conn->addrlist_family, conn->addrlist); 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 */static voidclosePGconn(PGconn *conn){ /* * 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 */ 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)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?