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

Commit8285fae

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 parent97b398e commit8285fae

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
@@ -99,7 +99,9 @@ typedef struct CopyStateData
9999
FILE*copy_file;/* used if copy_dest == COPY_FILE */
100100
StringInfofe_msgbuf;/* used for all dests during COPY TO, only for
101101
* dest == COPY_NEW_FE in COPY FROM */
102-
boolfe_eof;/* true if detected end of copy data */
102+
boolis_copy_from;/* COPY TO, or COPY FROM? */
103+
boolreached_eof;/* true if we read to end of copy data (not
104+
* all copy_dest types maintain this) */
103105
EolTypeeol_type;/* EOL type of input */
104106
intfile_encoding;/* file or remote side's character encoding */
105107
boolneed_transcoding;/* file encoding diff from server? */
@@ -567,6 +569,8 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
567569
ereport(ERROR,
568570
(errcode_for_file_access(),
569571
errmsg("could not read from COPY file: %m")));
572+
if (bytesread==0)
573+
cstate->reached_eof= true;
570574
break;
571575
caseCOPY_OLD_FE:
572576

@@ -587,7 +591,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
587591
bytesread=minread;
588592
break;
589593
caseCOPY_NEW_FE:
590-
while (maxread>0&&bytesread<minread&& !cstate->fe_eof)
594+
while (maxread>0&&bytesread<minread&& !cstate->reached_eof)
591595
{
592596
intavail;
593597

@@ -615,7 +619,7 @@ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
615619
break;
616620
case'c':/* CopyDone */
617621
/* COPY IN correctly terminated by frontend */
618-
cstate->fe_eof= true;
622+
cstate->reached_eof= true;
619623
returnbytesread;
620624
case'f':/* CopyFail */
621625
ereport(ERROR,
@@ -1025,6 +1029,8 @@ ProcessCopyOptions(ParseState *pstate,
10251029
if (cstate==NULL)
10261030
cstate= (CopyStateData*)palloc0(sizeof(CopyStateData));
10271031

1032+
cstate->is_copy_from=is_from;
1033+
10281034
cstate->file_encoding=-1;
10291035

10301036
/* Extract options from the statement node tree */
@@ -1698,11 +1704,23 @@ ClosePipeToProgram(CopyState cstate)
16981704
(errcode_for_file_access(),
16991705
errmsg("could not close pipe to external command: %m")));
17001706
elseif (pclose_rc!=0)
1707+
{
1708+
/*
1709+
* If we ended a COPY FROM PROGRAM before reaching EOF, then it's
1710+
* expectable for the called program to fail with SIGPIPE, and we
1711+
* should not report that as an error. Otherwise, SIGPIPE indicates a
1712+
* problem.
1713+
*/
1714+
if (cstate->is_copy_from&& !cstate->reached_eof&&
1715+
WIFSIGNALED(pclose_rc)&&WTERMSIG(pclose_rc)==SIGPIPE)
1716+
return;
1717+
17011718
ereport(ERROR,
17021719
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
17031720
errmsg("program \"%s\" failed",
17041721
cstate->filename),
17051722
errdetail_internal("%s",wait_result_to_str(pclose_rc))));
1723+
}
17061724
}
17071725

17081726
/*
@@ -3038,7 +3056,7 @@ BeginCopyFrom(ParseState *pstate,
30383056
oldcontext=MemoryContextSwitchTo(cstate->copycontext);
30393057

30403058
/* Initialize state variables */
3041-
cstate->fe_eof= false;
3059+
cstate->reached_eof= false;
30423060
cstate->eol_type=EOL_UNKNOWN;
30433061
cstate->cur_relname=RelationGetRelationName(cstate->rel);
30443062
cstate->cur_lineno=0;

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,11 +2192,16 @@ OpenTransientFile(FileName fileName, int fileFlags, int fileMode)
21922192
* Routines that want to initiate a pipe stream should use OpenPipeStream
21932193
* rather than plain popen(). This lets fd.c deal with freeing FDs if
21942194
* necessary. When done, call ClosePipeStream rather than pclose.
2195+
*
2196+
* This function also ensures that the popen'd program is run with default
2197+
* SIGPIPE processing, rather than the SIG_IGN setting the backend normally
2198+
* uses. This ensures desirable response to, eg, closing a read pipe early.
21952199
*/
21962200
FILE*
21972201
OpenPipeStream(constchar*command,constchar*mode)
21982202
{
21992203
FILE*file;
2204+
intsave_errno;
22002205

22012206
DO_DB(elog(LOG,"OpenPipeStream: Allocated %d (%s)",
22022207
numAllocatedDescs,command));
@@ -2214,8 +2219,13 @@ OpenPipeStream(const char *command, const char *mode)
22142219
TryAgain:
22152220
fflush(stdout);
22162221
fflush(stderr);
2222+
pqsignal(SIGPIPE,SIG_DFL);
22172223
errno=0;
2218-
if ((file=popen(command,mode))!=NULL)
2224+
file=popen(command,mode);
2225+
save_errno=errno;
2226+
pqsignal(SIGPIPE,SIG_IGN);
2227+
errno=save_errno;
2228+
if (file!=NULL)
22192229
{
22202230
AllocateDesc*desc=&allocatedDescs[numAllocatedDescs];
22212231

@@ -2228,12 +2238,9 @@ OpenPipeStream(const char *command, const char *mode)
22282238

22292239
if (errno==EMFILE||errno==ENFILE)
22302240
{
2231-
intsave_errno=errno;
2232-
22332241
ereport(LOG,
22342242
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
22352243
errmsg("out of file descriptors: %m; release and retry")));
2236-
errno=0;
22372244
if (ReleaseLruFile())
22382245
gotoTryAgain;
22392246
errno=save_errno;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp