LCOV - code coverage report
Current view: top level - src/backend/storage/file - buffile.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 65.5 % 383 251
Test Date: 2026-01-26 10:56:24 Functions: 84.0 % 25 21
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 35.0 % 206 72

             Branch data     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                 :             : 
      57                 :             : /*
      58                 :             :  * We break BufFiles into gigabyte-sized segments, regardless of RELSEG_SIZE.
      59                 :             :  * The reason is that we'd like large BufFiles to be spread across multiple
      60                 :             :  * tablespaces when available.
      61                 :             :  */
      62                 :             : #define MAX_PHYSICAL_FILESIZE   0x40000000
      63                 :             : #define BUFFILE_SEG_SIZE                (MAX_PHYSICAL_FILESIZE / BLCKSZ)
      64                 :             : 
      65                 :             : /*
      66                 :             :  * This data structure represents a buffered file that consists of one or
      67                 :             :  * more physical files (each accessed through a virtual file descriptor
      68                 :             :  * managed by fd.c).
      69                 :             :  */
      70                 :             : struct BufFile
      71                 :             : {
      72                 :             :         int                     numFiles;               /* number of physical files in set */
      73                 :             :         /* all files except the last have length exactly MAX_PHYSICAL_FILESIZE */
      74                 :             :         File       *files;                      /* palloc'd array with numFiles entries */
      75                 :             : 
      76                 :             :         bool            isInterXact;    /* keep open over transactions? */
      77                 :             :         bool            dirty;                  /* does buffer need to be written? */
      78                 :             :         bool            readOnly;               /* has the file been set to read only? */
      79                 :             : 
      80                 :             :         FileSet    *fileset;            /* space for fileset based segment files */
      81                 :             :         const char *name;                       /* name of fileset based BufFile */
      82                 :             : 
      83                 :             :         /*
      84                 :             :          * resowner is the ResourceOwner to use for underlying temp files.  (We
      85                 :             :          * don't need to remember the memory context we're using explicitly,
      86                 :             :          * because after creation we only repalloc our arrays larger.)
      87                 :             :          */
      88                 :             :         ResourceOwner resowner;
      89                 :             : 
      90                 :             :         /*
      91                 :             :          * "current pos" is position of start of buffer within the logical file.
      92                 :             :          * Position as seen by user of BufFile is (curFile, curOffset + pos).
      93                 :             :          */
      94                 :             :         int                     curFile;                /* file index (0..n) part of current pos */
      95                 :             :         pgoff_t         curOffset;              /* offset part of current pos */
      96                 :             :         int64           pos;                    /* next read/write position in buffer */
      97                 :             :         int64           nbytes;                 /* total # of valid bytes in buffer */
      98                 :             : 
      99                 :             :         /*
     100                 :             :          * XXX Should ideally use PGIOAlignedBlock, but might need a way to avoid
     101                 :             :          * wasting per-file alignment padding when some users create many files.
     102                 :             :          */
     103                 :             :         PGAlignedBlock buffer;
     104                 :             : };
     105                 :             : 
     106                 :             : static BufFile *makeBufFileCommon(int nfiles);
     107                 :             : static BufFile *makeBufFile(File firstfile);
     108                 :             : static void extendBufFile(BufFile *file);
     109                 :             : static void BufFileLoadBuffer(BufFile *file);
     110                 :             : static void BufFileDumpBuffer(BufFile *file);
     111                 :             : static void BufFileFlush(BufFile *file);
     112                 :             : static File MakeNewFileSetSegment(BufFile *buffile, int segment);
     113                 :             : 
     114                 :             : /*
     115                 :             :  * Create BufFile and perform the common initialization.
     116                 :             :  */
     117                 :             : static BufFile *
     118                 :        1366 : makeBufFileCommon(int nfiles)
     119                 :             : {
     120                 :        1366 :         BufFile    *file = palloc_object(BufFile);
     121                 :             : 
     122                 :        1366 :         file->numFiles = nfiles;
     123                 :        1366 :         file->isInterXact = false;
     124                 :        1366 :         file->dirty = false;
     125                 :        1366 :         file->resowner = CurrentResourceOwner;
     126                 :        1366 :         file->curFile = 0;
     127                 :        1366 :         file->curOffset = 0;
     128                 :        1366 :         file->pos = 0;
     129                 :        1366 :         file->nbytes = 0;
     130                 :             : 
     131                 :        2732 :         return file;
     132                 :        1366 : }
     133                 :             : 
     134                 :             : /*
     135                 :             :  * Create a BufFile given the first underlying physical file.
     136                 :             :  * NOTE: caller must set isInterXact if appropriate.
     137                 :             :  */
     138                 :             : static BufFile *
     139                 :         485 : makeBufFile(File firstfile)
     140                 :             : {
     141                 :         485 :         BufFile    *file = makeBufFileCommon(1);
     142                 :             : 
     143                 :         485 :         file->files = palloc_object(File);
     144                 :         485 :         file->files[0] = firstfile;
     145                 :         485 :         file->readOnly = false;
     146                 :         485 :         file->fileset = NULL;
     147                 :         485 :         file->name = NULL;
     148                 :             : 
     149                 :         970 :         return file;
     150                 :         485 : }
     151                 :             : 
     152                 :             : /*
     153                 :             :  * Add another component temp file.
     154                 :             :  */
     155                 :             : static void
     156                 :           0 : extendBufFile(BufFile *file)
     157                 :             : {
     158                 :           0 :         File            pfile;
     159                 :           0 :         ResourceOwner oldowner;
     160                 :             : 
     161                 :             :         /* Be sure to associate the file with the BufFile's resource owner */
     162                 :           0 :         oldowner = CurrentResourceOwner;
     163                 :           0 :         CurrentResourceOwner = file->resowner;
     164                 :             : 
     165         [ #  # ]:           0 :         if (file->fileset == NULL)
     166                 :           0 :                 pfile = OpenTemporaryFile(file->isInterXact);
     167                 :             :         else
     168                 :           0 :                 pfile = MakeNewFileSetSegment(file, file->numFiles);
     169                 :             : 
     170         [ #  # ]:           0 :         Assert(pfile >= 0);
     171                 :             : 
     172                 :           0 :         CurrentResourceOwner = oldowner;
     173                 :             : 
     174                 :           0 :         file->files = (File *) repalloc(file->files,
     175                 :           0 :                                                                         (file->numFiles + 1) * sizeof(File));
     176                 :           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 *
     193                 :         485 : BufFileCreateTemp(bool interXact)
     194                 :             : {
     195                 :         485 :         BufFile    *file;
     196                 :         485 :         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                 :             :          */
     207                 :         485 :         PrepareTempTablespaces();
     208                 :             : 
     209                 :         485 :         pfile = OpenTemporaryFile(interXact);
     210         [ +  - ]:         485 :         Assert(pfile >= 0);
     211                 :             : 
     212                 :         485 :         file = makeBufFile(pfile);
     213                 :         485 :         file->isInterXact = interXact;
     214                 :             : 
     215                 :         970 :         return file;
     216                 :         485 : }
     217                 :             : 
     218                 :             : /*
     219                 :             :  * Build the name for a given segment of a given BufFile.
     220                 :             :  */
     221                 :             : static void
     222                 :        1762 : FileSetSegmentName(char *name, const char *buffile_name, int segment)
     223                 :             : {
     224                 :        1762 :         snprintf(name, MAXPGPATH, "%s.%d", buffile_name, segment);
     225                 :        1762 : }
     226                 :             : 
     227                 :             : /*
     228                 :             :  * Create a new segment file backing a fileset based BufFile.
     229                 :             :  */
     230                 :             : static File
     231                 :         448 : MakeNewFileSetSegment(BufFile *buffile, int segment)
     232                 :             : {
     233                 :         448 :         char            name[MAXPGPATH];
     234                 :         448 :         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                 :         448 :         FileSetSegmentName(name, buffile->name, segment + 1);
     243                 :         448 :         FileSetDelete(buffile->fileset, name, true);
     244                 :             : 
     245                 :             :         /* Create the new segment. */
     246                 :         448 :         FileSetSegmentName(name, buffile->name, segment);
     247                 :         448 :         file = FileSetCreate(buffile->fileset, name);
     248                 :             : 
     249                 :             :         /* FileSetCreate would've errored out */
     250         [ +  - ]:         448 :         Assert(file > 0);
     251                 :             : 
     252                 :         896 :         return file;
     253                 :         448 : }
     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 *
     267                 :         448 : BufFileCreateFileSet(FileSet *fileset, const char *name)
     268                 :             : {
     269                 :         448 :         BufFile    *file;
     270                 :             : 
     271                 :         448 :         file = makeBufFileCommon(1);
     272                 :         448 :         file->fileset = fileset;
     273                 :         448 :         file->name = pstrdup(name);
     274                 :         448 :         file->files = palloc_object(File);
     275                 :         448 :         file->files[0] = MakeNewFileSetSegment(file, 0);
     276                 :         448 :         file->readOnly = false;
     277                 :             : 
     278                 :         896 :         return file;
     279                 :         448 : }
     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 *
     291                 :         433 : BufFileOpenFileSet(FileSet *fileset, const char *name, int mode,
     292                 :             :                                    bool missing_ok)
     293                 :             : {
     294                 :         433 :         BufFile    *file;
     295                 :         433 :         char            segment_name[MAXPGPATH];
     296                 :         433 :         Size            capacity = 16;
     297                 :         433 :         File       *files;
     298                 :         433 :         int                     nfiles = 0;
     299                 :             : 
     300                 :         433 :         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                 :         866 :         for (;;)
     307                 :             :         {
     308                 :             :                 /* See if we need to expand our file segment array. */
     309         [ +  - ]:         866 :                 if (nfiles + 1 > capacity)
     310                 :             :                 {
     311                 :           0 :                         capacity *= 2;
     312                 :           0 :                         files = repalloc_array(files, File, capacity);
     313                 :           0 :                 }
     314                 :             :                 /* Try to load a segment. */
     315                 :         866 :                 FileSetSegmentName(segment_name, name, nfiles);
     316                 :         866 :                 files[nfiles] = FileSetOpen(fileset, segment_name, mode);
     317         [ +  + ]:         866 :                 if (files[nfiles] <= 0)
     318                 :         433 :                         break;
     319                 :         433 :                 ++nfiles;
     320                 :             : 
     321         [ +  + ]:         433 :                 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         [ +  - ]:         433 :         if (nfiles == 0)
     329                 :             :         {
     330                 :             :                 /* free the memory */
     331                 :           0 :                 pfree(files);
     332                 :             : 
     333         [ #  # ]:           0 :                 if (missing_ok)
     334                 :           0 :                         return NULL;
     335                 :             : 
     336   [ #  #  #  # ]:           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                 :           0 :         }
     341                 :             : 
     342                 :         433 :         file = makeBufFileCommon(nfiles);
     343                 :         433 :         file->files = files;
     344                 :         433 :         file->readOnly = (mode == O_RDONLY);
     345                 :         433 :         file->fileset = fileset;
     346                 :         433 :         file->name = pstrdup(name);
     347                 :             : 
     348                 :         433 :         return file;
     349                 :         433 : }
     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
     364                 :           0 : BufFileDeleteFileSet(FileSet *fileset, const char *name, bool missing_ok)
     365                 :             : {
     366                 :           0 :         char            segment_name[MAXPGPATH];
     367                 :           0 :         int                     segment = 0;
     368                 :           0 :         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                 :           0 :         for (;;)
     376                 :             :         {
     377                 :           0 :                 FileSetSegmentName(segment_name, name, segment);
     378         [ #  # ]:           0 :                 if (!FileSetDelete(fileset, segment_name, true))
     379                 :           0 :                         break;
     380                 :           0 :                 found = true;
     381                 :           0 :                 ++segment;
     382                 :             : 
     383         [ #  # ]:           0 :                 CHECK_FOR_INTERRUPTS();
     384                 :             :         }
     385                 :             : 
     386   [ #  #  #  # ]:           0 :         if (!found && !missing_ok)
     387   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not delete unknown BufFile \"%s\"", name);
     388                 :           0 : }
     389                 :             : 
     390                 :             : /*
     391                 :             :  * BufFileExportFileSet --- flush and make read-only, in preparation for sharing.
     392                 :             :  */
     393                 :             : void
     394                 :          82 : BufFileExportFileSet(BufFile *file)
     395                 :             : {
     396                 :             :         /* Must be a file belonging to a FileSet. */
     397         [ +  - ]:          82 :         Assert(file->fileset != NULL);
     398                 :             : 
     399                 :             :         /* It's probably a bug if someone calls this twice. */
     400         [ +  - ]:          82 :         Assert(!file->readOnly);
     401                 :             : 
     402                 :          82 :         BufFileFlush(file);
     403                 :          82 :         file->readOnly = true;
     404                 :          82 : }
     405                 :             : 
     406                 :             : /*
     407                 :             :  * Close a BufFile
     408                 :             :  *
     409                 :             :  * Like fclose(), this also implicitly FileCloses the underlying File.
     410                 :             :  */
     411                 :             : void
     412                 :        1338 : BufFileClose(BufFile *file)
     413                 :             : {
     414                 :        1338 :         int                     i;
     415                 :             : 
     416                 :             :         /* flush any unwritten data */
     417                 :        1338 :         BufFileFlush(file);
     418                 :             :         /* close and delete the underlying file(s) */
     419         [ +  + ]:        2704 :         for (i = 0; i < file->numFiles; i++)
     420                 :        1366 :                 FileClose(file->files[i]);
     421                 :             :         /* release the buffer space */
     422                 :        1338 :         pfree(file->files);
     423                 :        1338 :         pfree(file);
     424                 :        1338 : }
     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                 :       34558 : BufFileLoadBuffer(BufFile *file)
     435                 :             : {
     436                 :       34558 :         File            thisfile;
     437                 :       34558 :         instr_time      io_start;
     438                 :       34558 :         instr_time      io_time;
     439                 :             : 
     440                 :             :         /*
     441                 :             :          * Advance to next component file if necessary and possible.
     442                 :             :          */
     443   [ -  +  #  # ]:       34558 :         if (file->curOffset >= MAX_PHYSICAL_FILESIZE &&
     444                 :           0 :                 file->curFile + 1 < file->numFiles)
     445                 :             :         {
     446                 :           0 :                 file->curFile++;
     447                 :           0 :                 file->curOffset = 0;
     448                 :           0 :         }
     449                 :             : 
     450                 :       34558 :         thisfile = file->files[file->curFile];
     451                 :             : 
     452         [ -  + ]:       34558 :         if (track_io_timing)
     453                 :           0 :                 INSTR_TIME_SET_CURRENT(io_start);
     454                 :             :         else
     455                 :       34558 :                 INSTR_TIME_SET_ZERO(io_start);
     456                 :             : 
     457                 :             :         /*
     458                 :             :          * Read whatever we can get, up to a full bufferload.
     459                 :             :          */
     460                 :       69116 :         file->nbytes = FileRead(thisfile,
     461                 :       34558 :                                                         file->buffer.data,
     462                 :             :                                                         sizeof(file->buffer.data),
     463                 :       34558 :                                                         file->curOffset,
     464                 :             :                                                         WAIT_EVENT_BUFFILE_READ);
     465         [ +  - ]:       34558 :         if (file->nbytes < 0)
     466                 :             :         {
     467                 :           0 :                 file->nbytes = 0;
     468   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     469                 :             :                                 (errcode_for_file_access(),
     470                 :             :                                  errmsg("could not read file \"%s\": %m",
     471                 :             :                                                 FilePathName(thisfile))));
     472                 :           0 :         }
     473                 :             : 
     474         [ +  - ]:       34558 :         if (track_io_timing)
     475                 :             :         {
     476                 :           0 :                 INSTR_TIME_SET_CURRENT(io_time);
     477                 :           0 :                 INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_read_time, io_time, io_start);
     478                 :           0 :         }
     479                 :             : 
     480                 :             :         /* we choose not to advance curOffset here */
     481                 :             : 
     482         [ +  + ]:       34558 :         if (file->nbytes > 0)
     483                 :       34106 :                 pgBufferUsage.temp_blks_read++;
     484                 :       34558 : }
     485                 :             : 
     486                 :             : /*
     487                 :             :  * BufFileDumpBuffer
     488                 :             :  *
     489                 :             :  * Dump buffer contents starting at curOffset.
     490                 :             :  * At call, should have dirty = true, nbytes > 0.
     491                 :             :  * On exit, dirty is cleared if successful write, and curOffset is advanced.
     492                 :             :  */
     493                 :             : static void
     494                 :       35453 : BufFileDumpBuffer(BufFile *file)
     495                 :             : {
     496                 :       35453 :         int64           wpos = 0;
     497                 :       35453 :         int64           bytestowrite;
     498                 :       35453 :         File            thisfile;
     499                 :             : 
     500                 :             :         /*
     501                 :             :          * Unlike BufFileLoadBuffer, we must dump the whole buffer even if it
     502                 :             :          * crosses a component-file boundary; so we need a loop.
     503                 :             :          */
     504         [ +  + ]:       70906 :         while (wpos < file->nbytes)
     505                 :             :         {
     506                 :       35453 :                 int64           availbytes;
     507                 :       35453 :                 instr_time      io_start;
     508                 :       35453 :                 instr_time      io_time;
     509                 :             : 
     510                 :             :                 /*
     511                 :             :                  * Advance to next component file if necessary and possible.
     512                 :             :                  */
     513         [ +  - ]:       35453 :                 if (file->curOffset >= MAX_PHYSICAL_FILESIZE)
     514                 :             :                 {
     515         [ #  # ]:           0 :                         while (file->curFile + 1 >= file->numFiles)
     516                 :           0 :                                 extendBufFile(file);
     517                 :           0 :                         file->curFile++;
     518                 :           0 :                         file->curOffset = 0;
     519                 :           0 :                 }
     520                 :             : 
     521                 :             :                 /*
     522                 :             :                  * Determine how much we need to write into this file.
     523                 :             :                  */
     524                 :       35453 :                 bytestowrite = file->nbytes - wpos;
     525                 :       35453 :                 availbytes = MAX_PHYSICAL_FILESIZE - file->curOffset;
     526                 :             : 
     527         [ +  - ]:       35453 :                 if (bytestowrite > availbytes)
     528                 :           0 :                         bytestowrite = availbytes;
     529                 :             : 
     530                 :       35453 :                 thisfile = file->files[file->curFile];
     531                 :             : 
     532         [ -  + ]:       35453 :                 if (track_io_timing)
     533                 :           0 :                         INSTR_TIME_SET_CURRENT(io_start);
     534                 :             :                 else
     535                 :       35453 :                         INSTR_TIME_SET_ZERO(io_start);
     536                 :             : 
     537                 :       70906 :                 bytestowrite = FileWrite(thisfile,
     538                 :       35453 :                                                                  file->buffer.data + wpos,
     539                 :       35453 :                                                                  bytestowrite,
     540                 :       35453 :                                                                  file->curOffset,
     541                 :             :                                                                  WAIT_EVENT_BUFFILE_WRITE);
     542         [ +  - ]:       35453 :                 if (bytestowrite <= 0)
     543   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     544                 :             :                                         (errcode_for_file_access(),
     545                 :             :                                          errmsg("could not write to file \"%s\": %m",
     546                 :             :                                                         FilePathName(thisfile))));
     547                 :             : 
     548         [ -  + ]:       35453 :                 if (track_io_timing)
     549                 :             :                 {
     550                 :           0 :                         INSTR_TIME_SET_CURRENT(io_time);
     551                 :           0 :                         INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_write_time, io_time, io_start);
     552                 :           0 :                 }
     553                 :             : 
     554                 :       35453 :                 file->curOffset += bytestowrite;
     555                 :       35453 :                 wpos += bytestowrite;
     556                 :             : 
     557                 :       35453 :                 pgBufferUsage.temp_blks_written++;
     558                 :       35453 :         }
     559                 :       35453 :         file->dirty = false;
     560                 :             : 
     561                 :             :         /*
     562                 :             :          * At this point, curOffset has been advanced to the end of the buffer,
     563                 :             :          * ie, its original value + nbytes.  We need to make it point to the
     564                 :             :          * logical file position, ie, original value + pos, in case that is less
     565                 :             :          * (as could happen due to a small backwards seek in a dirty buffer!)
     566                 :             :          */
     567                 :       35453 :         file->curOffset -= (file->nbytes - file->pos);
     568         [ +  - ]:       35453 :         if (file->curOffset < 0)  /* handle possible segment crossing */
     569                 :             :         {
     570                 :           0 :                 file->curFile--;
     571         [ #  # ]:           0 :                 Assert(file->curFile >= 0);
     572                 :           0 :                 file->curOffset += MAX_PHYSICAL_FILESIZE;
     573                 :           0 :         }
     574                 :             : 
     575                 :             :         /*
     576                 :             :          * Now we can set the buffer empty without changing the logical position
     577                 :             :          */
     578                 :       35453 :         file->pos = 0;
     579                 :       35453 :         file->nbytes = 0;
     580                 :       35453 : }
     581                 :             : 
     582                 :             : /*
     583                 :             :  * BufFileRead variants
     584                 :             :  *
     585                 :             :  * Like fread() except we assume 1-byte element size and report I/O errors via
     586                 :             :  * ereport().
     587                 :             :  *
     588                 :             :  * If 'exact' is true, then an error is also raised if the number of bytes
     589                 :             :  * read is not exactly 'size' (no short reads).  If 'exact' and 'eofOK' are
     590                 :             :  * true, then reading zero bytes is ok.
     591                 :             :  */
     592                 :             : static size_t
     593                 :     5404465 : BufFileReadCommon(BufFile *file, void *ptr, size_t size, bool exact, bool eofOK)
     594                 :             : {
     595                 :     5404465 :         size_t          start_size = size;
     596                 :     5404465 :         size_t          nread = 0;
     597                 :     5404465 :         size_t          nthistime;
     598                 :             : 
     599                 :     5404465 :         BufFileFlush(file);
     600                 :             : 
     601         [ +  + ]:    10832062 :         while (size > 0)
     602                 :             :         {
     603         [ +  + ]:     5428049 :                 if (file->pos >= file->nbytes)
     604                 :             :                 {
     605                 :             :                         /* Try to load more data into buffer. */
     606                 :       34558 :                         file->curOffset += file->pos;
     607                 :       34558 :                         file->pos = 0;
     608                 :       34558 :                         file->nbytes = 0;
     609                 :       34558 :                         BufFileLoadBuffer(file);
     610         [ +  + ]:       34558 :                         if (file->nbytes <= 0)
     611                 :         452 :                                 break;                  /* no more data available */
     612                 :       34106 :                 }
     613                 :             : 
     614                 :     5427597 :                 nthistime = file->nbytes - file->pos;
     615         [ +  + ]:     5427597 :                 if (nthistime > size)
     616                 :     5393985 :                         nthistime = size;
     617         [ -  + ]:     5427597 :                 Assert(nthistime > 0);
     618                 :             : 
     619                 :     5427597 :                 memcpy(ptr, file->buffer.data + file->pos, nthistime);
     620                 :             : 
     621                 :     5427597 :                 file->pos += nthistime;
     622                 :     5427597 :                 ptr = (char *) ptr + nthistime;
     623                 :     5427597 :                 size -= nthistime;
     624                 :     5427597 :                 nread += nthistime;
     625                 :             :         }
     626                 :             : 
     627         [ +  - ]:     5404917 :         if (exact &&
     628   [ +  +  +  - ]:     5404465 :                 (nread != start_size && !(nread == 0 && eofOK)))
     629   [ #  #  #  #  :           0 :                 ereport(ERROR,
                   #  # ]
     630                 :             :                                 errcode_for_file_access(),
     631                 :             :                                 file->name ?
     632                 :             :                                 errmsg("could not read from file set \"%s\": read only %zu of %zu bytes",
     633                 :             :                                            file->name, nread, start_size) :
     634                 :             :                                 errmsg("could not read from temporary file: read only %zu of %zu bytes",
     635                 :             :                                            nread, start_size));
     636                 :             : 
     637                 :    10808930 :         return nread;
     638                 :     5404465 : }
     639                 :             : 
     640                 :             : /*
     641                 :             :  * Legacy interface where the caller needs to check for end of file or short
     642                 :             :  * reads.
     643                 :             :  */
     644                 :             : size_t
     645                 :           0 : BufFileRead(BufFile *file, void *ptr, size_t size)
     646                 :             : {
     647                 :           0 :         return BufFileReadCommon(file, ptr, size, false, false);
     648                 :             : }
     649                 :             : 
     650                 :             : /*
     651                 :             :  * Require read of exactly the specified size.
     652                 :             :  */
     653                 :             : void
     654                 :     3316558 : BufFileReadExact(BufFile *file, void *ptr, size_t size)
     655                 :             : {
     656                 :     3316558 :         BufFileReadCommon(file, ptr, size, true, false);
     657                 :     3316558 : }
     658                 :             : 
     659                 :             : /*
     660                 :             :  * Require read of exactly the specified size, but optionally allow end of
     661                 :             :  * file (in which case 0 is returned).
     662                 :             :  */
     663                 :             : size_t
     664                 :     2087907 : BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
     665                 :             : {
     666                 :     2087907 :         return BufFileReadCommon(file, ptr, size, true, eofOK);
     667                 :             : }
     668                 :             : 
     669                 :             : /*
     670                 :             :  * BufFileWrite
     671                 :             :  *
     672                 :             :  * Like fwrite() except we assume 1-byte element size and report errors via
     673                 :             :  * ereport().
     674                 :             :  */
     675                 :             : void
     676                 :     4197667 : BufFileWrite(BufFile *file, const void *ptr, size_t size)
     677                 :             : {
     678                 :     4197667 :         size_t          nthistime;
     679                 :             : 
     680         [ +  - ]:     4197667 :         Assert(!file->readOnly);
     681                 :             : 
     682         [ +  + ]:     8420888 :         while (size > 0)
     683                 :             :         {
     684         [ +  + ]:     4223221 :                 if (file->pos >= BLCKSZ)
     685                 :             :                 {
     686                 :             :                         /* Buffer full, dump it out */
     687         [ +  + ]:       29135 :                         if (file->dirty)
     688                 :       29057 :                                 BufFileDumpBuffer(file);
     689                 :             :                         else
     690                 :             :                         {
     691                 :             :                                 /* Hmm, went directly from reading to writing? */
     692                 :          78 :                                 file->curOffset += file->pos;
     693                 :          78 :                                 file->pos = 0;
     694                 :          78 :                                 file->nbytes = 0;
     695                 :             :                         }
     696                 :       29135 :                 }
     697                 :             : 
     698                 :     4223221 :                 nthistime = BLCKSZ - file->pos;
     699         [ +  + ]:     4223221 :                 if (nthistime > size)
     700                 :     4188225 :                         nthistime = size;
     701         [ +  - ]:     4223221 :                 Assert(nthistime > 0);
     702                 :             : 
     703                 :     4223221 :                 memcpy(file->buffer.data + file->pos, ptr, nthistime);
     704                 :             : 
     705                 :     4223221 :                 file->dirty = true;
     706                 :     4223221 :                 file->pos += nthistime;
     707         [ +  + ]:     4223221 :                 if (file->nbytes < file->pos)
     708                 :     4222577 :                         file->nbytes = file->pos;
     709                 :     4223221 :                 ptr = (const char *) ptr + nthistime;
     710                 :     4223221 :                 size -= nthistime;
     711                 :             :         }
     712                 :     4197667 : }
     713                 :             : 
     714                 :             : /*
     715                 :             :  * BufFileFlush
     716                 :             :  *
     717                 :             :  * Like fflush(), except that I/O errors are reported with ereport().
     718                 :             :  */
     719                 :             : static void
     720                 :     5414352 : BufFileFlush(BufFile *file)
     721                 :             : {
     722         [ +  + ]:     5414352 :         if (file->dirty)
     723                 :        6396 :                 BufFileDumpBuffer(file);
     724                 :             : 
     725         [ +  - ]:     5414352 :         Assert(!file->dirty);
     726                 :     5414352 : }
     727                 :             : 
     728                 :             : /*
     729                 :             :  * BufFileSeek
     730                 :             :  *
     731                 :             :  * Like fseek(), except that target position needs two values in order to
     732                 :             :  * work when logical filesize exceeds maximum value representable by pgoff_t.
     733                 :             :  * We do not support relative seeks across more than that, however.
     734                 :             :  * I/O errors are reported by ereport().
     735                 :             :  *
     736                 :             :  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
     737                 :             :  * impossible seek is attempted.
     738                 :             :  */
     739                 :             : int
     740                 :       15603 : BufFileSeek(BufFile *file, int fileno, pgoff_t offset, int whence)
     741                 :             : {
     742                 :       15603 :         int                     newFile;
     743                 :       15603 :         pgoff_t         newOffset;
     744                 :             : 
     745   [ -  +  -  - ]:       15603 :         switch (whence)
     746                 :             :         {
     747                 :             :                 case SEEK_SET:
     748         [ +  - ]:       15603 :                         if (fileno < 0)
     749                 :           0 :                                 return EOF;
     750                 :       15603 :                         newFile = fileno;
     751                 :       15603 :                         newOffset = offset;
     752                 :       15603 :                         break;
     753                 :             :                 case SEEK_CUR:
     754                 :             : 
     755                 :             :                         /*
     756                 :             :                          * Relative seek considers only the signed offset, ignoring
     757                 :             :                          * fileno.
     758                 :             :                          */
     759                 :           0 :                         newFile = file->curFile;
     760                 :           0 :                         newOffset = (file->curOffset + file->pos) + offset;
     761                 :           0 :                         break;
     762                 :             :                 case SEEK_END:
     763                 :             : 
     764                 :             :                         /*
     765                 :             :                          * The file size of the last file gives us the end offset of that
     766                 :             :                          * file.
     767                 :             :                          */
     768                 :           0 :                         newFile = file->numFiles - 1;
     769                 :           0 :                         newOffset = FileSize(file->files[file->numFiles - 1]);
     770         [ #  # ]:           0 :                         if (newOffset < 0)
     771   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     772                 :             :                                                 (errcode_for_file_access(),
     773                 :             :                                                  errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
     774                 :             :                                                                 FilePathName(file->files[file->numFiles - 1]),
     775                 :             :                                                                 file->name)));
     776                 :           0 :                         break;
     777                 :             :                 default:
     778   [ #  #  #  # ]:           0 :                         elog(ERROR, "invalid whence: %d", whence);
     779                 :           0 :                         return EOF;
     780                 :             :         }
     781         [ -  + ]:       15603 :         while (newOffset < 0)
     782                 :             :         {
     783         [ #  # ]:           0 :                 if (--newFile < 0)
     784                 :           0 :                         return EOF;
     785                 :           0 :                 newOffset += MAX_PHYSICAL_FILESIZE;
     786                 :             :         }
     787         [ +  + ]:       15603 :         if (newFile == file->curFile &&
     788   [ +  +  +  + ]:       15575 :                 newOffset >= file->curOffset &&
     789                 :       10916 :                 newOffset <= file->curOffset + file->nbytes)
     790                 :             :         {
     791                 :             :                 /*
     792                 :             :                  * Seek is to a point within existing buffer; we can just adjust
     793                 :             :                  * pos-within-buffer, without flushing buffer.  Note this is OK
     794                 :             :                  * whether reading or writing, but buffer remains dirty if we were
     795                 :             :                  * writing.
     796                 :             :                  */
     797                 :        7136 :                 file->pos = (int64) (newOffset - file->curOffset);
     798                 :        7136 :                 return 0;
     799                 :             :         }
     800                 :             :         /* Otherwise, must reposition buffer, so flush any dirty data */
     801                 :        8467 :         BufFileFlush(file);
     802                 :             : 
     803                 :             :         /*
     804                 :             :          * At this point and no sooner, check for seek past last segment. The
     805                 :             :          * above flush could have created a new segment, so checking sooner would
     806                 :             :          * not work (at least not with this code).
     807                 :             :          */
     808                 :             : 
     809                 :             :         /* convert seek to "start of next seg" to "end of last seg" */
     810   [ -  +  #  # ]:        8467 :         if (newFile == file->numFiles && newOffset == 0)
     811                 :             :         {
     812                 :           0 :                 newFile--;
     813                 :           0 :                 newOffset = MAX_PHYSICAL_FILESIZE;
     814                 :           0 :         }
     815         [ -  + ]:        8467 :         while (newOffset > MAX_PHYSICAL_FILESIZE)
     816                 :             :         {
     817         [ #  # ]:           0 :                 if (++newFile >= file->numFiles)
     818                 :           0 :                         return EOF;
     819                 :           0 :                 newOffset -= MAX_PHYSICAL_FILESIZE;
     820                 :             :         }
     821         [ -  + ]:        8467 :         if (newFile >= file->numFiles)
     822                 :           0 :                 return EOF;
     823                 :             :         /* Seek is OK! */
     824                 :        8467 :         file->curFile = newFile;
     825                 :        8467 :         file->curOffset = newOffset;
     826                 :        8467 :         file->pos = 0;
     827                 :        8467 :         file->nbytes = 0;
     828                 :        8467 :         return 0;
     829                 :       15603 : }
     830                 :             : 
     831                 :             : void
     832                 :          90 : BufFileTell(BufFile *file, int *fileno, pgoff_t *offset)
     833                 :             : {
     834                 :          90 :         *fileno = file->curFile;
     835                 :          90 :         *offset = file->curOffset + file->pos;
     836                 :          90 : }
     837                 :             : 
     838                 :             : /*
     839                 :             :  * BufFileSeekBlock --- block-oriented seek
     840                 :             :  *
     841                 :             :  * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
     842                 :             :  * the file.  Note that users of this interface will fail if their files
     843                 :             :  * exceed BLCKSZ * PG_INT64_MAX bytes, but that is quite a lot; we don't
     844                 :             :  * work with tables bigger than that, either...
     845                 :             :  *
     846                 :             :  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
     847                 :             :  * impossible seek is attempted.
     848                 :             :  */
     849                 :             : int
     850                 :       15142 : BufFileSeekBlock(BufFile *file, int64 blknum)
     851                 :             : {
     852                 :       30284 :         return BufFileSeek(file,
     853                 :       15142 :                                            (int) (blknum / BUFFILE_SEG_SIZE),
     854                 :       15142 :                                            (pgoff_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
     855                 :             :                                            SEEK_SET);
     856                 :             : }
     857                 :             : 
     858                 :             : /*
     859                 :             :  * Returns the amount of data in the given BufFile, in bytes.
     860                 :             :  *
     861                 :             :  * Returned value includes the size of any holes left behind by BufFileAppend.
     862                 :             :  * ereport()s on failure.
     863                 :             :  */
     864                 :             : int64
     865                 :          58 : BufFileSize(BufFile *file)
     866                 :             : {
     867                 :          58 :         int64           lastFileSize;
     868                 :             : 
     869                 :             :         /* Get the size of the last physical file. */
     870                 :          58 :         lastFileSize = FileSize(file->files[file->numFiles - 1]);
     871         [ +  - ]:          58 :         if (lastFileSize < 0)
     872   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     873                 :             :                                 (errcode_for_file_access(),
     874                 :             :                                  errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
     875                 :             :                                                 FilePathName(file->files[file->numFiles - 1]),
     876                 :             :                                                 file->name)));
     877                 :             : 
     878                 :         174 :         return ((file->numFiles - 1) * (int64) MAX_PHYSICAL_FILESIZE) +
     879                 :          58 :                 lastFileSize;
     880                 :          58 : }
     881                 :             : 
     882                 :             : /*
     883                 :             :  * Append the contents of the source file to the end of the target file.
     884                 :             :  *
     885                 :             :  * Note that operation subsumes ownership of underlying resources from
     886                 :             :  * "source".  Caller should never call BufFileClose against source having
     887                 :             :  * called here first.  Resource owners for source and target must match,
     888                 :             :  * too.
     889                 :             :  *
     890                 :             :  * This operation works by manipulating lists of segment files, so the
     891                 :             :  * file content is always appended at a MAX_PHYSICAL_FILESIZE-aligned
     892                 :             :  * boundary, typically creating empty holes before the boundary.  These
     893                 :             :  * areas do not contain any interesting data, and cannot be read from by
     894                 :             :  * caller.
     895                 :             :  *
     896                 :             :  * Returns the block number within target where the contents of source
     897                 :             :  * begins.  Caller should apply this as an offset when working off block
     898                 :             :  * positions that are in terms of the original BufFile space.
     899                 :             :  */
     900                 :             : int64
     901                 :          28 : BufFileAppend(BufFile *target, BufFile *source)
     902                 :             : {
     903                 :          28 :         int64           startBlock = (int64) target->numFiles * BUFFILE_SEG_SIZE;
     904                 :          28 :         int                     newNumFiles = target->numFiles + source->numFiles;
     905                 :          28 :         int                     i;
     906                 :             : 
     907         [ +  - ]:          28 :         Assert(source->readOnly);
     908         [ +  - ]:          28 :         Assert(!source->dirty);
     909                 :             : 
     910         [ +  - ]:          28 :         if (target->resowner != source->resowner)
     911   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not append BufFile with non-matching resource owner");
     912                 :             : 
     913                 :          28 :         target->files = (File *)
     914                 :          28 :                 repalloc(target->files, sizeof(File) * newNumFiles);
     915         [ +  + ]:          56 :         for (i = target->numFiles; i < newNumFiles; i++)
     916                 :          28 :                 target->files[i] = source->files[i - target->numFiles];
     917                 :          28 :         target->numFiles = newNumFiles;
     918                 :             : 
     919                 :          56 :         return startBlock;
     920                 :          28 : }
     921                 :             : 
     922                 :             : /*
     923                 :             :  * Truncate a BufFile created by BufFileCreateFileSet up to the given fileno
     924                 :             :  * and the offset.
     925                 :             :  */
     926                 :             : void
     927                 :           0 : BufFileTruncateFileSet(BufFile *file, int fileno, pgoff_t offset)
     928                 :             : {
     929                 :           0 :         int                     numFiles = file->numFiles;
     930                 :           0 :         int                     newFile = fileno;
     931                 :           0 :         pgoff_t         newOffset = file->curOffset;
     932                 :           0 :         char            segment_name[MAXPGPATH];
     933                 :           0 :         int                     i;
     934                 :             : 
     935                 :             :         /*
     936                 :             :          * Loop over all the files up to the given fileno and remove the files
     937                 :             :          * that are greater than the fileno and truncate the given file up to the
     938                 :             :          * offset. Note that we also remove the given fileno if the offset is 0
     939                 :             :          * provided it is not the first file in which we truncate it.
     940                 :             :          */
     941         [ #  # ]:           0 :         for (i = file->numFiles - 1; i >= fileno; i--)
     942                 :             :         {
     943   [ #  #  #  # ]:           0 :                 if ((i != fileno || offset == 0) && i != 0)
     944                 :             :                 {
     945                 :           0 :                         FileSetSegmentName(segment_name, file->name, i);
     946                 :           0 :                         FileClose(file->files[i]);
     947         [ #  # ]:           0 :                         if (!FileSetDelete(file->fileset, segment_name, true))
     948   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     949                 :             :                                                 (errcode_for_file_access(),
     950                 :             :                                                  errmsg("could not delete fileset \"%s\": %m",
     951                 :             :                                                                 segment_name)));
     952                 :           0 :                         numFiles--;
     953                 :           0 :                         newOffset = MAX_PHYSICAL_FILESIZE;
     954                 :             : 
     955                 :             :                         /*
     956                 :             :                          * This is required to indicate that we have deleted the given
     957                 :             :                          * fileno.
     958                 :             :                          */
     959         [ #  # ]:           0 :                         if (i == fileno)
     960                 :           0 :                                 newFile--;
     961                 :           0 :                 }
     962                 :             :                 else
     963                 :             :                 {
     964                 :           0 :                         if (FileTruncate(file->files[i], offset,
     965         [ #  # ]:           0 :                                                          WAIT_EVENT_BUFFILE_TRUNCATE) < 0)
     966   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     967                 :             :                                                 (errcode_for_file_access(),
     968                 :             :                                                  errmsg("could not truncate file \"%s\": %m",
     969                 :             :                                                                 FilePathName(file->files[i]))));
     970                 :           0 :                         newOffset = offset;
     971                 :             :                 }
     972                 :           0 :         }
     973                 :             : 
     974                 :           0 :         file->numFiles = numFiles;
     975                 :             : 
     976                 :             :         /*
     977                 :             :          * If the truncate point is within existing buffer then we can just adjust
     978                 :             :          * pos within buffer.
     979                 :             :          */
     980         [ #  # ]:           0 :         if (newFile == file->curFile &&
     981   [ #  #  #  # ]:           0 :                 newOffset >= file->curOffset &&
     982                 :           0 :                 newOffset <= file->curOffset + file->nbytes)
     983                 :             :         {
     984                 :             :                 /* No need to reset the current pos if the new pos is greater. */
     985         [ #  # ]:           0 :                 if (newOffset <= file->curOffset + file->pos)
     986                 :           0 :                         file->pos = (int64) newOffset - file->curOffset;
     987                 :             : 
     988                 :             :                 /* Adjust the nbytes for the current buffer. */
     989                 :           0 :                 file->nbytes = (int64) newOffset - file->curOffset;
     990                 :           0 :         }
     991   [ #  #  #  # ]:           0 :         else if (newFile == file->curFile &&
     992                 :           0 :                          newOffset < file->curOffset)
     993                 :             :         {
     994                 :             :                 /*
     995                 :             :                  * The truncate point is within the existing file but prior to the
     996                 :             :                  * current position, so we can forget the current buffer and reset the
     997                 :             :                  * current position.
     998                 :             :                  */
     999                 :           0 :                 file->curOffset = newOffset;
    1000                 :           0 :                 file->pos = 0;
    1001                 :           0 :                 file->nbytes = 0;
    1002                 :           0 :         }
    1003         [ #  # ]:           0 :         else if (newFile < file->curFile)
    1004                 :             :         {
    1005                 :             :                 /*
    1006                 :             :                  * The truncate point is prior to the current file, so need to reset
    1007                 :             :                  * the current position accordingly.
    1008                 :             :                  */
    1009                 :           0 :                 file->curFile = newFile;
    1010                 :           0 :                 file->curOffset = newOffset;
    1011                 :           0 :                 file->pos = 0;
    1012                 :           0 :                 file->nbytes = 0;
    1013                 :           0 :         }
    1014                 :             :         /* Nothing to do, if the truncate point is beyond current file. */
    1015                 :           0 : }
        

Generated by: LCOV version 2.3.2-1