From 2aa64f79f5e977317dfd4189f30ec9f65d1b208b Mon Sep 17 00:00:00 2001 From: Tom Lane 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 98a0fb9243..a25dc0b8a6 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 8bc5b28ee9..aed201e36d 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 ea357ba613..a667fa70c4 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;