Add BufFileRead variants with short read and EOF detection
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 16 Jan 2023 08:20:44 +0000 (09:20 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 16 Jan 2023 10:01:31 +0000 (11:01 +0100)
Most callers of BufFileRead() want to check whether they read the full
specified length.  Checking this at every call site is very tedious.
This patch provides additional variants BufFileReadExact() and
BufFileReadMaybeEOF() that include the length checks.

I considered changing BufFileRead() itself, but this function is also
used in extensions, and so changing the behavior like this would
create a lot of problems there.  The new names are analogous to the
existing LogicalTapeReadExact().

Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/f3501945-c591-8cc3-5ef0-b72a2e0eaa9c@enterprisedb.com

src/backend/access/gist/gistbuildbuffers.c
src/backend/backup/backup_manifest.c
src/backend/executor/nodeHashjoin.c
src/backend/replication/logical/worker.c
src/backend/storage/file/buffile.c
src/backend/utils/sort/logtape.c
src/backend/utils/sort/sharedtuplestore.c
src/backend/utils/sort/tuplestore.c
src/include/storage/buffile.h

index 1a732dd5cf167f12d9f81eb5944a1b3a2a0a45cd..3399a6ae6850ef6159f0166029dcc6aac405bdef 100644 (file)
@@ -753,14 +753,9 @@ 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 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);
+   BufFileReadExact(file, ptr, BLCKSZ);
 }
 
 static void
index d325ef047aef0c847040ac2dbd0aff2719221545..fabd2ca2992e677c540a2f6b4c9e0f35dec5ce89 100644 (file)
@@ -362,17 +362,10 @@ SendBackupManifest(backup_manifest_info *manifest, bbsink *sink)
    while (manifest_bytes_done < manifest->manifest_size)
    {
        size_t      bytes_to_read;
-       size_t      rc;
 
        bytes_to_read = Min(sink->bbs_buffer_length,
                            manifest->manifest_size - manifest_bytes_done);
-       rc = BufFileRead(manifest->buffile, sink->bbs_buffer,
-                        bytes_to_read);
-       if (rc != bytes_to_read)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from temporary file: read only %zu of %zu bytes",
-                           rc, bytes_to_read)));
+       BufFileReadExact(manifest->buffile, sink->bbs_buffer, bytes_to_read);
        bbsink_manifest_contents(sink, bytes_to_read);
        manifest_bytes_done += bytes_to_read;
    }
index 9dbbd7f8c38260ccb12aec3bbbcafe4f8cf76dae..b215e3f59a5aaa24610fe4ba66a18dd9419f274a 100644 (file)
@@ -1260,28 +1260,18 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
     * we can read them both in one BufFileRead() call without any type
     * cheating.
     */
-   nread = BufFileRead(file, header, sizeof(header));
+   nread = BufFileReadMaybeEOF(file, header, sizeof(header), true);
    if (nread == 0)             /* end of file */
    {
        ExecClearTuple(tupleSlot);
        return NULL;
    }
-   if (nread != sizeof(header))
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                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];
-   nread = BufFileRead(file,
-                       ((char *) tuple + sizeof(uint32)),
-                       header[1] - sizeof(uint32));
-   if (nread != header[1] - sizeof(uint32))
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from hash-join temporary file: read only %zu of %zu bytes",
-                       nread, header[1] - sizeof(uint32))));
+   BufFileReadExact(file,
+                    (char *) tuple + sizeof(uint32),
+                    header[1] - sizeof(uint32));
    ExecForceStoreMinimalTuple(tuple, tupleSlot, true);
    return tupleSlot;
 }
index f3856c984337606440ce49979bfba614af633d66..d8b8a374c6293618ccddd3a2f42c968ac5301eaf 100644 (file)
@@ -2069,19 +2069,13 @@ apply_spooled_messages(FileSet *stream_fileset, TransactionId xid,
        CHECK_FOR_INTERRUPTS();
 
        /* read length of the on-disk record */
-       nbytes = BufFileRead(stream_fd, &len, sizeof(len));
+       nbytes = BufFileReadMaybeEOF(stream_fd, &len, sizeof(len), true);
 
        /* have we reached end of the file? */
        if (nbytes == 0)
            break;
 
        /* do we have a correct length? */
-       if (nbytes != sizeof(len))
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from streaming transaction's changes file \"%s\": read only %zu of %zu bytes",
-                           path, nbytes, sizeof(len))));
-
        if (len <= 0)
            elog(ERROR, "incorrect length %d in streaming transaction's changes file \"%s\"",
                 len, path);
@@ -2090,12 +2084,7 @@ apply_spooled_messages(FileSet *stream_fileset, TransactionId xid,
        buffer = repalloc(buffer, len);
 
        /* and finally read the data into the buffer */
-       nbytes = BufFileRead(stream_fd, buffer, len);
-       if (nbytes != len)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from streaming transaction's changes file \"%s\": read only %zu of %zu bytes",
-                           path, nbytes, (size_t) len)));
+       BufFileReadExact(stream_fd, buffer, len);
 
        BufFileTell(stream_fd, &fileno, &offset);
 
@@ -3993,7 +3982,6 @@ static void
 subxact_info_read(Oid subid, TransactionId xid)
 {
    char        path[MAXPGPATH];
-   size_t      nread;
    Size        len;
    BufFile    *fd;
    MemoryContext oldctx;
@@ -4013,12 +4001,7 @@ subxact_info_read(Oid subid, TransactionId xid)
        return;
 
    /* read number of subxact items */
-   nread = BufFileRead(fd, &subxact_data.nsubxacts, sizeof(subxact_data.nsubxacts));
-   if (nread != sizeof(subxact_data.nsubxacts))
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from streaming transaction's subxact file \"%s\": read only %zu of %zu bytes",
-                       path, nread, sizeof(subxact_data.nsubxacts))));
+   BufFileReadExact(fd, &subxact_data.nsubxacts, sizeof(subxact_data.nsubxacts));
 
    len = sizeof(SubXactInfo) * subxact_data.nsubxacts;
 
@@ -4037,14 +4020,7 @@ subxact_info_read(Oid subid, TransactionId xid)
    MemoryContextSwitchTo(oldctx);
 
    if (len > 0)
-   {
-       nread = BufFileRead(fd, subxact_data.subxacts, len);
-       if (nread != len)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from streaming transaction's subxact file \"%s\": read only %zu of %zu bytes",
-                       path, nread, len)));
-   }
+       BufFileReadExact(fd, subxact_data.subxacts, len);
 
    BufFileClose(fd);
 }
index 4a5bd0e224d2ca167c7cb186970be1a49759fb7c..c5464b6aa628770c4f9f429380731b13c42bda4e 100644 (file)
@@ -573,14 +573,19 @@ BufFileDumpBuffer(BufFile *file)
 }
 
 /*
- * BufFileRead
+ * BufFileRead variants
  *
  * Like fread() except we assume 1-byte element size and report I/O errors via
  * ereport().
+ *
+ * If 'exact' is true, then an error is also raised if the number of bytes
+ * read is not exactly 'size' (no short reads).  If 'exact' and 'eofOK' are
+ * true, then reading zero bytes is ok.
  */
-size_t
-BufFileRead(BufFile *file, void *ptr, size_t size)
+static size_t
+BufFileReadCommon(BufFile *file, void *ptr, size_t size, bool exact, bool eofOK)
 {
+   size_t      start_size = size;
    size_t      nread = 0;
    size_t      nthistime;
 
@@ -612,9 +617,48 @@ BufFileRead(BufFile *file, void *ptr, size_t size)
        nread += nthistime;
    }
 
+   if (exact &&
+       (nread != start_size && !(nread == 0 && eofOK)))
+       ereport(ERROR,
+               errcode_for_file_access(),
+               file->name ?
+               errmsg("could not read from file set \"%s\": read only %zu of %zu bytes",
+                      file->name, nread, start_size) :
+               errmsg("could not read from temporary file: read only %zu of %zu bytes",
+                      nread, start_size));
+
    return nread;
 }
 
+/*
+ * Legacy interface where the caller needs to check for end of file or short
+ * reads.
+ */
+size_t
+BufFileRead(BufFile *file, void *ptr, size_t size)
+{
+   return BufFileReadCommon(file, ptr, size, false, false);
+}
+
+/*
+ * Require read of exactly the specified size.
+ */
+void
+BufFileReadExact(BufFile *file, void *ptr, size_t size)
+{
+   BufFileReadCommon(file, ptr, size, true, false);
+}
+
+/*
+ * Require read of exactly the specified size, but optionally allow end of
+ * file (in which case 0 is returned).
+ */
+size_t
+BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
+{
+   return BufFileReadCommon(file, ptr, size, true, eofOK);
+}
+
 /*
  * BufFileWrite
  *
index 907dd8f0a7843cb76606fa7c36c5f6ac5feb4b9d..56ac0298c55787051cf4863dbc0f77800623fb44 100644 (file)
@@ -281,19 +281,12 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, const void *buffer)
 static void
 ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
 {
-   size_t      nread;
-
    if (BufFileSeekBlock(lts->pfile, blocknum) != 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 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)));
+   BufFileReadExact(lts->pfile, buffer, BLCKSZ);
 }
 
 /*
index 65bbe0a52d1ea87f1796e00157b47c64e487eb1a..e3da83f10be220cc37b42907c8c563802bae9a43 100644 (file)
@@ -422,23 +422,10 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
     */
    if (accessor->sts->meta_data_size > 0)
    {
-       if (BufFileRead(accessor->read_file,
-                       meta_data,
-                       accessor->sts->meta_data_size) !=
-           accessor->sts->meta_data_size)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from shared tuplestore temporary file"),
-                    errdetail_internal("Short read while reading meta-data.")));
+       BufFileReadExact(accessor->read_file, meta_data, accessor->sts->meta_data_size);
        accessor->read_bytes += accessor->sts->meta_data_size;
    }
-   if (BufFileRead(accessor->read_file,
-                   &size,
-                   sizeof(size)) != sizeof(size))
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from shared tuplestore temporary file"),
-                errdetail_internal("Short read while reading size.")));
+   BufFileReadExact(accessor->read_file, &size, sizeof(size));
    accessor->read_bytes += sizeof(size);
    if (size > accessor->read_buffer_size)
    {
@@ -455,13 +442,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
    this_chunk_size = Min(remaining_size,
                          BLCKSZ * STS_CHUNK_PAGES - accessor->read_bytes);
    destination = accessor->read_buffer + sizeof(uint32);
-   if (BufFileRead(accessor->read_file,
-                   destination,
-                   this_chunk_size) != this_chunk_size)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from shared tuplestore temporary file"),
-                errdetail_internal("Short read while reading tuple.")));
+   BufFileReadExact(accessor->read_file, destination, this_chunk_size);
    accessor->read_bytes += this_chunk_size;
    remaining_size -= this_chunk_size;
    destination += this_chunk_size;
@@ -473,12 +454,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
        /* We are now positioned at the start of an overflow chunk. */
        SharedTuplestoreChunk chunk_header;
 
-       if (BufFileRead(accessor->read_file, &chunk_header, STS_CHUNK_HEADER_SIZE) !=
-           STS_CHUNK_HEADER_SIZE)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from shared tuplestore temporary file"),
-                    errdetail_internal("Short read while reading overflow chunk header.")));
+       BufFileReadExact(accessor->read_file, &chunk_header, STS_CHUNK_HEADER_SIZE);
        accessor->read_bytes = STS_CHUNK_HEADER_SIZE;
        if (chunk_header.overflow == 0)
            ereport(ERROR,
@@ -489,13 +465,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
        this_chunk_size = Min(remaining_size,
                              BLCKSZ * STS_CHUNK_PAGES -
                              STS_CHUNK_HEADER_SIZE);
-       if (BufFileRead(accessor->read_file,
-                       destination,
-                       this_chunk_size) != this_chunk_size)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from shared tuplestore temporary file"),
-                    errdetail_internal("Short read while reading tuple.")));
+       BufFileReadExact(accessor->read_file, destination, this_chunk_size);
        accessor->read_bytes += this_chunk_size;
        remaining_size -= this_chunk_size;
        destination += this_chunk_size;
@@ -551,7 +521,6 @@ sts_parallel_scan_next(SharedTuplestoreAccessor *accessor, void *meta_data)
        if (!eof)
        {
            SharedTuplestoreChunk chunk_header;
-           size_t      nread;
 
            /* Make sure we have the file open. */
            if (accessor->read_file == NULL)
@@ -570,13 +539,7 @@ sts_parallel_scan_next(SharedTuplestoreAccessor *accessor, void *meta_data)
                        (errcode_for_file_access(),
                         errmsg("could not seek to block %u in shared tuplestore temporary file",
                                read_page)));
-           nread = BufFileRead(accessor->read_file, &chunk_header,
-                               STS_CHUNK_HEADER_SIZE);
-           if (nread != STS_CHUNK_HEADER_SIZE)
-               ereport(ERROR,
-                       (errcode_for_file_access(),
-                        errmsg("could not read from shared tuplestore temporary file: read only %zu of %zu bytes",
-                               nread, STS_CHUNK_HEADER_SIZE)));
+           BufFileReadExact(accessor->read_file, &chunk_header, STS_CHUNK_HEADER_SIZE);
 
            /*
             * If this is an overflow chunk, we skip it and any following
index 44f1d9e85563725427ab1625ff21c1fd23fcd8ca..bc36662198a51a904c97ba359669c85a5b1908c0 100644 (file)
@@ -1468,15 +1468,11 @@ getlen(Tuplestorestate *state, bool eofOK)
    unsigned int len;
    size_t      nbytes;
 
-   nbytes = BufFileRead(state->myfile, &len, sizeof(len));
-   if (nbytes == sizeof(len))
+   nbytes = BufFileReadMaybeEOF(state->myfile, &len, sizeof(len), eofOK);
+   if (nbytes == 0)
+       return 0;
+   else
        return len;
-   if (nbytes != 0 || !eofOK)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes",
-                       nbytes, sizeof(len))));
-   return 0;
 }
 
 
@@ -1528,25 +1524,12 @@ 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;
-   nread = BufFileRead(state->myfile, tupbody, tupbodylen);
-   if (nread != (size_t) tupbodylen)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes",
-                       nread, (size_t) tupbodylen)));
+   BufFileReadExact(state->myfile, tupbody, tupbodylen);
    if (state->backward)        /* need trailing length word? */
-   {
-       nread = BufFileRead(state->myfile, &tuplen, sizeof(tuplen));
-       if (nread != sizeof(tuplen))
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from tuplestore temporary file: read only %zu of %zu bytes",
-                           nread, sizeof(tuplen))));
-   }
+       BufFileReadExact(state->myfile, &tuplen, sizeof(tuplen));
    return (void *) tuple;
 }
index 7ed5b03516fa0dad744bd228c63529304db55c64..658376671933c7295d0814a717d5b6a0440d85ba 100644 (file)
@@ -38,7 +38,9 @@ typedef struct BufFile BufFile;
 
 extern BufFile *BufFileCreateTemp(bool interXact);
 extern void BufFileClose(BufFile *file);
-extern size_t BufFileRead(BufFile *file, void *ptr, size_t size);
+extern pg_nodiscard size_t BufFileRead(BufFile *file, void *ptr, size_t size);
+extern void BufFileReadExact(BufFile *file, void *ptr, size_t size);
+extern size_t BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK);
 extern void BufFileWrite(BufFile *file, const void *ptr, size_t size);
 extern int BufFileSeek(BufFile *file, int fileno, off_t offset, int whence);
 extern void BufFileTell(BufFile *file, int *fileno, off_t *offset);