⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fe-protocol3.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * fe-protocol3.c *	  functions that are specific to frontend/backend protocol version 3 * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.22.2.1 2005/11/22 18:23:30 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres_fe.h"#include <errno.h>#include <ctype.h>#include <fcntl.h>#include "libpq-fe.h"#include "libpq-int.h"#include "mb/pg_wchar.h"#ifdef WIN32#include "win32.h"#else#include <unistd.h>#include <netinet/in.h>#ifdef HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif#include <arpa/inet.h>#endif/* * This macro lists the backend message types that could be "long" (more * than a couple of kilobytes). */#define VALID_LONG_MESSAGE_TYPE(id) \	((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \	 (id) == 'E' || (id) == 'N' || (id) == 'A')static void handleSyncLoss(PGconn *conn, char id, int msgLength);static int	getRowDescriptions(PGconn *conn);static int	getAnotherTuple(PGconn *conn, int msgLength);static int	getParameterStatus(PGconn *conn);static int	getNotify(PGconn *conn);static int	getCopyStart(PGconn *conn, ExecStatusType copytype);static int	getReadyForQuery(PGconn *conn);static int build_startup_packet(const PGconn *conn, char *packet,					 const PQEnvironmentOption *options);/* * parseInput: if appropriate, parse input data from backend * until input is exhausted or a stopping state is reached. * Note that this function will NOT attempt to read more data from the backend. */voidpqParseInput3(PGconn *conn){	char		id;	int			msgLength;	int			avail;	/*	 * Loop to parse successive complete messages available in the buffer.	 */	for (;;)	{		/*		 * Try to read a message.  First get the type code and length. Return		 * if not enough data.		 */		conn->inCursor = conn->inStart;		if (pqGetc(&id, conn))			return;		if (pqGetInt(&msgLength, 4, conn))			return;		/*		 * Try to validate message type/length here.  A length less than 4 is		 * definitely broken.  Large lengths should only be believed for a few		 * message types.		 */		if (msgLength < 4)		{			handleSyncLoss(conn, id, msgLength);			return;		}		if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))		{			handleSyncLoss(conn, id, msgLength);			return;		}		/*		 * Can't process if message body isn't all here yet.		 */		msgLength -= 4;		avail = conn->inEnd - conn->inCursor;		if (avail < msgLength)		{			/*			 * Before returning, enlarge the input buffer if needed to hold			 * the whole message.  This is better than leaving it to			 * pqReadData because we can avoid multiple cycles of realloc()			 * when the message is large; also, we can implement a reasonable			 * recovery strategy if we are unable to make the buffer big			 * enough.			 */			if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))			{				/*				 * XXX add some better recovery code... plan is to skip over				 * the message using its length, then report an error. For the				 * moment, just treat this like loss of sync (which indeed it				 * might be!)				 */				handleSyncLoss(conn, id, msgLength);			}			return;		}		/*		 * NOTIFY and NOTICE messages can happen in any state; always process		 * them right away.		 *		 * Most other messages should only be processed while in BUSY state.		 * (In particular, in READY state we hold off further parsing until		 * the application collects the current PGresult.)		 *		 * However, if the state is IDLE then we got trouble; we need to deal		 * with the unexpected message somehow.		 *		 * ParameterStatus ('S') messages are a special case: in IDLE state we		 * must process 'em (this case could happen if a new value was adopted		 * from config file due to SIGHUP), but otherwise we hold off until		 * BUSY state.		 */		if (id == 'A')		{			if (getNotify(conn))				return;		}		else if (id == 'N')		{			if (pqGetErrorNotice3(conn, false))				return;		}		else if (conn->asyncStatus != PGASYNC_BUSY)		{			/* If not IDLE state, just wait ... */			if (conn->asyncStatus != PGASYNC_IDLE)				return;			/*			 * Unexpected message in IDLE state; need to recover somehow.			 * ERROR messages are displayed using the notice processor;			 * ParameterStatus is handled normally; anything else is just			 * dropped on the floor after displaying a suitable warning			 * notice.	(An ERROR is very possibly the backend telling us why			 * it is about to close the connection, so we don't want to just			 * discard it...)			 */			if (id == 'E')			{				if (pqGetErrorNotice3(conn, false /* treat as notice */ ))					return;			}			else if (id == 'S')			{				if (getParameterStatus(conn))					return;			}			else			{				pqInternalNotice(&conn->noticeHooks,						"message type 0x%02x arrived from server while idle",								 id);				/* Discard the unexpected message */				conn->inCursor += msgLength;			}		}		else		{			/*			 * In BUSY state, we can process everything.			 */			switch (id)			{				case 'C':		/* command complete */					if (pqGets(&conn->workBuffer, conn))						return;					if (conn->result == NULL)					{						conn->result = PQmakeEmptyPGresult(conn,														   PGRES_COMMAND_OK);						if (!conn->result)							return;					}					strncpy(conn->result->cmdStatus, conn->workBuffer.data,							CMDSTATUS_LEN);					conn->asyncStatus = PGASYNC_READY;					break;				case 'E':		/* error return */					if (pqGetErrorNotice3(conn, true))						return;					conn->asyncStatus = PGASYNC_READY;					break;				case 'Z':		/* backend is ready for new query */					if (getReadyForQuery(conn))						return;					conn->asyncStatus = PGASYNC_IDLE;					break;				case 'I':		/* empty query */					if (conn->result == NULL)					{						conn->result = PQmakeEmptyPGresult(conn,														   PGRES_EMPTY_QUERY);						if (!conn->result)							return;					}					conn->asyncStatus = PGASYNC_READY;					break;				case '1':		/* Parse Complete */					/* If we're doing PQprepare, we're done; else ignore */					if (conn->queryclass == PGQUERY_PREPARE)					{						if (conn->result == NULL)						{							conn->result = PQmakeEmptyPGresult(conn,														   PGRES_COMMAND_OK);							if (!conn->result)								return;						}						conn->asyncStatus = PGASYNC_READY;					}					break;				case '2':		/* Bind Complete */				case '3':		/* Close Complete */					/* Nothing to do for these message types */					break;				case 'S':		/* parameter status */					if (getParameterStatus(conn))						return;					break;				case 'K':		/* secret key data from the backend */					/*					 * This is expected only during backend startup, but it's					 * just as easy to handle it as part of the main loop.					 * Save the data and continue processing.					 */					if (pqGetInt(&(conn->be_pid), 4, conn))						return;					if (pqGetInt(&(conn->be_key), 4, conn))						return;					break;				case 'T':		/* Row Description */					if (conn->result == NULL)					{						/* First 'T' in a query sequence */						if (getRowDescriptions(conn))							return;					}					else					{						/*						 * A new 'T' message is treated as the start of						 * another PGresult.  (It is not clear that this is						 * really possible with the current backend.) We stop						 * parsing until the application accepts the current						 * result.						 */						conn->asyncStatus = PGASYNC_READY;						return;					}					break;				case 'n':		/* No Data */					/*					 * NoData indicates that we will not be seeing a					 * RowDescription message because the statement or portal					 * inquired about doesn't return rows. Set up a COMMAND_OK					 * result, instead of TUPLES_OK.					 */					if (conn->result == NULL)						conn->result = PQmakeEmptyPGresult(conn,														   PGRES_COMMAND_OK);					break;				case 'D':		/* Data Row */					if (conn->result != NULL &&						conn->result->resultStatus == PGRES_TUPLES_OK)					{						/* Read another tuple of a normal query response */						if (getAnotherTuple(conn, msgLength))							return;					}					else if (conn->result != NULL &&							 conn->result->resultStatus == PGRES_FATAL_ERROR)					{						/*						 * We've already choked for some reason.  Just discard						 * tuples till we get to the end of the query.						 */						conn->inCursor += msgLength;					}					else					{						/* Set up to report error at end of query */						printfPQExpBuffer(&conn->errorMessage,										  libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));						pqSaveErrorResult(conn);						/* Discard the unexpected message */						conn->inCursor += msgLength;					}					break;				case 'G':		/* Start Copy In */					if (getCopyStart(conn, PGRES_COPY_IN))						return;					conn->asyncStatus = PGASYNC_COPY_IN;					break;				case 'H':		/* Start Copy Out */					if (getCopyStart(conn, PGRES_COPY_OUT))						return;					conn->asyncStatus = PGASYNC_COPY_OUT;					conn->copy_already_done = 0;					break;				case 'd':		/* Copy Data */					/*					 * If we see Copy Data, just silently drop it.	This would					 * only occur if application exits COPY OUT mode too					 * early.					 */					conn->inCursor += msgLength;					break;				case 'c':		/* Copy Done */					/*					 * If we see Copy Done, just silently drop it.	This is					 * the normal case during PQendcopy.  We will keep					 * swallowing data, expecting to see command-complete for					 * the COPY command.					 */					break;				default:					printfPQExpBuffer(&conn->errorMessage,									  libpq_gettext(													"unexpected response from server; first received character was \"%c\"\n"),									  id);					/* build an error result holding the error message */					pqSaveErrorResult(conn);					/* not sure if we will see more, so go to ready state */					conn->asyncStatus = PGASYNC_READY;					/* Discard the unexpected message */					conn->inCursor += msgLength;					break;			}					/* switch on protocol character */		}		/* Successfully consumed this message */		if (conn->inCursor == conn->inStart + 5 + msgLength)		{			/* Normal case: parsing agrees with specified length */			conn->inStart = conn->inCursor;		}		else		{			/* Trouble --- report it */			printfPQExpBuffer(&conn->errorMessage,							  libpq_gettext("message contents do not agree with length in message type \"%c\"\n"),							  id);			/* build an error result holding the error message */			pqSaveErrorResult(conn);			conn->asyncStatus = PGASYNC_READY;			/* trust the specified message length as what to skip */			conn->inStart += 5 + msgLength;		}	}}/* * handleSyncLoss: clean up after loss of message-boundary sync * * There isn't really a lot we can do here except abandon the connection. */static voidhandleSyncLoss(PGconn *conn, char id, int msgLength){	printfPQExpBuffer(&conn->errorMessage,					  libpq_gettext(	"lost synchronization with server: got message type \"%c\", length %d\n"),					  id, msgLength);	/* build an error result holding the error message */	pqSaveErrorResult(conn);	conn->asyncStatus = PGASYNC_READY;	/* drop out of GetResult wait loop */	pqsecure_close(conn);	closesocket(conn->sock);	conn->sock = -1;	conn->status = CONNECTION_BAD;		/* No more connection to backend */}/* * parseInput subroutine to read a 'T' (row descriptions) message. * We build a PGresult structure containing the attribute data. * Returns: 0 if completed message, EOF if not enough data yet. * * Note that if we run out of data, we have to release the partially * constructed PGresult, and rebuild it again next time.  Fortunately, * that shouldn't happen often, since 'T' messages usually fit in a packet. */static intgetRowDescriptions(PGconn *conn){	PGresult   *result;	int			nfields;	int			i;	result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);	if (!result)		goto failure;	/* parseInput already read the 'T' label and message length. */	/* the next two bytes are the number of fields	*/	if (pqGetInt(&(result->numAttributes), 2, conn))		goto failure;	nfields = result->numAttributes;	/* allocate space for the attribute descriptors */	if (nfields > 0)	{		result->attDescs = (PGresAttDesc *)			pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);		if (!result->attDescs)			goto failure;		MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));	}	/* result->binary is true only if ALL columns are binary */	result->binary = (nfields > 0) ? 1 : 0;	/* get type info */	for (i = 0; i < nfields; i++)	{		int			tableid;		int			columnid;		int			typid;		int			typlen;		int			atttypmod;		int			format;		if (pqGets(&conn->workBuffer, conn) ||			pqGetInt(&tableid, 4, conn) ||			pqGetInt(&columnid, 2, conn) ||			pqGetInt(&typid, 4, conn) ||			pqGetInt(&typlen, 2, conn) ||			pqGetInt(&atttypmod, 4, conn) ||			pqGetInt(&format, 2, conn))		{			goto failure;		}		/*		 * Since pqGetInt treats 2-byte integers as unsigned, we need to		 * coerce these results to signed form.		 */		columnid = (int) ((int16) columnid);		typlen = (int) ((int16) typlen);		format = (int) ((int16) format);		result->attDescs[i].name = pqResultStrdup(result,												  conn->workBuffer.data);		if (!result->attDescs[i].name)			goto failure;		result->attDescs[i].tableid = tableid;		result->attDescs[i].columnid = columnid;		result->attDescs[i].format = format;		result->attDescs[i].typid = typid;		result->attDescs[i].typlen = typlen;		result->attDescs[i].atttypmod = atttypmod;		if (format != 1)			result->binary = 0;	}	/* Success! */	conn->result = result;	return 0;failure:	PQclear(result);	return EOF;}/* * parseInput subroutine to read a 'D' (row data) message. * We add another tuple to the existing PGresult structure. * Returns: 0 if completed message, EOF if error or not enough data yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received.  We keep a partially constructed * tuple in conn->curTuple, and avoid reallocating already-allocated storage. */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -