Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitffa4cbd

Browse files
committed
Handle EPIPE more sanely when we close a pipe reading from a program.
Previously, any program launched by COPY TO/FROM PROGRAM inherited theserver's setting of SIGPIPE handling, i.e. SIG_IGN. Hence, if we weredoing COPY FROM PROGRAM and closed the pipe early, the child processwould see EPIPE on its output file and typically would treat that asa fatal error, in turn causing the COPY to report error. Similarly,one could get a failure report from a query that didn't read all ofthe output from a contrib/file_fdw foreign table that uses file_fdw'sPROGRAM option.To fix, ensure that child programs inherit SIG_DFL not SIG_IGN processingof SIGPIPE. This seems like an all-around better situation since ifthe called program wants some non-default treatment of SIGPIPE, it wouldexpect to have to set that up for itself. Then in COPY, if it's COPYFROM PROGRAM and we stop reading short of detecting EOF, treat a SIGPIPEexit from the called program as a non-error condition. This still allowsus to report an error for any case where the called program gets SIGPIPEon some other file descriptor.As coded, we won't report a SIGPIPE if we stop reading as a result ofseeing an in-band EOF marker (e.g. COPY BINARY EOF marker). It'ssomewhat debatable whether we should complain if the called programcontinues to transmit data after an EOF marker. However, it seems likewe should avoid throwing error in any questionable cases, especially in aback-patched fix, and anyway it would take additional code to make suchan error get reported consistently.Back-patch to v10. We could go further back, since COPY FROM PROGRAMhas been around awhile, but AFAICS the only way to reach this situationusing core or contrib is via file_fdw, which has only supported PROGRAMsources since v10. The COPY statement per se has no feature wherebyit'd stop reading without having hit EOF or an error already. Therefore,I don't see any upside to back-patching further that'd outweigh therisk of complaints about behavioral change.Per bug #15449 from Eric Cyr.Patch by me, review by Etsuro Fujita and Kyotaro HoriguchiDiscussion:https://postgr.es/m/15449-1cf737dd5929450e@postgresql.org
1 parentd56e0fd commitffa4cbd

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

‎src/backend/commands/copy.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ typedef struct CopyStateData
114114
FILE*copy_file;/* used if copy_dest == COPY_FILE */
115115
StringInfofe_msgbuf;/* used for all dests during COPY TO, only for
116116
* dest == COPY_NEW_FE in COPY FROM */
117-
boolfe_eof;/* true if detected end of copy data */
117+
boolis_copy_from;/* COPY TO, or COPY FROM? */
118+
boolreached_eof;/* true if we read to end of copy data (not
119+
* all copy_dest types maintain this) */
118120
EolTypeeol_type;/* EOL type of input */
119121
intfile_encoding;/* file or remote side's character encoding */
120122
boolneed_transcoding;/* file encoding diff from server? */
@@ -575,6 +577,8 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
575577
ereport(ERROR,
576578
(errcode_for_file_access(),
577579
errmsg("could not read from COPY file: %m")));
580+
if (bytesread==0)
581+
cstate->reached_eof= true;
578582
break;
579583
caseCOPY_OLD_FE:
580584

