From 2aa64f79f5e977317dfd4189f30ec9f65d1b208b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 22 Jul 1999 02:40:07 +0000
Subject: [PATCH] Plug several holes in backend's ability to cope with
 unexpected loss of connection to frontend.

---
 src/backend/commands/copy.c | 40 ++++++++++++++++++--------
 src/backend/tcop/fastpath.c | 36 ++++++++++++++++--------
 src/backend/tcop/postgres.c | 56 ++++++++++++++++++++-----------------
 3 files changed, 84 insertions(+), 48 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 98a0fb92439..a25dc0b8a69 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.85 1999/07/17 20:16:51 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.86 1999/07/22 02:40:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,15 +54,19 @@ static void GetIndexRelations(Oid main_relation_oid,
 #ifdef COPY_PATCH
 static void CopyReadNewline(FILE *fp, int *newline);
 static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline);
-
 #else
 static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim);
-
 #endif
+
 static void CopyAttributeOut(FILE *fp, char *string, char *delim, int is_array);
 static int	CountTuples(Relation relation);
 
+/*
+ * Static communication variables ... pretty grotty, but COPY has
+ * never been reentrant...
+ */
 static int	lineno;
+static bool	fe_eof;
 
 /*
  * Internal communications functions
@@ -90,7 +94,10 @@ static void
 CopySendData(void *databuf, int datasize, FILE *fp)
 {
 	if (!fp)
-		pq_putbytes((char *) databuf, datasize);
+	{
+		if (pq_putbytes((char *) databuf, datasize))
+			fe_eof = true;
+	}
 	else
 		fwrite(databuf, datasize, 1, fp);
 }
@@ -121,7 +128,10 @@ static void
 CopyGetData(void *databuf, int datasize, FILE *fp)
 {
 	if (!fp)
-		pq_getbytes((char *) databuf, datasize);
+	{
+		if (pq_getbytes((char *) databuf, datasize))
+			fe_eof = true;
+	}
 	else
 		fread(databuf, datasize, 1, fp);
 }
@@ -134,7 +144,10 @@ CopyGetChar(FILE *fp)
 		unsigned char ch;
 
 		if (pq_getbytes((char *) &ch, 1))
+		{
+			fe_eof = true;
 			return EOF;
+		}
 		return ch;
 	}
 	else
@@ -145,8 +158,7 @@ static int
 CopyGetEof(FILE *fp)
 {
 	if (!fp)
-		return 0;				/* Never return EOF when talking to
-								 * frontend ? */
+		return fe_eof;
 	else
 		return feof(fp);
 }
@@ -154,7 +166,7 @@ CopyGetEof(FILE *fp)
 /*
  * CopyPeekChar reads a byte in "peekable" mode.
  * after each call to CopyPeekChar, a call to CopyDonePeek _must_
- * follow.
+ * follow, unless EOF was returned.
  * CopyDonePeek will either take the peeked char off the steam
  * (if pickup is != 0) or leave it on the stream (if pickup == 0)
  */
@@ -162,7 +174,12 @@ static int
 CopyPeekChar(FILE *fp)
 {
 	if (!fp)
-		return pq_peekbyte();
+	{
+		int ch = pq_peekbyte();
+		if (ch == EOF)
+			fe_eof = true;
+		return ch;
+	}
 	else
 		return getc(fp);
 }
@@ -668,6 +685,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim)
 	}
 
 	lineno = 0;
+	fe_eof = false;
+
 	while (!done)
 	{
 		if (!binary)
@@ -1193,10 +1212,7 @@ CopyReadAttribute(FILE *fp, bool *isnull, char *delim)
 							else
 							{
 								if (CopyGetEof(fp))
-								{
-									CopyDonePeek(fp, c, 1);		/* pick up */
 									return NULL;
-								}
 								CopyDonePeek(fp, c, 0); /* Return to stream! */
 							}
 						}
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 8bc5b28ee92..aed201e36d6 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.29 1999/07/17 20:17:50 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.30 1999/07/22 02:40:07 tgl Exp $
  *
  * NOTES
  *	  This cruft is the server side of PQfn.
