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

📄 fe-protocol3.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * fe-protocol3.c *	  functions that are specific to frontend/backend protocol version 3 * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.9.2.1 2003/12/28 17:44:05 tgl 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);					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);					conn->asyncStatus = PGASYNC_READY;					break;				case '1':		/* Parse Complete */				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);	/* parseInput already read the 'T' label and message length. */	/* the next two bytes are the number of fields	*/	if (pqGetInt(&(result->numAttributes), 2, conn))	{		PQclear(result);		return EOF;	}	nfields = result->numAttributes;	/* allocate space for the attribute descriptors */	if (nfields > 0)	{		result->attDescs = (PGresAttDesc *)			pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);		MemSet((char *) 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))		{			PQclear(result);			return EOF;		}		/*		 * 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);		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;}/* * 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. */static intgetAnotherTuple(PGconn *conn, int msgLength){	PGresult   *result = conn->result;	int			nfields = result->numAttributes;	PGresAttValue *tup;	int			tupnfields;		/* # fields from tuple */	int			vlen;			/* length of the current field value */	int			i;	/* Allocate tuple space if first time for this data message */	if (conn->curTuple == NULL)

⌨️ 快捷键说明

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