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 + -
显示快捷键?