LCOV - differential code coverage report
Current view: top level - src/backend/storage/file - buffile.c (source / functions) Coverage Total Hit UNC LBC UBC GNC CBC DUB DCB
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 76.2 % 323 246 1 76 10 236 2 10
Current Date: 2026-08-27 14:31:44 +0300 Functions: 92.0 % 25 23 1 1 3 20
Baseline: lcov-20260827-baseline Branches: 49.5 % 188 93 2 1 92 2 91
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 50.0 % 2 1 1 1
(30,360] days: 84.0 % 25 21 4 9 12
(360..) days: 75.7 % 296 224 72 224
Function coverage date bins:
(30,360] days: 100.0 % 3 3 3
(360..) days: 90.9 % 22 20 1 1 3 17
Branch coverage date bins:
(30,360] days: 50.0 % 6 3 2 1 2 1
(360..) days: 49.5 % 182 90 1 91 90

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * buffile.c
                                  4                 :                :  *    Management of large buffered temporary files.
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
                                  7                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :                :  *
                                  9                 :                :  * IDENTIFICATION
                                 10                 :                :  *    src/backend/storage/file/buffile.c
                                 11                 :                :  *
                                 12                 :                :  * NOTES:
                                 13                 :                :  *
                                 14                 :                :  * BufFiles provide a very incomplete emulation of stdio atop virtual Files
                                 15                 :                :  * (as managed by fd.c).  Currently, we only support the buffered-I/O
                                 16                 :                :  * aspect of stdio: a read or write of the low-level File occurs only
                                 17                 :                :  * when the buffer is filled or emptied.  This is an even bigger win
                                 18                 :                :  * for virtual Files than for ordinary kernel files, since reducing the
                                 19                 :                :  * frequency with which a virtual File is touched reduces "thrashing"
                                 20                 :                :  * of opening/closing file descriptors.
                                 21                 :                :  *
                                 22                 :                :  * Note that BufFile structs are allocated with palloc(), and therefore
                                 23                 :                :  * will go away automatically at query/transaction end.  Since the underlying
                                 24                 :                :  * virtual Files are made with OpenTemporaryFile, all resources for
                                 25                 :                :  * the file are certain to be cleaned up even if processing is aborted
                                 26                 :                :  * by ereport(ERROR).  The data structures required are made in the
                                 27                 :                :  * palloc context that was current when the BufFile was created, and
                                 28                 :                :  * any external resources such as temp files are owned by the ResourceOwner
                                 29                 :                :  * that was current at that time.
                                 30                 :                :  *
                                 31                 :                :  * BufFile also supports temporary files that exceed the OS file size limit
                                 32                 :                :  * (by opening multiple fd.c temporary files).  This is an essential feature
                                 33                 :                :  * for sorts and hashjoins on large amounts of data.
                                 34                 :                :  *
                                 35                 :                :  * BufFile supports temporary files that can be shared with other backends, as
                                 36                 :                :  * infrastructure for parallel execution.  Such files need to be created as a
                                 37                 :                :  * member of a SharedFileSet that all participants are attached to.
                                 38                 :                :  *
                                 39                 :                :  * BufFile also supports temporary files that can be used by the single backend
                                 40                 :                :  * when the corresponding files need to be survived across the transaction and
                                 41                 :                :  * need to be opened and closed multiple times.  Such files need to be created
                                 42                 :                :  * as a member of a FileSet.
                                 43                 :                :  *-------------------------------------------------------------------------
                                 44                 :                :  */
                                 45                 :                : 
                                 46                 :                : #include "postgres.h"
                                 47                 :                : 
                                 48                 :                : #include "commands/tablespace.h"
                                 49                 :                : #include "executor/instrument.h"
                                 50                 :                : #include "miscadmin.h"
                                 51                 :                : #include "pgstat.h"
                                 52                 :                : #include "storage/buffile.h"
                                 53                 :                : #include "storage/bufmgr.h"
                                 54                 :                : #include "storage/fd.h"
                                 55                 :                : #include "utils/resowner.h"
                                 56                 :                : #include "utils/wait_event.h"
                                 57                 :                : 
                                 58                 :                : /*
                                 59                 :                :  * We break BufFiles into gigabyte-sized segments, regardless of RELSEG_SIZE.
                                 60                 :                :  * The reason is that we'd like large BufFiles to be spread across multiple
                                 61                 :                :  * tablespaces when available.
                                 62                 :                :  */
                                 63                 :                : #define MAX_PHYSICAL_FILESIZE   0x40000000
                                 64                 :                : #define BUFFILE_SEG_SIZE        (MAX_PHYSICAL_FILESIZE / BLCKSZ)
                                 65                 :                : 
                                 66                 :                : /*
                                 67                 :                :  * This data structure represents a buffered file that consists of one or
                                 68                 :                :  * more physical files (each accessed through a virtual file descriptor
                                 69                 :                :  * managed by fd.c).
                                 70                 :                :  */
                                 71                 :                : struct BufFile
                                 72                 :                : {
                                 73                 :                :     int         numFiles;       /* number of physical files in set */
                                 74                 :                :     /* all files except the last have length exactly MAX_PHYSICAL_FILESIZE */
                                 75                 :                :     File       *files;          /* palloc'd array with numFiles entries */
                                 76                 :                : 
                                 77                 :                :     bool        isInterXact;    /* keep open over transactions? */
                                 78                 :                :     bool        dirty;          /* does buffer need to be written? */
                                 79                 :                :     bool        readOnly;       /* has the file been set to read only? */
                                 80                 :                : 
                                 81                 :                :     FileSet    *fileset;        /* space for fileset based segment files */
                                 82                 :                :     const char *name;           /* name of fileset based BufFile */
                                 83                 :                : 
                                 84                 :                :     /*
                                 85                 :                :      * resowner is the ResourceOwner to use for underlying temp files.  (We
                                 86                 :                :      * don't need to remember the memory context we're using explicitly,
                                 87                 :                :      * because after creation we only repalloc our arrays larger.)
                                 88                 :                :      */
                                 89                 :                :     ResourceOwner resowner;
                                 90                 :                : 
                                 91                 :                :     /*
                                 92                 :                :      * "current pos" is position of start of buffer within the logical file.
                                 93                 :                :      * Position as seen by user of BufFile is (curFile, curOffset + pos).
                                 94                 :                :      */
                                 95                 :                :     int         curFile;        /* file index (0..n) part of current pos */
                                 96                 :                :     pgoff_t     curOffset;      /* offset part of current pos */
                                 97                 :                :     int64       pos;            /* next read/write position in buffer */
                                 98                 :                :     int64       nbytes;         /* total # of valid bytes in buffer */
                                 99                 :                : 
                                100                 :                :     /*
                                101                 :                :      * XXX Should ideally use PGIOAlignedBlock, but might need a way to avoid
                                102                 :                :      * wasting per-file alignment padding when some users create many files.
                                103                 :                :      */
                                104                 :                :     PGAlignedBlock buffer;
                                105                 :                : };
                                106                 :                : 
                                107                 :                : static BufFile *makeBufFileCommon(int nfiles);
                                108                 :                : static BufFile *makeBufFile(File firstfile);
                                109                 :                : static void extendBufFile(BufFile *file);
                                110                 :                : static void BufFileLoadBuffer(BufFile *file);
                                111                 :                : static void BufFileDumpBuffer(BufFile *file);
                                112                 :                : static void BufFileFlush(BufFile *file);
                                113                 :                : static File MakeNewFileSetSegment(BufFile *buffile, int segment);
                                114                 :                : 
                                115                 :                : /*
                                116                 :                :  * Create BufFile and perform the common initialization.
                                117                 :                :  */
                                118                 :                : static BufFile *
 2994 ishii@postgresql.org      119                 :CBC        6694 : makeBufFileCommon(int nfiles)
                                120                 :                : {
  260 michael@paquier.xyz       121                 :           6694 :     BufFile    *file = palloc_object(BufFile);
                                122                 :                : 
 2994 ishii@postgresql.org      123                 :           6694 :     file->numFiles = nfiles;
 7025 tgl@sss.pgh.pa.us         124                 :           6694 :     file->isInterXact = false;
 9812                           125                 :           6694 :     file->dirty = false;
 4682                           126                 :           6694 :     file->resowner = CurrentResourceOwner;
 9812                           127                 :           6694 :     file->curFile = 0;
 1247 peter@eisentraut.org      128                 :           6694 :     file->curOffset = 0;
 9812 tgl@sss.pgh.pa.us         129                 :           6694 :     file->pos = 0;
                                130                 :           6694 :     file->nbytes = 0;
                                131                 :                : 
 2994 ishii@postgresql.org      132                 :           6694 :     return file;
                                133                 :                : }
                                134                 :                : 
                                135                 :                : /*
                                136                 :                :  * Create a BufFile given the first underlying physical file.
                                137                 :                :  * NOTE: caller must set isInterXact if appropriate.
                                138                 :                :  */
                                139                 :                : static BufFile *
                                140                 :           2072 : makeBufFile(File firstfile)
                                141                 :                : {
                                142                 :           2072 :     BufFile    *file = makeBufFileCommon(1);
                                143                 :                : 
  260 michael@paquier.xyz       144                 :           2072 :     file->files = palloc_object(File);
 2994 ishii@postgresql.org      145                 :           2072 :     file->files[0] = firstfile;
 3191 andres@anarazel.de        146                 :           2072 :     file->readOnly = false;
                                147                 :           2072 :     file->fileset = NULL;
                                148                 :           2072 :     file->name = NULL;
                                149                 :                : 
 9815 tgl@sss.pgh.pa.us         150                 :           2072 :     return file;
                                151                 :                : }
                                152                 :                : 
                                153                 :                : /*
                                154                 :                :  * Add another component temp file.
                                155                 :                :  */
                                156                 :                : static void
 9812 tgl@sss.pgh.pa.us         157                 :UBC           0 : extendBufFile(BufFile *file)
                                158                 :                : {
                                159                 :                :     File        pfile;
                                160                 :                :     ResourceOwner oldowner;
                                161                 :                : 
                                162                 :                :     /* Be sure to associate the file with the BufFile's resource owner */
 4682                           163                 :              0 :     oldowner = CurrentResourceOwner;
                                164                 :              0 :     CurrentResourceOwner = file->resowner;
                                165                 :                : 
 3191 andres@anarazel.de        166         [ #  # ]:              0 :     if (file->fileset == NULL)
                                167                 :              0 :         pfile = OpenTemporaryFile(file->isInterXact);
                                168                 :                :     else
 1823 akapila@postgresql.o      169                 :              0 :         pfile = MakeNewFileSetSegment(file, file->numFiles);
                                170                 :                : 
 9815 tgl@sss.pgh.pa.us         171         [ #  # ]:              0 :     Assert(pfile >= 0);
                                172                 :                : 
 4682                           173                 :              0 :     CurrentResourceOwner = oldowner;
                                174                 :                : 
   27 michael@paquier.xyz       175                 :UNC           0 :     file->files = repalloc_array(file->files, File, file->numFiles + 1);
 9815 tgl@sss.pgh.pa.us         176                 :UBC           0 :     file->files[file->numFiles] = pfile;
                                177                 :              0 :     file->numFiles++;
                                178                 :              0 : }
                                179                 :                : 
                                180                 :                : /*
                                181                 :                :  * Create a BufFile for a new temporary file (which will expand to become
                                182                 :                :  * multiple temporary files if more than MAX_PHYSICAL_FILESIZE bytes are
                                183                 :                :  * written to it).
                                184                 :                :  *
                                185                 :                :  * If interXact is true, the temp file will not be automatically deleted
                                186                 :                :  * at end of transaction.
                                187                 :                :  *
                                188                 :                :  * Note: if interXact is true, the caller had better be calling us in a
                                189                 :                :  * memory context, and with a resource owner, that will survive across
                                190                 :                :  * transaction boundaries.
                                191                 :                :  */
                                192                 :                : BufFile *
 7021 tgl@sss.pgh.pa.us         193                 :CBC        2072 : BufFileCreateTemp(bool interXact)
                                194                 :                : {
                                195                 :                :     BufFile    *file;
                                196                 :                :     File        pfile;
                                197                 :                : 
                                198                 :                :     /*
                                199                 :                :      * Ensure that temp tablespaces are set up for OpenTemporaryFile to use.
                                200                 :                :      * Possibly the caller will have done this already, but it seems useful to
                                201                 :                :      * double-check here.  Failure to do this at all would result in the temp
                                202                 :                :      * files always getting placed in the default tablespace, which is a
                                203                 :                :      * pretty hard-to-detect bug.  Callers may prefer to do it earlier if they
                                204                 :                :      * want to be sure that any required catalog access is done in some other
                                205                 :                :      * resource context.
                                206                 :                :      */
 2658                           207                 :           2072 :     PrepareTempTablespaces();
                                208                 :                : 
 7021                           209                 :           2072 :     pfile = OpenTemporaryFile(interXact);
 9815                           210         [ -  + ]:           2072 :     Assert(pfile >= 0);
                                211                 :                : 
 9812                           212                 :           2072 :     file = makeBufFile(pfile);
 8521                           213                 :           2072 :     file->isInterXact = interXact;
                                214                 :                : 
 9812                           215                 :           2072 :     return file;
                                216                 :                : }
                                217                 :                : 
                                218                 :                : /*
                                219                 :                :  * Build the name for a given segment of a given BufFile.
                                220                 :                :  */
                                221                 :                : static void
 1823 akapila@postgresql.o      222                 :           9908 : FileSetSegmentName(char *name, const char *buffile_name, int segment)
                                223                 :                : {
 3191 andres@anarazel.de        224                 :           9908 :     snprintf(name, MAXPGPATH, "%s.%d", buffile_name, segment);
                                225                 :           9908 : }
                                226                 :                : 
                                227                 :                : /*
                                228                 :                :  * Create a new segment file backing a fileset based BufFile.
                                229                 :                :  */
                                230                 :                : static File
 1823 akapila@postgresql.o      231                 :           2094 : MakeNewFileSetSegment(BufFile *buffile, int segment)
                                232                 :                : {
                                233                 :                :     char        name[MAXPGPATH];
                                234                 :                :     File        file;
                                235                 :                : 
                                236                 :                :     /*
                                237                 :                :      * It is possible that there are files left over from before a crash
                                238                 :                :      * restart with the same name.  In order for BufFileOpenFileSet() not to
                                239                 :                :      * get confused about how many segments there are, we'll unlink the next
                                240                 :                :      * segment number if it already exists.
                                241                 :                :      */
                                242                 :           2094 :     FileSetSegmentName(name, buffile->name, segment + 1);
                                243                 :           2094 :     FileSetDelete(buffile->fileset, name, true);
                                244                 :                : 
                                245                 :                :     /* Create the new segment. */
                                246                 :           2094 :     FileSetSegmentName(name, buffile->name, segment);
                                247                 :           2094 :     file = FileSetCreate(buffile->fileset, name);
                                248                 :                : 
                                249                 :                :     /* FileSetCreate would've errored out */
 3191 andres@anarazel.de        250         [ -  + ]:           2094 :     Assert(file > 0);
                                251                 :                : 
                                252                 :           2094 :     return file;
                                253                 :                : }
                                254                 :                : 
                                255                 :                : /*
                                256                 :                :  * Create a BufFile that can be discovered and opened read-only by other
                                257                 :                :  * backends that are attached to the same SharedFileSet using the same name.
                                258                 :                :  *
                                259                 :                :  * The naming scheme for fileset based BufFiles is left up to the calling code.
                                260                 :                :  * The name will appear as part of one or more filenames on disk, and might
                                261                 :                :  * provide clues to administrators about which subsystem is generating
                                262                 :                :  * temporary file data.  Since each SharedFileSet object is backed by one or
                                263                 :                :  * more uniquely named temporary directory, names don't conflict with
                                264                 :                :  * unrelated SharedFileSet objects.
                                265                 :                :  */
                                266                 :                : BufFile *
 1823 akapila@postgresql.o      267                 :           2094 : BufFileCreateFileSet(FileSet *fileset, const char *name)
                                268                 :                : {
                                269                 :                :     BufFile    *file;
                                270                 :                : 
 2994 ishii@postgresql.org      271                 :           2094 :     file = makeBufFileCommon(1);
 3191 andres@anarazel.de        272                 :           2094 :     file->fileset = fileset;
                                273                 :           2094 :     file->name = pstrdup(name);
  260 michael@paquier.xyz       274                 :           2094 :     file->files = palloc_object(File);
 1823 akapila@postgresql.o      275                 :           2094 :     file->files[0] = MakeNewFileSetSegment(file, 0);
 3191 andres@anarazel.de        276                 :           2094 :     file->readOnly = false;
                                277                 :                : 
                                278                 :           2094 :     return file;
                                279                 :                : }
                                280                 :                : 
                                281                 :                : /*
                                282                 :                :  * Open a file that was previously created in another backend (or this one)
                                283                 :                :  * with BufFileCreateFileSet in the same FileSet using the same name.
                                284                 :                :  * The backend that created the file must have called BufFileClose() or
                                285                 :                :  * BufFileExportFileSet() to make sure that it is ready to be opened by other
                                286                 :                :  * backends and render it read-only.  If missing_ok is true, which indicates
                                287                 :                :  * that missing files can be safely ignored, then return NULL if the BufFile
                                288                 :                :  * with the given name is not found, otherwise, throw an error.
                                289                 :                :  */
                                290                 :                : BufFile *
 1820 akapila@postgresql.o      291                 :           2801 : BufFileOpenFileSet(FileSet *fileset, const char *name, int mode,
                                292                 :                :                    bool missing_ok)
                                293                 :                : {
                                294                 :                :     BufFile    *file;
                                295                 :                :     char        segment_name[MAXPGPATH];
 3191 andres@anarazel.de        296                 :           2801 :     Size        capacity = 16;
                                297                 :                :     File       *files;
                                298                 :           2801 :     int         nfiles = 0;
                                299                 :                : 
  260 michael@paquier.xyz       300                 :           2801 :     files = palloc_array(File, capacity);
                                301                 :                : 
                                302                 :                :     /*
                                303                 :                :      * We don't know how many segments there are, so we'll probe the
                                304                 :                :      * filesystem to find out.
                                305                 :                :      */
                                306                 :                :     for (;;)
                                307                 :                :     {
                                308                 :                :         /* See if we need to expand our file segment array. */
 3191 andres@anarazel.de        309         [ -  + ]:           5329 :         if (nfiles + 1 > capacity)
                                310                 :                :         {
 3191 andres@anarazel.de        311                 :UBC           0 :             capacity *= 2;
  260 michael@paquier.xyz       312                 :              0 :             files = repalloc_array(files, File, capacity);
                                313                 :                :         }
                                314                 :                :         /* Try to load a segment. */
 1823 akapila@postgresql.o      315                 :CBC        5329 :         FileSetSegmentName(segment_name, name, nfiles);
                                316                 :           5329 :         files[nfiles] = FileSetOpen(fileset, segment_name, mode);
 3191 andres@anarazel.de        317         [ +  + ]:           5329 :         if (files[nfiles] <= 0)
                                318                 :           2801 :             break;
                                319                 :           2528 :         ++nfiles;
                                320                 :                : 
                                321         [ -  + ]:           2528 :         CHECK_FOR_INTERRUPTS();
                                322                 :                :     }
                                323                 :                : 
                                324                 :                :     /*
                                325                 :                :      * If we didn't find any files at all, then no BufFile exists with this
                                326                 :                :      * name.
                                327                 :                :      */
                                328         [ +  + ]:           2801 :     if (nfiles == 0)
                                329                 :                :     {
                                330                 :                :         /* free the memory */
 1820 akapila@postgresql.o      331                 :            273 :         pfree(files);
                                332                 :                : 
                                333         [ +  - ]:            273 :         if (missing_ok)
                                334                 :            273 :             return NULL;
                                335                 :                : 
 3179 andres@anarazel.de        336         [ #  # ]:UBC           0 :         ereport(ERROR,
                                337                 :                :                 (errcode_for_file_access(),
                                338                 :                :                  errmsg("could not open temporary file \"%s\" from BufFile \"%s\": %m",
                                339                 :                :                         segment_name, name)));
                                340                 :                :     }
                                341                 :                : 
 2994 ishii@postgresql.org      342                 :CBC        2528 :     file = makeBufFileCommon(nfiles);
 3191 andres@anarazel.de        343                 :           2528 :     file->files = files;
 1814 michael@paquier.xyz       344                 :           2528 :     file->readOnly = (mode == O_RDONLY);
 3191 andres@anarazel.de        345                 :           2528 :     file->fileset = fileset;
                                346                 :           2528 :     file->name = pstrdup(name);
                                347                 :                : 
                                348                 :           2528 :     return file;
                                349                 :                : }
                                350                 :                : 
                                351                 :                : /*
                                352                 :                :  * Delete a BufFile that was created by BufFileCreateFileSet in the given
                                353                 :                :  * FileSet using the given name.
                                354                 :                :  *
                                355                 :                :  * It is not necessary to delete files explicitly with this function.  It is
                                356                 :                :  * provided only as a way to delete files proactively, rather than waiting for
                                357                 :                :  * the FileSet to be cleaned up.
                                358                 :                :  *
                                359                 :                :  * Only one backend should attempt to delete a given name, and should know
                                360                 :                :  * that it exists and has been exported or closed otherwise missing_ok should
                                361                 :                :  * be passed true.
                                362                 :                :  */
                                363                 :                : void
 1820 akapila@postgresql.o      364                 :            352 : BufFileDeleteFileSet(FileSet *fileset, const char *name, bool missing_ok)
                                365                 :                : {
                                366                 :                :     char        segment_name[MAXPGPATH];
 3191 andres@anarazel.de        367                 :            352 :     int         segment = 0;
                                368                 :            352 :     bool        found = false;
                                369                 :                : 
                                370                 :                :     /*
                                371                 :                :      * We don't know how many segments the file has.  We'll keep deleting
                                372                 :                :      * until we run out.  If we don't manage to find even an initial segment,
                                373                 :                :      * raise an error.
                                374                 :                :      */
                                375                 :                :     for (;;)
                                376                 :                :     {
 1823 akapila@postgresql.o      377                 :            391 :         FileSetSegmentName(segment_name, name, segment);
                                378         [ +  + ]:            391 :         if (!FileSetDelete(fileset, segment_name, true))
 3191 andres@anarazel.de        379                 :            352 :             break;
                                380                 :             39 :         found = true;
                                381                 :             39 :         ++segment;
                                382                 :                : 
                                383         [ -  + ]:             39 :         CHECK_FOR_INTERRUPTS();
                                384                 :                :     }
                                385                 :                : 
 1820 akapila@postgresql.o      386   [ +  +  -  + ]:            352 :     if (!found && !missing_ok)
 1823 akapila@postgresql.o      387         [ #  # ]:UBC           0 :         elog(ERROR, "could not delete unknown BufFile \"%s\"", name);
 3191 andres@anarazel.de        388                 :CBC         352 : }
                                389                 :                : 
                                390                 :                : /*
                                391                 :                :  * BufFileExportFileSet --- flush and make read-only, in preparation for sharing.
                                392                 :                :  */
                                393                 :                : void
 1823 akapila@postgresql.o      394                 :            379 : BufFileExportFileSet(BufFile *file)
                                395                 :                : {
                                396                 :                :     /* Must be a file belonging to a FileSet. */
 3191 andres@anarazel.de        397         [ -  + ]:            379 :     Assert(file->fileset != NULL);
                                398                 :                : 
                                399                 :                :     /* It's probably a bug if someone calls this twice. */
                                400         [ -  + ]:            379 :     Assert(!file->readOnly);
                                401                 :                : 
                                402                 :            379 :     BufFileFlush(file);
                                403                 :            379 :     file->readOnly = true;
                                404                 :            379 : }
                                405                 :                : 
                                406                 :                : /*
                                407                 :                :  * Close a BufFile
                                408                 :                :  *
                                409                 :                :  * Like fclose(), this also implicitly FileCloses the underlying File.
                                410                 :                :  */
                                411                 :                : void
 9815 tgl@sss.pgh.pa.us         412                 :           6534 : BufFileClose(BufFile *file)
                                413                 :                : {
                                414                 :                :     int         i;
                                415                 :                : 
                                416                 :                :     /* flush any unwritten data */
                                417                 :           6534 :     BufFileFlush(file);
                                418                 :                :     /* close and delete the underlying file(s) */
 9812                           419         [ +  + ]:          13221 :     for (i = 0; i < file->numFiles; i++)
                                420                 :           6687 :         FileClose(file->files[i]);
                                421                 :                :     /* release the buffer space */
                                422                 :           6534 :     pfree(file->files);
 9815                           423                 :           6534 :     pfree(file);
                                424                 :           6534 : }
                                425                 :                : 
                                426                 :                : /*
                                427                 :                :  * BufFileLoadBuffer
                                428                 :                :  *
                                429                 :                :  * Load some data into buffer, if possible, starting from curOffset.
                                430                 :                :  * At call, must have dirty = false, pos and nbytes = 0.
                                431                 :                :  * On exit, nbytes is number of bytes loaded.
                                432                 :                :  */
                                433                 :                : static void
                                434                 :          67995 : BufFileLoadBuffer(BufFile *file)
                                435                 :                : {
                                436                 :                :     File        thisfile;
                                437                 :                :     instr_time  io_start;
                                438                 :                :     instr_time  io_time;
                                439                 :                :     ssize_t     rc;
                                440                 :                : 
                                441                 :                :     /*
                                442                 :                :      * Advance to next component file if necessary and possible.
                                443                 :                :      */
                                444         [ -  + ]:          67995 :     if (file->curOffset >= MAX_PHYSICAL_FILESIZE &&
 9633 bruce@momjian.us          445         [ #  # ]:UBC           0 :         file->curFile + 1 < file->numFiles)
                                446                 :                :     {
 9815 tgl@sss.pgh.pa.us         447                 :              0 :         file->curFile++;
 1247 peter@eisentraut.org      448                 :              0 :         file->curOffset = 0;
                                449                 :                :     }
                                450                 :                : 
 1602 michael@paquier.xyz       451                 :CBC       67995 :     thisfile = file->files[file->curFile];
                                452                 :                : 
                                453         [ -  + ]:          67995 :     if (track_io_timing)
 1602 michael@paquier.xyz       454                 :UBC           0 :         INSTR_TIME_SET_CURRENT(io_start);
                                455                 :                :     else
 1315 andres@anarazel.de        456                 :CBC       67995 :         INSTR_TIME_SET_ZERO(io_start);
                                457                 :                : 
                                458                 :                :     /*
                                459                 :                :      * Read whatever we can get, up to a full bufferload.
                                460                 :                :      */
   43 peter@eisentraut.org      461                 :GNC       67995 :     rc = FileRead(thisfile,
                                462                 :          67995 :                   file->buffer.data,
                                463                 :                :                   sizeof(file->buffer.data),
                                464                 :                :                   file->curOffset,
                                465                 :                :                   WAIT_EVENT_BUFFILE_READ);
                                466         [ -  + ]:          67995 :     if (rc < 0)
                                467                 :                :     {
 9815 tgl@sss.pgh.pa.us         468                 :UBC           0 :         file->nbytes = 0;
 2263 tmunro@postgresql.or      469         [ #  # ]:              0 :         ereport(ERROR,
                                470                 :                :                 (errcode_for_file_access(),
                                471                 :                :                  errmsg("could not read file \"%s\": %m",
                                472                 :                :                         FilePathName(thisfile))));
                                473                 :                :     }
                                474                 :                : 
   43 peter@eisentraut.org      475                 :GNC       67995 :     file->nbytes = rc;
                                476                 :                : 
 1602 michael@paquier.xyz       477         [ -  + ]:CBC       67995 :     if (track_io_timing)
                                478                 :                :     {
 1602 michael@paquier.xyz       479                 :UBC           0 :         INSTR_TIME_SET_CURRENT(io_time);
 1246 andres@anarazel.de        480                 :              0 :         INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_read_time, io_time, io_start);
                                481                 :                :     }
                                482                 :                : 
                                483                 :                :     /* we choose not to advance curOffset here */
                                484                 :                : 
 3222 rhaas@postgresql.org      485         [ +  + ]:CBC       67995 :     if (file->nbytes > 0)
                                486                 :          66205 :         pgBufferUsage.temp_blks_read++;
 9815 tgl@sss.pgh.pa.us         487                 :          67995 : }
                                488                 :                : 
                                489                 :                : /*
                                490                 :                :  * BufFileDumpBuffer
                                491                 :                :  *
                                492                 :                :  * Dump buffer contents starting at curOffset.
                                493                 :                :  * At call, should have dirty = true, nbytes > 0.
                                494                 :                :  * On exit, dirty is cleared if successful write, and curOffset is advanced.
                                495                 :                :  */
                                496                 :                : static void
                                497                 :          73601 : BufFileDumpBuffer(BufFile *file)
                                498                 :                : {
  244 michael@paquier.xyz       499                 :          73601 :     int64       wpos = 0;
                                500                 :                :     File        thisfile;
                                501                 :                : 
                                502                 :                :     /*
                                503                 :                :      * Unlike BufFileLoadBuffer, we must dump the whole buffer even if it
                                504                 :                :      * crosses a component-file boundary; so we need a loop.
                                505                 :                :      */
 9815 tgl@sss.pgh.pa.us         506         [ +  + ]:         147202 :     while (wpos < file->nbytes)
                                507                 :                :     {
                                508                 :                :         int64       availbytes;
                                509                 :                :         instr_time  io_start;
                                510                 :                :         instr_time  io_time;
                                511                 :                :         size_t      bytestowrite;
                                512                 :                :         ssize_t     rc;
                                513                 :                : 
                                514                 :                :         /*
                                515                 :                :          * Advance to next component file if necessary and possible.
                                516                 :                :          */
 3206 andres@anarazel.de        517         [ -  + ]:          73601 :         if (file->curOffset >= MAX_PHYSICAL_FILESIZE)
                                518                 :                :         {
 9633 bruce@momjian.us          519         [ #  # ]:UBC           0 :             while (file->curFile + 1 >= file->numFiles)
 9812 tgl@sss.pgh.pa.us         520                 :              0 :                 extendBufFile(file);
 9815                           521                 :              0 :             file->curFile++;
 1247 peter@eisentraut.org      522                 :              0 :             file->curOffset = 0;
                                523                 :                :         }
                                524                 :                : 
                                525                 :                :         /*
                                526                 :                :          * Determine how much we need to write into this file.
                                527                 :                :          */
 9815 tgl@sss.pgh.pa.us         528                 :CBC       73601 :         bytestowrite = file->nbytes - wpos;
 3206 andres@anarazel.de        529                 :          73601 :         availbytes = MAX_PHYSICAL_FILESIZE - file->curOffset;
                                530                 :                : 
  244 michael@paquier.xyz       531         [ -  + ]:          73601 :         if (bytestowrite > availbytes)
  244 michael@paquier.xyz       532                 :UBC           0 :             bytestowrite = availbytes;
                                533                 :                : 
 9812 tgl@sss.pgh.pa.us         534                 :CBC       73601 :         thisfile = file->files[file->curFile];
                                535                 :                : 
 1602 michael@paquier.xyz       536         [ -  + ]:          73601 :         if (track_io_timing)
 1602 michael@paquier.xyz       537                 :UBC           0 :             INSTR_TIME_SET_CURRENT(io_start);
                                538                 :                :         else
 1315 andres@anarazel.de        539                 :CBC       73601 :             INSTR_TIME_SET_ZERO(io_start);
                                540                 :                : 
   43 peter@eisentraut.org      541                 :GNC       73601 :         rc = FileWrite(thisfile,
                                542                 :          73601 :                        file->buffer.data + wpos,
                                543                 :                :                        bytestowrite,
                                544                 :                :                        file->curOffset,
                                545                 :                :                        WAIT_EVENT_BUFFILE_WRITE);
                                546         [ -  + ]:          73601 :         if (rc <= 0)
 2263 tmunro@postgresql.or      547         [ #  # ]:UBC           0 :             ereport(ERROR,
                                548                 :                :                     (errcode_for_file_access(),
                                549                 :                :                      errmsg("could not write to file \"%s\": %m",
                                550                 :                :                             FilePathName(thisfile))));
                                551                 :                : 
 1602 michael@paquier.xyz       552         [ -  + ]:CBC       73601 :         if (track_io_timing)
                                553                 :                :         {
 1602 michael@paquier.xyz       554                 :UBC           0 :             INSTR_TIME_SET_CURRENT(io_time);
 1246 andres@anarazel.de        555                 :              0 :             INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_write_time, io_time, io_start);
                                556                 :                :         }
                                557                 :                : 
   43 peter@eisentraut.org      558                 :GNC       73601 :         file->curOffset += rc;
                                559                 :          73601 :         wpos += rc;
                                560                 :                : 
 6099 rhaas@postgresql.org      561                 :CBC       73601 :         pgBufferUsage.temp_blks_written++;
                                562                 :                :     }
 9815 tgl@sss.pgh.pa.us         563                 :          73601 :     file->dirty = false;
                                564                 :                : 
                                565                 :                :     /*
                                566                 :                :      * At this point, curOffset has been advanced to the end of the buffer,
                                567                 :                :      * ie, its original value + nbytes.  We need to make it point to the
                                568                 :                :      * logical file position, ie, original value + pos, in case that is less
                                569                 :                :      * (as could happen due to a small backwards seek in a dirty buffer!)
                                570                 :                :      */
                                571                 :          73601 :     file->curOffset -= (file->nbytes - file->pos);
                                572         [ -  + ]:          73601 :     if (file->curOffset < 0)  /* handle possible segment crossing */
                                573                 :                :     {
 9815 tgl@sss.pgh.pa.us         574                 :UBC           0 :         file->curFile--;
                                575         [ #  # ]:              0 :         Assert(file->curFile >= 0);
                                576                 :              0 :         file->curOffset += MAX_PHYSICAL_FILESIZE;
                                577                 :                :     }
                                578                 :                : 
                                579                 :                :     /*
                                580                 :                :      * Now we can set the buffer empty without changing the logical position
                                581                 :                :      */
 9815 tgl@sss.pgh.pa.us         582                 :CBC       73601 :     file->pos = 0;
                                583                 :          73601 :     file->nbytes = 0;
                                584                 :          73601 : }
                                585                 :                : 
                                586                 :                : /*
                                587                 :                :  * BufFileRead variants
                                588                 :                :  *
                                589                 :                :  * Like fread() except we assume 1-byte element size and report I/O errors via
                                590                 :                :  * ereport().
                                591                 :                :  *
                                592                 :                :  * If 'exact' is true, then an error is also raised if the number of bytes
                                593                 :                :  * read is not exactly 'size' (no short reads).  If 'exact' and 'eofOK' are
                                594                 :                :  * true, then reading zero bytes is ok.
                                595                 :                :  */
                                596                 :                : static size_t
 1319 peter@eisentraut.org      597                 :       20616256 : BufFileReadCommon(BufFile *file, void *ptr, size_t size, bool exact, bool eofOK)
                                598                 :                : {
                                599                 :       20616256 :     size_t      start_size = size;
 9815 tgl@sss.pgh.pa.us         600                 :       20616256 :     size_t      nread = 0;
                                601                 :                :     size_t      nthistime;
                                602                 :                : 
 2263 tmunro@postgresql.or      603                 :       20616256 :     BufFileFlush(file);
                                604                 :                : 
 9815 tgl@sss.pgh.pa.us         605         [ +  + ]:       41249624 :     while (size > 0)
                                606                 :                :     {
                                607         [ +  + ]:       20635158 :         if (file->pos >= file->nbytes)
                                608                 :                :         {
                                609                 :                :             /* Try to load more data into buffer. */
                                610                 :          67995 :             file->curOffset += file->pos;
                                611                 :          67995 :             file->pos = 0;
                                612                 :          67995 :             file->nbytes = 0;
                                613                 :          67995 :             BufFileLoadBuffer(file);
                                614         [ +  + ]:          67995 :             if (file->nbytes <= 0)
                                615                 :           1790 :                 break;          /* no more data available */
                                616                 :                :         }
                                617                 :                : 
                                618                 :       20633368 :         nthistime = file->nbytes - file->pos;
                                619         [ +  + ]:       20633368 :         if (nthistime > size)
                                620                 :       20569324 :             nthistime = size;
                                621         [ -  + ]:       20633368 :         Assert(nthistime > 0);
                                622                 :                : 
 2917                           623                 :       20633368 :         memcpy(ptr, file->buffer.data + file->pos, nthistime);
                                624                 :                : 
 9815                           625                 :       20633368 :         file->pos += nthistime;
 1336 peter@eisentraut.org      626                 :       20633368 :         ptr = (char *) ptr + nthistime;
 9815 tgl@sss.pgh.pa.us         627                 :       20633368 :         size -= nthistime;
                                628                 :       20633368 :         nread += nthistime;
                                629                 :                :     }
                                630                 :                : 
 1319 peter@eisentraut.org      631   [ +  -  +  + ]:       20616256 :     if (exact &&
                                632   [ +  -  -  + ]:           1790 :         (nread != start_size && !(nread == 0 && eofOK)))
 1319 peter@eisentraut.org      633   [ #  #  #  # ]:UBC           0 :         ereport(ERROR,
                                634                 :                :                 errcode_for_file_access(),
                                635                 :                :                 file->name ?
                                636                 :                :                 errmsg("could not read from file set \"%s\": read only %zu of %zu bytes",
                                637                 :                :                        file->name, nread, start_size) :
                                638                 :                :                 errmsg("could not read from temporary file: read only %zu of %zu bytes",
                                639                 :                :                        nread, start_size));
                                640                 :                : 
 9815 tgl@sss.pgh.pa.us         641                 :CBC    20616256 :     return nread;
                                642                 :                : }
                                643                 :                : 
                                644                 :                : /*
                                645                 :                :  * Legacy interface where the caller needs to check for end of file or short
                                646                 :                :  * reads.
                                647                 :                :  */
                                648                 :                : size_t
 1319 peter@eisentraut.org      649                 :UBC           0 : BufFileRead(BufFile *file, void *ptr, size_t size)
                                650                 :                : {
                                651                 :              0 :     return BufFileReadCommon(file, ptr, size, false, false);
                                652                 :                : }
                                653                 :                : 
                                654                 :                : /*
                                655                 :                :  * Require read of exactly the specified size.
                                656                 :                :  */
                                657                 :                : void
 1319 peter@eisentraut.org      658                 :CBC    12756307 : BufFileReadExact(BufFile *file, void *ptr, size_t size)
                                659                 :                : {
                                660                 :       12756307 :     BufFileReadCommon(file, ptr, size, true, false);
                                661                 :       12756307 : }
                                662                 :                : 
                                663                 :                : /*
                                664                 :                :  * Require read of exactly the specified size, but optionally allow end of
                                665                 :                :  * file (in which case 0 is returned).
                                666                 :                :  */
                                667                 :                : size_t
                                668                 :        7859949 : BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
                                669                 :                : {
                                670                 :        7859949 :     return BufFileReadCommon(file, ptr, size, true, eofOK);
                                671                 :                : }
                                672                 :                : 
                                673                 :                : /*
                                674                 :                :  * BufFileWrite
                                675                 :                :  *
                                676                 :                :  * Like fwrite() except we assume 1-byte element size and report errors via
                                677                 :                :  * ereport().
                                678                 :                :  */
                                679                 :                : void
 1336                           680                 :       16151973 : BufFileWrite(BufFile *file, const void *ptr, size_t size)
                                681                 :                : {
                                682                 :                :     size_t      nthistime;
                                683                 :                : 
 3191 andres@anarazel.de        684         [ -  + ]:       16151973 :     Assert(!file->readOnly);
                                685                 :                : 
 9815 tgl@sss.pgh.pa.us         686         [ +  + ]:       32331968 :     while (size > 0)
                                687                 :                :     {
                                688         [ +  + ]:       16179995 :         if (file->pos >= BLCKSZ)
                                689                 :                :         {
                                690                 :                :             /* Buffer full, dump it out */
                                691         [ +  + ]:          46386 :             if (file->dirty)
                                692                 :          46074 :                 BufFileDumpBuffer(file);
                                693                 :                :             else
                                694                 :                :             {
                                695                 :                :                 /* Hmm, went directly from reading to writing? */
                                696                 :            312 :                 file->curOffset += file->pos;
                                697                 :            312 :                 file->pos = 0;
                                698                 :            312 :                 file->nbytes = 0;
                                699                 :                :             }
                                700                 :                :         }
                                701                 :                : 
                                702                 :       16179995 :         nthistime = BLCKSZ - file->pos;
                                703         [ +  + ]:       16179995 :         if (nthistime > size)
                                704                 :       16108778 :             nthistime = size;
                                705         [ -  + ]:       16179995 :         Assert(nthistime > 0);
                                706                 :                : 
 2917                           707                 :       16179995 :         memcpy(file->buffer.data + file->pos, ptr, nthistime);
                                708                 :                : 
 9815                           709                 :       16179995 :         file->dirty = true;
                                710                 :       16179995 :         file->pos += nthistime;
                                711         [ +  + ]:       16179995 :         if (file->nbytes < file->pos)
                                712                 :       16177419 :             file->nbytes = file->pos;
 1336 peter@eisentraut.org      713                 :       16179995 :         ptr = (const char *) ptr + nthistime;
 9815 tgl@sss.pgh.pa.us         714                 :       16179995 :         size -= nthistime;
                                715                 :                :     }
                                716                 :       16151973 : }
                                717                 :                : 
                                718                 :                : /*
                                719                 :                :  * BufFileFlush
                                720                 :                :  *
                                721                 :                :  * Like fflush(), except that I/O errors are reported with ereport().
                                722                 :                :  */
                                723                 :                : static void
                                724                 :       20659031 : BufFileFlush(BufFile *file)
                                725                 :                : {
                                726         [ +  + ]:       20659031 :     if (file->dirty)
                                727                 :          27527 :         BufFileDumpBuffer(file);
                                728                 :                : 
 2263 tmunro@postgresql.or      729         [ -  + ]:       20659031 :     Assert(!file->dirty);
 9815 tgl@sss.pgh.pa.us         730                 :       20659031 : }
                                731                 :                : 
                                732                 :                : /*
                                733                 :                :  * BufFileSeek
                                734                 :                :  *
                                735                 :                :  * Like fseek(), except that target position needs two values in order to
                                736                 :                :  * work when logical filesize exceeds maximum value representable by pgoff_t.
                                737                 :                :  * We do not support relative seeks across more than that, however.
                                738                 :                :  * I/O errors are reported by ereport().
                                739                 :                :  *
                                740                 :                :  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
                                741                 :                :  * impossible seek is attempted.
                                742                 :                :  */
                                743                 :                : int
  247 michael@paquier.xyz       744                 :          72775 : BufFileSeek(BufFile *file, int fileno, pgoff_t offset, int whence)
                                745                 :                : {
                                746                 :                :     int         newFile;
                                747                 :                :     pgoff_t     newOffset;
                                748                 :                : 
 9815 tgl@sss.pgh.pa.us         749   [ +  -  +  - ]:          72775 :     switch (whence)
                                750                 :                :     {
                                751                 :          72440 :         case SEEK_SET:
 9809                           752         [ -  + ]:          72440 :             if (fileno < 0)
 9815 tgl@sss.pgh.pa.us         753                 :UBC           0 :                 return EOF;
 9815 tgl@sss.pgh.pa.us         754                 :CBC       72440 :             newFile = fileno;
                                755                 :          72440 :             newOffset = offset;
                                756                 :          72440 :             break;
 9815 tgl@sss.pgh.pa.us         757                 :UBC           0 :         case SEEK_CUR:
                                758                 :                : 
                                759                 :                :             /*
                                760                 :                :              * Relative seek considers only the signed offset, ignoring
                                761                 :                :              * fileno.
                                762                 :                :              */
                                763                 :              0 :             newFile = file->curFile;
                                764                 :              0 :             newOffset = (file->curOffset + file->pos) + offset;
                                765                 :              0 :             break;
 9815 tgl@sss.pgh.pa.us         766                 :CBC         335 :         case SEEK_END:
                                767                 :                : 
                                768                 :                :             /*
                                769                 :                :              * The file size of the last file gives us the end offset of that
                                770                 :                :              * file.
                                771                 :                :              */
 2192 akapila@postgresql.o      772                 :            335 :             newFile = file->numFiles - 1;
                                773                 :            335 :             newOffset = FileSize(file->files[file->numFiles - 1]);
                                774         [ -  + ]:            335 :             if (newOffset < 0)
 2192 akapila@postgresql.o      775         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                776                 :                :                         (errcode_for_file_access(),
                                777                 :                :                          errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
                                778                 :                :                                 FilePathName(file->files[file->numFiles - 1]),
                                779                 :                :                                 file->name)));
 9815 tgl@sss.pgh.pa.us         780                 :CBC         335 :             break;
 9815 tgl@sss.pgh.pa.us         781                 :UBC           0 :         default:
 8435                           782         [ #  # ]:              0 :             elog(ERROR, "invalid whence: %d", whence);
                                783                 :                :             return EOF;
                                784                 :                :     }
 9815 tgl@sss.pgh.pa.us         785         [ -  + ]:CBC       72775 :     while (newOffset < 0)
                                786                 :                :     {
 9815 tgl@sss.pgh.pa.us         787         [ #  # ]:UBC           0 :         if (--newFile < 0)
                                788                 :              0 :             return EOF;
                                789                 :              0 :         newOffset += MAX_PHYSICAL_FILESIZE;
                                790                 :                :     }
 9815 tgl@sss.pgh.pa.us         791         [ +  + ]:CBC       72775 :     if (newFile == file->curFile &&
                                792         [ +  + ]:          72622 :         newOffset >= file->curOffset &&
                                793         [ +  + ]:          52993 :         newOffset <= file->curOffset + file->nbytes)
                                794                 :                :     {
                                795                 :                :         /*
                                796                 :                :          * Seek is to a point within existing buffer; we can just adjust
                                797                 :                :          * pos-within-buffer, without flushing buffer.  Note this is OK
                                798                 :                :          * whether reading or writing, but buffer remains dirty if we were
                                799                 :                :          * writing.
                                800                 :                :          */
  244 michael@paquier.xyz       801                 :          36913 :         file->pos = (int64) (newOffset - file->curOffset);
 9815 tgl@sss.pgh.pa.us         802                 :          36913 :         return 0;
                                803                 :                :     }
                                804                 :                :     /* Otherwise, must reposition buffer, so flush any dirty data */
 2263 tmunro@postgresql.or      805                 :          35862 :     BufFileFlush(file);
                                806                 :                : 
                                807                 :                :     /*
                                808                 :                :      * At this point and no sooner, check for seek past last segment. The
                                809                 :                :      * above flush could have created a new segment, so checking sooner would
                                810                 :                :      * not work (at least not with this code).
                                811                 :                :      */
                                812                 :                : 
                                813                 :                :     /* convert seek to "start of next seg" to "end of last seg" */
 3206 andres@anarazel.de        814   [ -  +  -  - ]:          35862 :     if (newFile == file->numFiles && newOffset == 0)
                                815                 :                :     {
 3206 andres@anarazel.de        816                 :UBC           0 :         newFile--;
                                817                 :              0 :         newOffset = MAX_PHYSICAL_FILESIZE;
                                818                 :                :     }
 3206 andres@anarazel.de        819         [ -  + ]:CBC       35862 :     while (newOffset > MAX_PHYSICAL_FILESIZE)
                                820                 :                :     {
 3206 andres@anarazel.de        821         [ #  # ]:UBC           0 :         if (++newFile >= file->numFiles)
                                822                 :              0 :             return EOF;
                                823                 :              0 :         newOffset -= MAX_PHYSICAL_FILESIZE;
                                824                 :                :     }
 9809 tgl@sss.pgh.pa.us         825         [ -  + ]:CBC       35862 :     if (newFile >= file->numFiles)
 9809 tgl@sss.pgh.pa.us         826                 :UBC           0 :         return EOF;
                                827                 :                :     /* Seek is OK! */
 9815 tgl@sss.pgh.pa.us         828                 :CBC       35862 :     file->curFile = newFile;
                                829                 :          35862 :     file->curOffset = newOffset;
                                830                 :          35862 :     file->pos = 0;
                                831                 :          35862 :     file->nbytes = 0;
                                832                 :          35862 :     return 0;
                                833                 :                : }
                                834                 :                : 
                                835                 :                : void
  247 michael@paquier.xyz       836                 :          88717 : BufFileTell(BufFile *file, int *fileno, pgoff_t *offset)
                                837                 :                : {
 9815 tgl@sss.pgh.pa.us         838                 :          88717 :     *fileno = file->curFile;
                                839                 :          88717 :     *offset = file->curOffset + file->pos;
                                840                 :          88717 : }
                                841                 :                : 
                                842                 :                : /*
                                843                 :                :  * BufFileSeekBlock --- block-oriented seek
                                844                 :                :  *
                                845                 :                :  * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
                                846                 :                :  * the file.  Note that users of this interface will fail if their files
                                847                 :                :  * exceed BLCKSZ * PG_INT64_MAX bytes, but that is quite a lot; we don't
                                848                 :                :  * work with tables bigger than that, either...
                                849                 :                :  *
                                850                 :                :  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
                                851                 :                :  * impossible seek is attempted.
                                852                 :                :  */
                                853                 :                : int
 1014 michael@paquier.xyz       854                 :          70487 : BufFileSeekBlock(BufFile *file, int64 blknum)
                                855                 :                : {
 9812 tgl@sss.pgh.pa.us         856                 :         140974 :     return BufFileSeek(file,
 6744                           857                 :          70487 :                        (int) (blknum / BUFFILE_SEG_SIZE),
  247 michael@paquier.xyz       858                 :          70487 :                        (pgoff_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
                                859                 :                :                        SEEK_SET);
                                860                 :                : }
                                861                 :                : 
                                862                 :                : /*
                                863                 :                :  * Returns the amount of data in the given BufFile, in bytes.
                                864                 :                :  *
                                865                 :                :  * Returned value includes the size of any holes left behind by BufFileAppend.
                                866                 :                :  * ereport()s on failure.
                                867                 :                :  */
                                868                 :                : int64
 3128 rhaas@postgresql.org      869                 :            291 : BufFileSize(BufFile *file)
                                870                 :                : {
                                871                 :                :     int64       lastFileSize;
                                872                 :                : 
                                873                 :                :     /* Get the size of the last physical file. */
 2850 tmunro@postgresql.or      874                 :            291 :     lastFileSize = FileSize(file->files[file->numFiles - 1]);
 3039 heikki.linnakangas@i      875         [ -  + ]:            291 :     if (lastFileSize < 0)
 2829 pg@bowt.ie                876         [ #  # ]:UBC           0 :         ereport(ERROR,
                                877                 :                :                 (errcode_for_file_access(),
                                878                 :                :                  errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
                                879                 :                :                         FilePathName(file->files[file->numFiles - 1]),
                                880                 :                :                         file->name)));
                                881                 :                : 
 2842 tmunro@postgresql.or      882                 :CBC         291 :     return ((file->numFiles - 1) * (int64) MAX_PHYSICAL_FILESIZE) +
                                883                 :                :         lastFileSize;
                                884                 :                : }
                                885                 :                : 
                                886                 :                : /*
                                887                 :                :  * Append the contents of the source file to the end of the target file.
                                888                 :                :  *
                                889                 :                :  * Note that operation subsumes ownership of underlying resources from
                                890                 :                :  * "source".  Caller should never call BufFileClose against source having
                                891                 :                :  * called here first.  Resource owners for source and target must match,
                                892                 :                :  * too.
                                893                 :                :  *
                                894                 :                :  * This operation works by manipulating lists of segment files, so the
                                895                 :                :  * file content is always appended at a MAX_PHYSICAL_FILESIZE-aligned
                                896                 :                :  * boundary, typically creating empty holes before the boundary.  These
                                897                 :                :  * areas do not contain any interesting data, and cannot be read from by
                                898                 :                :  * caller.
                                899                 :                :  *
                                900                 :                :  * Returns the block number within target where the contents of source
                                901                 :                :  * begins.  Caller should apply this as an offset when working off block
                                902                 :                :  * positions that are in terms of the original BufFile space.
                                903                 :                :  */
                                904                 :                : int64
 3128 rhaas@postgresql.org      905                 :            153 : BufFileAppend(BufFile *target, BufFile *source)
                                906                 :                : {
 1011 michael@paquier.xyz       907                 :            153 :     int64       startBlock = (int64) target->numFiles * BUFFILE_SEG_SIZE;
 3128 rhaas@postgresql.org      908                 :            153 :     int         newNumFiles = target->numFiles + source->numFiles;
                                909                 :                :     int         i;
                                910                 :                : 
                                911         [ -  + ]:            153 :     Assert(source->readOnly);
                                912         [ -  + ]:            153 :     Assert(!source->dirty);
                                913                 :                : 
                                914         [ -  + ]:            153 :     if (target->resowner != source->resowner)
 3128 rhaas@postgresql.org      915         [ #  # ]:UBC           0 :         elog(ERROR, "could not append BufFile with non-matching resource owner");
                                916                 :                : 
   27 michael@paquier.xyz       917                 :GNC         153 :     target->files = repalloc_array(target->files, File, newNumFiles);
 3128 rhaas@postgresql.org      918         [ +  + ]:CBC         306 :     for (i = target->numFiles; i < newNumFiles; i++)
                                919                 :            153 :         target->files[i] = source->files[i - target->numFiles];
                                920                 :            153 :     target->numFiles = newNumFiles;
                                921                 :                : 
                                922                 :            153 :     return startBlock;
                                923                 :                : }
                                924                 :                : 
                                925                 :                : /*
                                926                 :                :  * Truncate a BufFile created by BufFileCreateFileSet up to the given fileno
                                927                 :                :  * and the offset.
                                928                 :                :  */
                                929                 :                : void
  247 michael@paquier.xyz       930                 :              9 : BufFileTruncateFileSet(BufFile *file, int fileno, pgoff_t offset)
                                931                 :                : {
 2192 akapila@postgresql.o      932                 :              9 :     int         numFiles = file->numFiles;
                                933                 :              9 :     int         newFile = fileno;
  247 michael@paquier.xyz       934                 :              9 :     pgoff_t     newOffset = file->curOffset;
                                935                 :                :     char        segment_name[MAXPGPATH];
                                936                 :                :     int         i;
                                937                 :                : 
                                938                 :                :     /*
                                939                 :                :      * Loop over all the files up to the given fileno and remove the files
                                940                 :                :      * that are greater than the fileno and truncate the given file up to the
                                941                 :                :      * offset. Note that we also remove the given fileno if the offset is 0
                                942                 :                :      * provided it is not the first file in which we truncate it.
                                943                 :                :      */
 2192 akapila@postgresql.o      944         [ +  + ]:             18 :     for (i = file->numFiles - 1; i >= fileno; i--)
                                945                 :                :     {
                                946   [ +  -  -  +  :              9 :         if ((i != fileno || offset == 0) && i != 0)
                                              -  - ]
                                947                 :                :         {
 1823 akapila@postgresql.o      948                 :UBC           0 :             FileSetSegmentName(segment_name, file->name, i);
 2192                           949                 :              0 :             FileClose(file->files[i]);
 1823                           950         [ #  # ]:              0 :             if (!FileSetDelete(file->fileset, segment_name, true))
 2192                           951         [ #  # ]:              0 :                 ereport(ERROR,
                                952                 :                :                         (errcode_for_file_access(),
                                953                 :                :                          errmsg("could not delete fileset \"%s\": %m",
                                954                 :                :                                 segment_name)));
                                955                 :              0 :             numFiles--;
                                956                 :              0 :             newOffset = MAX_PHYSICAL_FILESIZE;
                                957                 :                : 
                                958                 :                :             /*
                                959                 :                :              * This is required to indicate that we have deleted the given
                                960                 :                :              * fileno.
                                961                 :                :              */
                                962         [ #  # ]:              0 :             if (i == fileno)
                                963                 :              0 :                 newFile--;
                                964                 :                :         }
                                965                 :                :         else
                                966                 :                :         {
 2192 akapila@postgresql.o      967         [ -  + ]:CBC           9 :             if (FileTruncate(file->files[i], offset,
                                968                 :                :                              WAIT_EVENT_BUFFILE_TRUNCATE) < 0)
 2192 akapila@postgresql.o      969         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                970                 :                :                         (errcode_for_file_access(),
                                971                 :                :                          errmsg("could not truncate file \"%s\": %m",
                                972                 :                :                                 FilePathName(file->files[i]))));
 2192 akapila@postgresql.o      973                 :CBC           9 :             newOffset = offset;
                                974                 :                :         }
                                975                 :                :     }
                                976                 :                : 
                                977                 :              9 :     file->numFiles = numFiles;
                                978                 :                : 
                                979                 :                :     /*
                                980                 :                :      * If the truncate point is within existing buffer then we can just adjust
                                981                 :                :      * pos within buffer.
                                982                 :                :      */
                                983         [ +  - ]:              9 :     if (newFile == file->curFile &&
                                984         [ +  - ]:              9 :         newOffset >= file->curOffset &&
                                985         [ -  + ]:              9 :         newOffset <= file->curOffset + file->nbytes)
                                986                 :                :     {
                                987                 :                :         /* No need to reset the current pos if the new pos is greater. */
 2192 akapila@postgresql.o      988         [ #  # ]:UBC           0 :         if (newOffset <= file->curOffset + file->pos)
  244 michael@paquier.xyz       989                 :              0 :             file->pos = (int64) newOffset - file->curOffset;
                                990                 :                : 
                                991                 :                :         /* Adjust the nbytes for the current buffer. */
                                992                 :              0 :         file->nbytes = (int64) newOffset - file->curOffset;
                                993                 :                :     }
 2192 akapila@postgresql.o      994         [ +  - ]:CBC           9 :     else if (newFile == file->curFile &&
                                995         [ -  + ]:              9 :              newOffset < file->curOffset)
                                996                 :                :     {
                                997                 :                :         /*
                                998                 :                :          * The truncate point is within the existing file but prior to the
                                999                 :                :          * current position, so we can forget the current buffer and reset the
                               1000                 :                :          * current position.
                               1001                 :                :          */
 2192 akapila@postgresql.o     1002                 :UBC           0 :         file->curOffset = newOffset;
                               1003                 :              0 :         file->pos = 0;
                               1004                 :              0 :         file->nbytes = 0;
                               1005                 :                :     }
 2192 akapila@postgresql.o     1006         [ -  + ]:CBC           9 :     else if (newFile < file->curFile)
                               1007                 :                :     {
                               1008                 :                :         /*
                               1009                 :                :          * The truncate point is prior to the current file, so need to reset
                               1010                 :                :          * the current position accordingly.
                               1011                 :                :          */
 2192 akapila@postgresql.o     1012                 :UBC           0 :         file->curFile = newFile;
                               1013                 :              0 :         file->curOffset = newOffset;
                               1014                 :              0 :         file->pos = 0;
                               1015                 :              0 :         file->nbytes = 0;
                               1016                 :                :     }
                               1017                 :                :     /* Nothing to do, if the truncate point is beyond current file. */
 2192 akapila@postgresql.o     1018                 :CBC           9 : }
        

Generated by: LCOV version 2.0-1