@@ -595,7 +599,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
595599
bytesread=minread;
596600
break;
597601
caseCOPY_NEW_FE:
598-
while (maxread>0&&bytesread<minread&& !cstate->fe_eof)
602+
while (maxread>0&&bytesread<minread&& !cstate->reached_eof)
599603
{
600604
intavail;
601605

@@ -623,7 +627,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
623627
break;
624628
case'c':/* CopyDone */
625629
/* COPY IN correctly terminated by frontend */
626-
cstate->fe_eof= true;
630+
cstate->reached_eof= true;
627631
returnbytesread;
628632
case'f':/* CopyFail */
629633
ereport(ERROR,
@@ -1050,6 +1054,8 @@ ProcessCopyOptions(ParseState *pstate,
10501054
if (cstate==NULL)
10511055
cstate= (CopyStateData*)palloc0(sizeof(CopyStateData));
10521056

1057+
cstate->is_copy_from=is_from;
1058+
10531059
cstate->file_encoding=-1;
10541060

10551061
/* Extract options from the statement node tree */
@@ -1727,11 +1733,23 @@ ClosePipeToProgram(CopyState cstate)
17271733
(errcode_for_file_access(),
17281734
errmsg("could not close pipe to external command: %m")));
17291735
elseif (pclose_rc!=0)
1736+
{
1737+
/*
1738+
* If we ended a COPY FROM PROGRAM before reaching EOF, then it's
1739+
* expectable for the called program to fail with SIGPIPE, and we
1740+
* should not report that as an error. Otherwise, SIGPIPE indicates a
1741+
* problem.
1742+
*/
1743+
if (cstate->is_copy_from&& !cstate->reached_eof&&
1744+
WIFSIGNALED(pclose_rc)&&WTERMSIG(pclose_rc)==SIGPIPE)
1745+
return;
1746+
17301747
ereport(ERROR,
17311748
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
17321749
errmsg("program \"%s\" failed",
17331750
cstate->filename),
17341751
errdetail_internal("%s",wait_result_to_str(pclose_rc))));
1752+
}
17351753
}
17361754

17371755
/*
@@ -3194,7 +3212,7 @@ BeginCopyFrom(ParseState *pstate,
31943212
oldcontext=MemoryContextSwitchTo(cstate->copycontext);
31953213

31963214
/* Initialize state variables */
3197-
cstate->fe_eof= false;
3215+
cstate->reached_eof= false;
31983216
cstate->eol_type=EOL_UNKNOWN;
31993217
cstate->cur_relname=RelationGetRelationName(cstate->rel);
32003218
cstate->cur_lineno=0;

‎src/backend/storage/file/fd.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,11 +2279,16 @@ OpenTransientFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
22792279
* Routines that want to initiate a pipe stream should use OpenPipeStream
22802280
* rather than plain popen(). This lets fd.c deal with freeing FDs if
22812281
* necessary. When done, call ClosePipeStream rather than pclose.
2282+
*
2283+
* This function also ensures that the popen'd program is run with default
2284+
* SIGPIPE processing, rather than the SIG_IGN setting the backend normally
2285+
* uses. This ensures desirable response to, eg, closing a read pipe early.
22822286
*/
22832287
FILE*
22842288
OpenPipeStream(constchar*command,constchar*mode)
22852289
{
22862290
FILE*file;
2291+
intsave_errno;
22872292

22882293
DO_DB(elog(LOG,"OpenPipeStream: Allocated %d (%s)",
22892294
numAllocatedDescs,command));
@@ -2301,8 +2306,13 @@ OpenPipeStream(const char *command, const char *mode)
23012306
TryAgain:
23022307
fflush(stdout);
23032308
fflush(stderr);
2309+
pqsignal(SIGPIPE,SIG_DFL);
23042310
errno=0;
2305-
if ((file=popen(command,mode))!=NULL)
2311+
file=popen(command,mode);
2312+
save_errno=errno;
2313+
pqsignal(SIGPIPE,SIG_IGN);
2314+
errno=save_errno;
2315+
if (file!=NULL)
23062316
{
23072317
AllocateDesc*desc=&allocatedDescs[numAllocatedDescs];
23082318

@@ -2315,12 +2325,9 @@ OpenPipeStream(const char *command, const char *mode)
23152325

23162326
if (errno==EMFILE||errno==ENFILE)
23172327
{
2318-
intsave_errno=errno;
2319-
23202328
ereport(LOG,
23212329
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
23222330
errmsg("out of file descriptors: %m; release and retry")));
2323-
errno=0;
23242331
if (ReleaseLruFile())
23252332
gotoTryAgain;
23262333
errno=save_errno;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp