]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Don't try to read a multi-GB pg_stat_statements file in one call.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 31 Oct 2021 23:13:48 +0000 (19:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 31 Oct 2021 23:13:48 +0000 (19:13 -0400)
Windows fails on a request to read() more than INT_MAX bytes,
and perhaps other platforms could have similar issues.  Let's
adjust this code to read at most 1GB per call.

(One would not have thought the file could get that big, but now
we have a field report of trouble, so it can.  We likely ought to
add some mechanism to limit the size of the query-texts file
separately from the size of the hash table.  That is not this
patch, though.)

Per bug #17254 from Yusuke Egashira.  It's been like this for
awhile, so back-patch to all supported branches.

Discussion: https://postgr.es/m/17254-a926c89dc03375c2@postgresql.org

contrib/pg_stat_statements/pg_stat_statements.c

index 490ade9e5fe7869dbf4ac0cee903d6bfbbd1e0d1..7c2452fda7cc90366e84b241abf8c12817d7f3c5 100644 (file)
@@ -1933,6 +1933,7 @@ qtext_load_file(Size *buffer_size)
        char       *buf;
        int                     fd;
        struct stat stat;
+       Size            nread;
 
        fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
        if (fd < 0)
@@ -1973,23 +1974,35 @@ qtext_load_file(Size *buffer_size)
        }
 
        /*
-        * OK, slurp in the file.  If we get a short read and errno doesn't get
-        * set, the reason is probably that garbage collection truncated the file
-        * since we did the fstat(), so we don't log a complaint --- but we don't
-        * return the data, either, since it's most likely corrupt due to
-        * concurrent writes from garbage collection.
+        * OK, slurp in the file.  Windows fails if we try to read more than
+        * INT_MAX bytes at once, and other platforms might not like that either,
+        * so read a very large file in 1GB segments.
         */
-       errno = 0;
-       if (read(fd, buf, stat.st_size) != stat.st_size)
+       nread = 0;
+       while (nread < stat.st_size)
        {
-               if (errno)
-                       ereport(LOG,
-                                       (errcode_for_file_access(),
-                                        errmsg("could not read file \"%s\": %m",
-                                                       PGSS_TEXT_FILE)));
-               free(buf);
-               CloseTransientFile(fd);
-               return NULL;
+               int                     toread = Min(1024 * 1024 * 1024, stat.st_size - nread);
+
+               /*
+                * If we get a short read and errno doesn't get set, the reason is
+                * probably that garbage collection truncated the file since we did
+                * the fstat(), so we don't log a complaint --- but we don't return
+                * the data, either, since it's most likely corrupt due to concurrent
+                * writes from garbage collection.
+                */
+               errno = 0;
+               if (read(fd, buf + nread, toread) != toread)
+               {
+                       if (errno)
+                               ereport(LOG,
+                                               (errcode_for_file_access(),
+                                                errmsg("could not read file \"%s\": %m",
+                                                               PGSS_TEXT_FILE)));
+                       free(buf);
+                       CloseTransientFile(fd);
+                       return NULL;
+               }
+               nread += toread;
        }
 
        if (CloseTransientFile(fd))
@@ -1997,7 +2010,7 @@ qtext_load_file(Size *buffer_size)
                                (errcode_for_file_access(),
                                 errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
 
-       *buffer_size = stat.st_size;
+       *buffer_size = nread;
        return buf;
 }