@@ -265,8 +265,11 @@ update_fp_info(Oid func_id, struct fp_info * fip)
  * This corresponds to the libpq protocol symbol "F".
  *
  * RETURNS:
- *		nothing of significance.
- *		All errors result in elog(ERROR,...).
+ *		0 if successful completion, EOF if frontend connection lost.
+ *
+ * Note: All ordinary errors result in elog(ERROR,...).  However,
+ * if we lose the frontend connection there is no one to elog to,
+ * and no use in proceeding...
  */
 int
 HandleFunctionRequest()
@@ -282,9 +285,11 @@ HandleFunctionRequest()
 	char	   *p;
 	struct fp_info *fip;
 
-	pq_getint(&tmp, 4);			/* function oid */
+	if (pq_getint(&tmp, 4))		/* function oid */
+		return EOF;
 	fid = (Oid) tmp;
-	pq_getint(&nargs, 4);		/* # of arguments */
+	if (pq_getint(&nargs, 4))	/* # of arguments */
+		return EOF;
 
 	/*
 	 * This is where the one-back caching is done. If you want to save
@@ -294,6 +299,13 @@ HandleFunctionRequest()
 	if (!valid_fp_info(fid, fip))
 		update_fp_info(fid, fip);
 
+	/*
+	 * XXX FIXME: elog() here means we lose sync with the frontend,
+	 * since we have not swallowed all of its input message.  What
+	 * should happen is we absorb all of the input message per protocol
+	 * syntax, and *then* do error checking and elog if appropriate.
+	 */
+
 	if (fip->nargs != nargs)
 	{
 		elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
@@ -311,13 +323,15 @@ HandleFunctionRequest()
 			arg[i] = (char *) NULL;
 		else
 		{
-			pq_getint(&argsize, 4);
+			if (pq_getint(&argsize, 4))
+				return EOF;
 
 			Assert(argsize > 0);
 			if (fip->argbyval[i])
 			{					/* by-value */
 				Assert(argsize <= 4);
-				pq_getint(&tmp, argsize);
+				if (pq_getint(&tmp, argsize))
+					return EOF;
 				arg[i] = (char *) tmp;
 			}
 			else
@@ -329,14 +343,16 @@ HandleFunctionRequest()
 																 * 98 Jan 6 */
 						elog(ERROR, "HandleFunctionRequest: palloc failed");
 					VARSIZE(p) = argsize + VARHDRSZ;
-					pq_getbytes(VARDATA(p), argsize);
+					if (pq_getbytes(VARDATA(p), argsize))
+						return EOF;
 				}
 				else
 				{				/* ... fixed */
 					/* XXX cross our fingers and trust "argsize" */
 					if (!(p = palloc(argsize + 1)))
 						elog(ERROR, "HandleFunctionRequest: palloc failed");
-					pq_getbytes(p, argsize);
+					if (pq_getbytes(p, argsize))
+						return EOF;
 				}
 				palloced |= (1 << i);
 				arg[i] = p;
@@ -374,7 +390,5 @@ HandleFunctionRequest()
 	if (!fip->retbyval)
 		pfree(retval);
 
-
-
 	return 0;
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ea357ba613a..a667fa70c40 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.126 1999/07/19 02:27:06 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.127 1999/07/22 02:40:07 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -158,9 +158,9 @@ int			_exec_repeat_ = 1;
  *		decls for routines only used in this file
  * ----------------------------------------------------------------
  */
-static char InteractiveBackend(char *inBuf);
-static char SocketBackend(char *inBuf);
-static char ReadCommand(char *inBuf);
+static int InteractiveBackend(char *inBuf);
+static int SocketBackend(char *inBuf);
+static int ReadCommand(char *inBuf);
 static void pg_exec_query(char *query_string);
 
 
@@ -172,10 +172,12 @@ static void pg_exec_query(char *query_string);
 /* ----------------
  *	InteractiveBackend() is called for user interactive connections
  *	the string entered by the user is placed in its parameter inBuf.
+ *
+ *  EOF is returned if end-of-file input is seen; time to shut down.
  * ----------------
  */
 
-static char
+static int
 InteractiveBackend(char *inBuf)
 {
 	char	   *stuff = inBuf;	/* current place in input buffer */
@@ -244,8 +246,7 @@ InteractiveBackend(char *inBuf)
 		{
 			if (Verbose)
 				puts("EOF");
-			IsEmptyQuery = true;
-			proc_exit(0);
+			return EOF;
 		}
 
 		/* ----------------
@@ -274,11 +275,13 @@ InteractiveBackend(char *inBuf)
  *
  *	If the input is a fastpath function call (case 'F') then
  *	the function call is processed in HandleFunctionRequest().
- *	(now called from PostgresMain())
+ *	(now called from PostgresMain()).
+ *
+ *  EOF is returned if the connection is lost.
  * ----------------
  */
 
-static char
+static int
 SocketBackend(char *inBuf)
 {
 	char		qtype;
@@ -290,13 +293,7 @@ SocketBackend(char *inBuf)
 	 */
 	qtype = '?';
 	if (pq_getbytes(&qtype, 1) == EOF)
-	{
-		/* ------------
-		 *	when front-end applications quits/dies
-		 * ------------
-		 */
-		proc_exit(0);
-	}
+		return EOF;
 
 	switch (qtype)
 	{
@@ -305,7 +302,8 @@ SocketBackend(char *inBuf)
 			 * ----------------
 			 */
 		case 'Q':
-			pq_getstr(inBuf, MAX_PARSE_BUFFER);
+			if (pq_getstr(inBuf, MAX_PARSE_BUFFER))
+				return EOF;
 			result = 'Q';
 			break;
 
@@ -314,8 +312,8 @@ SocketBackend(char *inBuf)
 			 * ----------------
 			 */
 		case 'F':
-			pq_getstr(inBuf, MAX_PARSE_BUFFER); /* ignore the rest of the
-												 * line */
+			if (pq_getstr(inBuf, MAX_PARSE_BUFFER))
+				return EOF;		/* ignore "string" at start of F message */
 			result = 'F';
 			break;
 
@@ -345,10 +343,10 @@ SocketBackend(char *inBuf)
  *		ReadCommand reads a command from either the frontend or
  *		standard input, places it in inBuf, and returns a char
  *		representing whether the string is a 'Q'uery or a 'F'astpath
- *		call.
+ *		call.  EOF is returned if end of file.
  * ----------------
  */
-static char
+static int
 ReadCommand(char *inBuf)
 {
 	if (IsUnderPostmaster)
@@ -890,7 +888,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	bool		secure = true;
 	int			errs = 0;
 
-	char		firstchar;
+	int			firstchar;
 	char		parser_input[MAX_PARSE_BUFFER];
 	char	   *userName;
 
@@ -1494,7 +1492,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.126 $ $Date: 1999/07/19 02:27:06 $\n");
+		puts("$Revision: 1.127 $ $Date: 1999/07/22 02:40:07 $\n");
 	}
 
 	/* ----------------
@@ -1581,7 +1579,12 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 					TPRINTF(TRACE_VERBOSE, "StartTransactionCommand");
 				StartTransactionCommand();
 
-				HandleFunctionRequest();
+				if (HandleFunctionRequest() == EOF)
+				{
+					/* lost frontend connection during F message input */
+					pq_close();
+					proc_exit(0);
+				}
 				break;
 
 				/* ----------------
@@ -1621,10 +1624,13 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 				break;
 
 				/* ----------------
-				 *	'X' means that the frontend is closing down the socket
+				 *	'X' means that the frontend is closing down the socket.
+				 *	EOF means unexpected loss of frontend connection.
+				 *	Either way, perform normal shutdown.
 				 * ----------------
 				 */
 			case 'X':
+			case EOF:
 				pq_close();
 				proc_exit(0);
 				break;
-- 
GitLab