diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7fa29531cc..0f7f210099 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -102,7 +102,9 @@ typedef struct CopyStateData FILE *copy_file; /* used if copy_dest == COPY_FILE */ StringInfo fe_msgbuf; /* used for all dests during COPY TO, only for * dest == COPY_NEW_FE in COPY FROM */ - bool fe_eof; /* true if detected end of copy data */ + bool is_copy_from; /* COPY TO, or COPY FROM? */ + bool reached_eof; /* true if we read to end of copy data (not + * all copy_dest types maintain this) */ EolType eol_type; /* EOL type of input */ int file_encoding; /* file or remote side's character encoding */ bool need_transcoding; /* file encoding diff from server? */ @@ -566,6 +568,8 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read from COPY file: %m"))); + if (bytesread == 0) + cstate->reached_eof = true; break; case COPY_OLD_FE: @@ -586,7 +590,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread) bytesread = minread; break; case COPY_NEW_FE: - while (maxread > 0 && bytesread < minread && !cstate->fe_eof) + while (maxread > 0 && bytesread < minread && !cstate->reached_eof) { int avail; @@ -614,7 +618,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread) break; case 'c': /* CopyDone */ /* COPY IN correctly terminated by frontend */ - cstate->fe_eof = true; + cstate->reached_eof = true; return bytesread; case 'f': /* CopyFail */ ereport(ERROR, @@ -1040,6 +1044,8 @@ ProcessCopyOptions(ParseState *pstate, if (cstate == NULL) cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); + cstate->is_copy_from = is_from; + cstate->file_encoding = -1; /* Extract options from the statement node tree */ @@ -1717,11 +1723,23 @@ ClosePipeToProgram(CopyState cstate) (errcode_for_file_access(), errmsg("could not close pipe to external command: %m"))); else if (pclose_rc != 0) + { + /* + * If we ended a COPY FROM PROGRAM before reaching EOF, then it's + * expectable for the called program to fail with SIGPIPE, and we + * should not report that as an error. Otherwise, SIGPIPE indicates a + * problem. + */ + if (cstate->is_copy_from && !cstate->reached_eof && + WIFSIGNALED(pclose_rc) && WTERMSIG(pclose_rc) == SIGPIPE) + return; + ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("program \"%s\" failed", cstate->filename), errdetail_internal("%s", wait_result_to_str(pclose_rc)))); + } } /* @@ -3033,7 +3051,7 @@ BeginCopyFrom(ParseState *pstate, oldcontext = MemoryContextSwitchTo(cstate->copycontext); /* Initialize state variables */ - cstate->fe_eof = false; + cstate->reached_eof = false; cstate->eol_type = EOL_UNKNOWN; cstate->cur_relname = RelationGetRelationName(cstate->rel); cstate->cur_lineno = 0; diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index 0f8b1bb450..79a147466b 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -2438,11 +2438,16 @@ OpenTransientFilePerm(const char *fileName, int fileFlags, mode_t fileMode) * Routines that want to initiate a pipe stream should use OpenPipeStream * rather than plain popen(). This lets fd.c deal with freeing FDs if * necessary. When done, call ClosePipeStream rather than pclose. + * + * This function also ensures that the popen'd program is run with default + * SIGPIPE processing, rather than the SIG_IGN setting the backend normally + * uses. This ensures desirable response to, eg, closing a read pipe early. */ FILE * OpenPipeStream(const char *command, const char *mode) { FILE *file; + int save_errno; DO_DB(elog(LOG, "OpenPipeStream: Allocated %d (%s)", numAllocatedDescs, command)); @@ -2460,8 +2465,13 @@ OpenPipeStream(const char *command, const char *mode) TryAgain: fflush(stdout); fflush(stderr); + pqsignal(SIGPIPE, SIG_DFL); errno = 0; - if ((file = popen(command, mode)) != NULL) + file = popen(command, mode); + save_errno = errno; + pqsignal(SIGPIPE, SIG_IGN); + errno = save_errno; + if (file != NULL) { AllocateDesc *desc = &allocatedDescs[numAllocatedDescs]; @@ -2474,12 +2484,9 @@ TryAgain: if (errno == EMFILE || errno == ENFILE) { - int save_errno = errno; - ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("out of file descriptors: %m; release and retry"))); - errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno;