diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index d34182a7b0..57f695ca51 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -106,8 +106,8 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, bool missing_ok) { bytea *buf; - size_t nbytes = 0; FILE *file; + size_t nbytes; /* clamp request size to what we can actually deliver */ if (bytes_to_read > (int64) (MaxAllocSize - VARHDRSZ)) @@ -126,69 +126,63 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, filename))); } - if (fseeko(file, (off_t) seek_offset, - (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0) + /* + * Virtual File? seek_offset must be zero, otherwise fseeko will fail. + * Avoid syscall fseeko if seek_offset is zero. + */ + if (seek_offset != 0 && + fseeko(file, (off_t) seek_offset, + (seek_offset > 0) ? SEEK_SET : SEEK_END) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not seek in file \"%s\": %m", filename))); if (bytes_to_read > 0) { - /* If passed explicit read size just do it */ + /* If passed explicit read size just do it */ buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ); - nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); + /* Bypass fread buffer: less copies. */ + setvbuf(file, VARDATA(buf), _IOFBF, (size_t) bytes_to_read); + + nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); } - else + else { - /* Negative read size, read rest of file */ StringInfoData sbuf; + ssize_t rbytes; + int fd; initStringInfo(&sbuf); + /* Leave room in the buffer for the varlena length word */ sbuf.len += VARHDRSZ; Assert(sbuf.len < sbuf.maxlen); - while (!(feof(file) || ferror(file))) - { - size_t rbytes; - - /* Minimum amount to read at a time */ + /* Minimum amount to read at a time */ #define MIN_READ_SIZE 4096 - /* - * If not at end of file, and sbuf.len is equal to - * MaxAllocSize - 1, then either the file is too large, or - * there is nothing left to read. Attempt to read one more - * byte to see if the end of file has been reached. If not, - * the file is too large; we'd rather give the error message - * for that ourselves. - */ - if (sbuf.len == MaxAllocSize - 1) - { - char rbuf[1]; - - if (fread(rbuf, 1, 1, file) != 0 || !feof(file)) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("file length too large"))); - else - break; - } - - /* OK, ensure that we can read at least MIN_READ_SIZE */ + fd = fileno(file); + nbytes = 0; + do { + /* OK, ensure that we can read at least MIN_READ_SIZE */ enlargeStringInfo(&sbuf, MIN_READ_SIZE); /* * stringinfo.c likes to allocate in powers of 2, so it's likely * that much more space is available than we asked for. Use all - * of it, rather than making more fread calls than necessary. + * of it, rather than making more read calls than necessary. */ - rbytes = fread(sbuf.data + sbuf.len, 1, - (size_t) (sbuf.maxlen - sbuf.len - 1), file); + rbytes = read(fd, sbuf.data + sbuf.len, (size_t) (sbuf.maxlen - sbuf.len - 1)); + sbuf.len += rbytes; nbytes += rbytes; - } + } while(rbytes > 0 && sbuf.len < (int64) (MaxAllocSize - VARHDRSZ)); + + if (sbuf.len > ((int64) MaxAllocSize - VARHDRSZ)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("file length too large"))); /* Now we can commandeer the stringinfo's buffer as the result */ buf = (bytea *) sbuf.data; @@ -199,10 +193,10 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", filename))); - SET_VARSIZE(buf, nbytes + VARHDRSZ); - FreeFile(file); + SET_VARSIZE(buf, nbytes + VARHDRSZ); + return buf; }