From 02b71f06be751ff9809f48742f15e1905a1a6d83 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Tue, 16 Jun 2020 13:50:56 +1200 Subject: [PATCH] Fix buffile.c error handling. Convert buffile.c error handling to use ereport. This fixes cases where I/O errors were indistinguishable from EOF or not reported. Also remove "%m" from error messages where errno would be bogus. While we're modifying those strings, add block numbers and short read byte counts where appropriate. Back-patch to all supported releases. Reported-by: Amit Khandekar Reviewed-by: Melanie Plageman Reviewed-by: Alvaro Herrera Reviewed-by: Robert Haas Reviewed-by: Ibrar Ahmed Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CA%2BhUKGJE04G%3D8TLK0DLypT_27D9dR8F1RQgNp0jK6qR0tZGWOw%40mail.gmail.com --- src/backend/access/gist/gistbuildbuffers.c | 24 ++++----- src/backend/executor/nodeHashjoin.c | 24 +++------ src/backend/storage/file/buffile.c | 57 ++++++++++++---------- src/backend/utils/sort/logtape.c | 19 +++++--- src/backend/utils/sort/tuplestore.c | 56 ++++++++++----------- 5 files changed, 86 insertions(+), 94 deletions(-) diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c index 3a35096ec33..14d827d8a57 100644 --- a/src/backend/access/gist/gistbuildbuffers.c +++ b/src/backend/access/gist/gistbuildbuffers.c @@ -757,26 +757,20 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, static void ReadTempFileBlock(BufFile *file, long blknum, void *ptr) { + size_t nread; + if (BufFileSeekBlock(file, blknum) != 0) - elog(ERROR, "could not seek temporary file: %m"); - if (BufFileRead(file, ptr, BLCKSZ) != BLCKSZ) - elog(ERROR, "could not read temporary file: %m"); + elog(ERROR, "could not seek to block %ld in temporary file", blknum); + nread = BufFileRead(file, ptr, BLCKSZ); + if (nread != BLCKSZ) + elog(ERROR, "could not read temporary file: read only %zu of %zu bytes", + nread, (size_t) BLCKSZ); } static void WriteTempFileBlock(BufFile *file, long blknum, void *ptr) { if (BufFileSeekBlock(file, blknum) != 0) - elog(ERROR, "could not seek temporary file: %m"); - if (BufFileWrite(file, ptr, BLCKSZ) != BLCKSZ) - { - /* - * the other errors in Read/WriteTempFileBlock shouldn't happen, but - * an error at write can easily happen if you run out of disk space. - */ - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write block %ld of temporary file: %m", - blknum))); - } + elog(ERROR, "could not seek to block %ld in temporary file", blknum); + BufFileWrite(file, ptr, BLCKSZ); } diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index d13efc4d989..6e7cbc34ca6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -820,7 +820,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate) if (BufFileSeek(innerFile, 0, 0L, SEEK_SET)) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not rewind hash-join temporary file: %m"))); + errmsg("could not rewind hash-join temporary file"))); while ((slot = ExecHashJoinGetSavedTuple(hjstate, innerFile, @@ -850,7 +850,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate) if (BufFileSeek(hashtable->outerBatchFile[curbatch], 0, 0L, SEEK_SET)) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not rewind hash-join temporary file: %m"))); + errmsg("could not rewind hash-join temporary file"))); } return true; @@ -872,7 +872,6 @@ ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue, BufFile **fileptr) { BufFile *file = *fileptr; - size_t written; if (file == NULL) { @@ -881,17 +880,8 @@ ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue, *fileptr = file; } - written = BufFileWrite(file, (void *) &hashvalue, sizeof(uint32)); - if (written != sizeof(uint32)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write to hash-join temporary file: %m"))); - - written = BufFileWrite(file, (void *) tuple, tuple->t_len); - if (written != tuple->t_len) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write to hash-join temporary file: %m"))); + BufFileWrite(file, (void *) &hashvalue, sizeof(uint32)); + BufFileWrite(file, (void *) tuple, tuple->t_len); } /* @@ -932,7 +922,8 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate, if (nread != sizeof(header)) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from hash-join temporary file: %m"))); + errmsg("could not read from hash-join temporary file: read only %zu of %zu bytes", + nread, sizeof(header)))); *hashvalue = header[0]; tuple = (MinimalTuple) palloc(header[1]); tuple->t_len = header[1]; @@ -942,7 +933,8 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate, if (nread != header[1] - sizeof(uint32)) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from hash-join temporary file: %m"))); + errmsg("could not read from hash-join temporary file: read only %zu of %zu bytes", + nread, header[1] - sizeof(uint32)))); return ExecStoreMinimalTuple(tuple, tupleSlot, true); } diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c index 5d95d5c45d7..73d7d1cfd35 100644 --- a/src/backend/storage/file/buffile.c +++ b/src/backend/storage/file/buffile.c @@ -93,8 +93,7 @@ static BufFile *makeBufFile(File firstfile); static void extendBufFile(BufFile *file); static void BufFileLoadBuffer(BufFile *file); static void BufFileDumpBuffer(BufFile *file); -static int BufFileFlush(BufFile *file); - +static void BufFileFlush(BufFile *file); /* * Create a BufFile given the first underlying physical file. @@ -247,7 +246,10 @@ BufFileLoadBuffer(BufFile *file) if (file->curOffset != file->offsets[file->curFile]) { if (FileSeek(thisfile, file->curOffset, SEEK_SET) != file->curOffset) - return; /* seek failed, read nothing */ + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not seek in file \"%s\": %m", + FilePathName(thisfile)))); file->offsets[file->curFile] = file->curOffset; } @@ -256,7 +258,14 @@ BufFileLoadBuffer(BufFile *file) */ file->nbytes = FileRead(thisfile, file->buffer.data, sizeof(file->buffer)); if (file->nbytes < 0) + { file->nbytes = 0; + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", + FilePathName(thisfile)))); + } + file->offsets[file->curFile] += file->nbytes; /* we choose not to advance curOffset here */ @@ -314,12 +323,19 @@ BufFileDumpBuffer(BufFile *file) if (file->curOffset != file->offsets[file->curFile]) { if (FileSeek(thisfile, file->curOffset, SEEK_SET) != file->curOffset) - return; /* seek failed, give up */ + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not seek in file \"%s\": %m", + FilePathName(thisfile)))); file->offsets[file->curFile] = file->curOffset; } bytestowrite = FileWrite(thisfile, file->buffer.data + wpos, bytestowrite); if (bytestowrite <= 0) - return; /* failed to write */ + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write to file \"%s\" : %m", + FilePathName(thisfile)))); + file->offsets[file->curFile] += bytestowrite; file->curOffset += bytestowrite; wpos += bytestowrite; @@ -352,7 +368,8 @@ BufFileDumpBuffer(BufFile *file) /* * BufFileRead * - * Like fread() except we assume 1-byte element size. + * Like fread() except we assume 1-byte element size and report I/O errors via + * ereport(). */ size_t BufFileRead(BufFile *file, void *ptr, size_t size) @@ -360,12 +377,7 @@ BufFileRead(BufFile *file, void *ptr, size_t size) size_t nread = 0; size_t nthistime; - if (file->dirty) - { - if (BufFileFlush(file) != 0) - return 0; /* could not flush... */ - Assert(!file->dirty); - } + BufFileFlush(file); while (size > 0) { @@ -399,7 +411,8 @@ BufFileRead(BufFile *file, void *ptr, size_t size) /* * BufFileWrite * - * Like fwrite() except we assume 1-byte element size. + * Like fwrite() except we assume 1-byte element size and report errors via + * ereport(). */ size_t BufFileWrite(BufFile *file, void *ptr, size_t size) @@ -413,11 +426,7 @@ BufFileWrite(BufFile *file, void *ptr, size_t size) { /* Buffer full, dump it out */ if (file->dirty) - { BufFileDumpBuffer(file); - if (file->dirty) - break; /* I/O error */ - } else { /* Hmm, went directly from reading to writing? */ @@ -449,19 +458,15 @@ BufFileWrite(BufFile *file, void *ptr, size_t size) /* * BufFileFlush * - * Like fflush() + * Like fflush(), except that I/O errors are reported with ereport(). */ -static int +static void BufFileFlush(BufFile *file) { if (file->dirty) - { BufFileDumpBuffer(file); - if (file->dirty) - return EOF; - } - return 0; + Assert(!file->dirty); } /* @@ -470,6 +475,7 @@ BufFileFlush(BufFile *file) * Like fseek(), except that target position needs two values in order to * work when logical filesize exceeds maximum value representable by long. * We do not support relative seeks across more than LONG_MAX, however. + * I/O errors are reported by ereport(). * * Result is 0 if OK, EOF if not. Logical position is not moved if an * impossible seek is attempted. @@ -527,8 +533,7 @@ BufFileSeek(BufFile *file, int fileno, off_t offset, int whence) return 0; } /* Otherwise, must reposition buffer, so flush any dirty data */ - if (BufFileFlush(file) != 0) - return EOF; + BufFileFlush(file); /* * At this point and no sooner, check for seek past last segment. The diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c index 774520752f0..7396b15f5ac 100644 --- a/src/backend/utils/sort/logtape.c +++ b/src/backend/utils/sort/logtape.c @@ -202,12 +202,12 @@ static void ltsDumpBuffer(LogicalTapeSet *lts, LogicalTape *lt); static void ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer) { - if (BufFileSeekBlock(lts->pfile, blocknum) != 0 || - BufFileWrite(lts->pfile, buffer, BLCKSZ) != BLCKSZ) + if (BufFileSeekBlock(lts->pfile, blocknum) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not write block %ld of temporary file: %m", + errmsg("could not seek to block %ld of temporary file", blocknum))); + BufFileWrite(lts->pfile, buffer, BLCKSZ); } /* @@ -219,12 +219,19 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer) static void ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer) { - if (BufFileSeekBlock(lts->pfile, blocknum) != 0 || - BufFileRead(lts->pfile, buffer, BLCKSZ) != BLCKSZ) + size_t nread; + + if (BufFileSeekBlock(lts->pfile, blocknum) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read block %ld of temporary file: %m", + errmsg("could not seek to block %ld of temporary file", blocknum))); + nread = BufFileRead(lts->pfile, buffer, BLCKSZ); + if (nread != BLCKSZ) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read block %ld of temporary file: read only %zu of %zu bytes", + blocknum, nread, (size_t) BLCKSZ))); } /* diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index 1347fc4520a..c77d3f85fe3 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -512,7 +512,7 @@ tuplestore_select_read_pointer(Tuplestorestate *state, int ptr) SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); } else { @@ -522,7 +522,7 @@ tuplestore_select_read_pointer(Tuplestorestate *state, int ptr) SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); } break; default: @@ -849,7 +849,7 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple) SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); state->status = TSS_WRITEFILE; /* @@ -953,7 +953,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward, SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); state->status = TSS_READFILE; /* FALL THRU into READFILE case */ @@ -1017,7 +1017,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward, SEEK_CUR) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); Assert(!state->truncated); return NULL; } @@ -1034,7 +1034,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward, SEEK_CUR) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); tup = READTUP(state, tuplen); return tup; @@ -1236,7 +1236,7 @@ tuplestore_rescan(Tuplestorestate *state) if (BufFileSeek(state->myfile, 0, 0L, SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); break; default: elog(ERROR, "invalid tuplestore state"); @@ -1301,7 +1301,7 @@ tuplestore_copy_read_pointer(Tuplestorestate *state, SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); } else { @@ -1310,7 +1310,7 @@ tuplestore_copy_read_pointer(Tuplestorestate *state, SEEK_SET) != 0) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not seek in tuplestore temporary file: %m"))); + errmsg("could not seek in tuplestore temporary file"))); } } else if (srcptr == state->activeptr) @@ -1457,7 +1457,8 @@ getlen(Tuplestorestate *state, bool eofOK) if (nbytes != 0 || !eofOK) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from tuplestore temporary file: %m"))); + errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes", + nbytes, sizeof(len)))); return 0; } @@ -1494,22 +1495,10 @@ writetup_heap(Tuplestorestate *state, void *tup) /* total on-disk footprint: */ unsigned int tuplen = tupbodylen + sizeof(int); - if (BufFileWrite(state->myfile, (void *) &tuplen, - sizeof(tuplen)) != sizeof(tuplen)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write to tuplestore temporary file: %m"))); - if (BufFileWrite(state->myfile, (void *) tupbody, - tupbodylen) != (size_t) tupbodylen) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write to tuplestore temporary file: %m"))); + BufFileWrite(state->myfile, (void *) &tuplen, sizeof(tuplen)); + BufFileWrite(state->myfile, (void *) tupbody, tupbodylen); if (state->backward) /* need trailing length word? */ - if (BufFileWrite(state->myfile, (void *) &tuplen, - sizeof(tuplen)) != sizeof(tuplen)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write to tuplestore temporary file: %m"))); + BufFileWrite(state->myfile, (void *) &tuplen, sizeof(tuplen)); FREEMEM(state, GetMemoryChunkSpace(tuple)); heap_free_minimal_tuple(tuple); @@ -1522,20 +1511,25 @@ readtup_heap(Tuplestorestate *state, unsigned int len) unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET; MinimalTuple tuple = (MinimalTuple) palloc(tuplen); char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET; + size_t nread; USEMEM(state, GetMemoryChunkSpace(tuple)); /* read in the tuple proper */ tuple->t_len = tuplen; - if (BufFileRead(state->myfile, (void *) tupbody, - tupbodylen) != (size_t) tupbodylen) + nread = BufFileRead(state->myfile, (void *) tupbody, tupbodylen); + if (nread != (size_t) tupbodylen) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from tuplestore temporary file: %m"))); + errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes", + nread, (size_t) tupbodylen))); if (state->backward) /* need trailing length word? */ - if (BufFileRead(state->myfile, (void *) &tuplen, - sizeof(tuplen)) != sizeof(tuplen)) + { + nread = BufFileRead(state->myfile, (void *) &tuplen, sizeof(tuplen)); + if (nread != sizeof(tuplen)) ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from tuplestore temporary file: %m"))); + errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes", + nread, sizeof(tuplen)))); + } return (void *) tuple; }