LCOV - differential code coverage report
Current view: top level - src/backend/storage/file - buffile.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 77.4 % 327 253 74 2 251 2
Current Date: 2024-04-14 14:21:10 Functions: 92.0 % 25 23 2 2 21 1
Baseline: 16@8cea358b128 Branches: 50.0 % 194 97 97 97
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (120,180] days: 100.0 % 2 2 2
(240..) days: 77.2 % 325 251 74 251
Function coverage date bins:
(120,180] days: 100.0 % 1 1 1
(240..) days: 91.7 % 24 22 2 1 21
Branch coverage date bins:
(240..) days: 50.0 % 194 97 97 97

 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-2024, 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                 :                :     off_t       curOffset;      /* offset part of current pos */
                                 96                 :                :     int         pos;            /* next read/write position in buffer */
                                 97                 :                :     int         nbytes;         /* total # of valid bytes in buffer */
                                 98                 :                : 
                                 99                 :                :     /*
                                100                 :                :      * XXX Should ideally us 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 *
 2129 ishii@postgresql.org      118                 :CBC        6142 : makeBufFileCommon(int nfiles)
                                119                 :                : {
 8768 bruce@momjian.us          120                 :           6142 :     BufFile    *file = (BufFile *) palloc(sizeof(BufFile));
                                121                 :                : 
 2129 ishii@postgresql.org      122                 :           6142 :     file->numFiles = nfiles;
 6160 tgl@sss.pgh.pa.us         123                 :           6142 :     file->isInterXact = false;
 8947                           124                 :           6142 :     file->dirty = false;
 3817                           125                 :           6142 :     file->resowner = CurrentResourceOwner;
 8947                           126                 :           6142 :     file->curFile = 0;
  382 peter@eisentraut.org      127                 :           6142 :     file->curOffset = 0;
 8947 tgl@sss.pgh.pa.us         128                 :           6142 :     file->pos = 0;
                                129                 :           6142 :     file->nbytes = 0;
                                130                 :                : 
 2129 ishii@postgresql.org      131                 :           6142 :     return file;
                                132                 :                : }
                                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                 :           1927 : makeBufFile(File firstfile)
                                140                 :                : {
                                141                 :           1927 :     BufFile    *file = makeBufFileCommon(1);
                                142                 :                : 
                                143                 :           1927 :     file->files = (File *) palloc(sizeof(File));
                                144                 :           1927 :     file->files[0] = firstfile;
 2326 andres@anarazel.de        145                 :           1927 :     file->readOnly = false;
                                146                 :           1927 :     file->fileset = NULL;
                                147                 :           1927 :     file->name = NULL;
                                148                 :                : 
 8950 tgl@sss.pgh.pa.us         149                 :           1927 :     return file;
                                150                 :                : }
                                151                 :                : 
                                152                 :                : /*
                                153                 :                :  * Add another component temp file.
                                154                 :                :  */
                                155                 :                : static void
 8947 tgl@sss.pgh.pa.us         156                 :UBC           0 : extendBufFile(BufFile *file)
                                157                 :                : {
                                158                 :                :     File        pfile;
                                159                 :                :     ResourceOwner oldowner;
                                160                 :                : 
                                161                 :                :     /* Be sure to associate the file with the BufFile's resource owner */
 3817                           162                 :              0 :     oldowner = CurrentResourceOwner;
                                163                 :              0 :     CurrentResourceOwner = file->resowner;
                                164                 :                : 
 2326 andres@anarazel.de        165         [ #  # ]:              0 :     if (file->fileset == NULL)
                                166                 :              0 :         pfile = OpenTemporaryFile(file->isInterXact);
                                167                 :                :     else
  958 akapila@postgresql.o      168                 :              0 :         pfile = MakeNewFileSetSegment(file, file->numFiles);
                                169                 :                : 
 8950 tgl@sss.pgh.pa.us         170         [ #  # ]:              0 :     Assert(pfile >= 0);
                                171                 :                : 
 3817                           172                 :              0 :     CurrentResourceOwner = oldowner;
                                173                 :                : 
 8950                           174                 :              0 :     file->files = (File *) repalloc(file->files,
 8768 bruce@momjian.us          175                 :              0 :                                     (file->numFiles + 1) * sizeof(File));
 8950 tgl@sss.pgh.pa.us         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 *
 6156 tgl@sss.pgh.pa.us         193                 :CBC        1927 : 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                 :                :      */
 1793                           207                 :           1927 :     PrepareTempTablespaces();
                                208                 :                : 
 6156                           209                 :           1927 :     pfile = OpenTemporaryFile(interXact);
 8950                           210         [ -  + ]:           1927 :     Assert(pfile >= 0);
                                211                 :                : 
 8947                           212                 :           1927 :     file = makeBufFile(pfile);
 7656                           213                 :           1927 :     file->isInterXact = interXact;
                                214                 :                : 
 8947                           215                 :           1927 :     return file;
                                216                 :                : }
                                217                 :                : 
                                218                 :                : /*
                                219                 :                :  * Build the name for a given segment of a given BufFile.
                                220                 :                :  */
                                221                 :                : static void
  958 akapila@postgresql.o      222                 :           9142 : FileSetSegmentName(char *name, const char *buffile_name, int segment)
                                223                 :                : {
 2326 andres@anarazel.de        224                 :           9142 :     snprintf(name, MAXPGPATH, "%s.%d", buffile_name, segment);
                                225                 :           9142 : }
                                226                 :                : 
                                227                 :                : /*
                                228                 :                :  * Create a new segment file backing a fileset based BufFile.
                                229                 :                :  */
                                230                 :                : static File
  958 akapila@postgresql.o      231                 :           1872 : 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                 :           1872 :     FileSetSegmentName(name, buffile->name, segment + 1);
                                243                 :           1872 :     FileSetDelete(buffile->fileset, name, true);
                                244                 :                : 
                                245                 :                :     /* Create the new segment. */
                                246                 :           1872 :     FileSetSegmentName(name, buffile->name, segment);
                                247                 :           1872 :     file = FileSetCreate(buffile->fileset, name);
                                248                 :                : 
                                249                 :                :     /* FileSetCreate would've errored out */
 2326 andres@anarazel.de        250         [ -  + ]:           1871 :     Assert(file > 0);
                                251                 :                : 
                                252                 :           1871 :     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 *
  958 akapila@postgresql.o      267                 :           1872 : BufFileCreateFileSet(FileSet *fileset, const char *name)
                                268                 :                : {
                                269                 :                :     BufFile    *file;
                                270                 :                : 
 2129 ishii@postgresql.org      271                 :           1872 :     file = makeBufFileCommon(1);
 2326 andres@anarazel.de        272                 :           1872 :     file->fileset = fileset;
                                273                 :           1872 :     file->name = pstrdup(name);
                                274                 :           1872 :     file->files = (File *) palloc(sizeof(File));
  958 akapila@postgresql.o      275                 :           1872 :     file->files[0] = MakeNewFileSetSegment(file, 0);
 2326 andres@anarazel.de        276                 :           1871 :     file->readOnly = false;
                                277                 :                : 
                                278                 :           1871 :     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 *
  955 akapila@postgresql.o      291                 :           2632 : BufFileOpenFileSet(FileSet *fileset, const char *name, int mode,
                                292                 :                :                    bool missing_ok)
                                293                 :                : {
                                294                 :                :     BufFile    *file;
                                295                 :                :     char        segment_name[MAXPGPATH];
 2326 andres@anarazel.de        296                 :           2632 :     Size        capacity = 16;
                                297                 :                :     File       *files;
                                298                 :           2632 :     int         nfiles = 0;
                                299                 :                : 
                                300                 :           2632 :     files = palloc(sizeof(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. */
                                309         [ -  + ]:           4975 :         if (nfiles + 1 > capacity)
                                310                 :                :         {
 2326 andres@anarazel.de        311                 :UBC           0 :             capacity *= 2;
                                312                 :              0 :             files = repalloc(files, sizeof(File) * capacity);
                                313                 :                :         }
                                314                 :                :         /* Try to load a segment. */
  958 akapila@postgresql.o      315                 :CBC        4975 :         FileSetSegmentName(segment_name, name, nfiles);
                                316                 :           4975 :         files[nfiles] = FileSetOpen(fileset, segment_name, mode);
 2326 andres@anarazel.de        317         [ +  + ]:           4975 :         if (files[nfiles] <= 0)
                                318                 :           2632 :             break;
                                319                 :           2343 :         ++nfiles;
                                320                 :                : 
                                321         [ -  + ]:           2343 :         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         [ +  + ]:           2632 :     if (nfiles == 0)
                                329                 :                :     {
                                330                 :                :         /* free the memory */
  955 akapila@postgresql.o      331                 :            289 :         pfree(files);
                                332                 :                : 
                                333         [ +  - ]:            289 :         if (missing_ok)
                                334                 :            289 :             return NULL;
                                335                 :                : 
 2314 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                 :                : 
 2129 ishii@postgresql.org      342                 :CBC        2343 :     file = makeBufFileCommon(nfiles);
 2326 andres@anarazel.de        343                 :           2343 :     file->files = files;
  949 michael@paquier.xyz       344                 :           2343 :     file->readOnly = (mode == O_RDONLY);
 2326 andres@anarazel.de        345                 :           2343 :     file->fileset = fileset;
                                346                 :           2343 :     file->name = pstrdup(name);
                                347                 :                : 
                                348                 :           2343 :     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
  955 akapila@postgresql.o      364                 :            380 : BufFileDeleteFileSet(FileSet *fileset, const char *name, bool missing_ok)
                                365                 :                : {
                                366                 :                :     char        segment_name[MAXPGPATH];
 2326 andres@anarazel.de        367                 :            380 :     int         segment = 0;
                                368                 :            380 :     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                 :                :     {
  958 akapila@postgresql.o      377                 :            423 :         FileSetSegmentName(segment_name, name, segment);
                                378         [ +  + ]:            423 :         if (!FileSetDelete(fileset, segment_name, true))
 2326 andres@anarazel.de        379                 :            380 :             break;
                                380                 :             43 :         found = true;
                                381                 :             43 :         ++segment;
                                382                 :                : 
                                383         [ -  + ]:             43 :         CHECK_FOR_INTERRUPTS();
                                384                 :                :     }
                                385                 :                : 
  955 akapila@postgresql.o      386   [ +  +  -  + ]:            380 :     if (!found && !missing_ok)
  958 akapila@postgresql.o      387         [ #  # ]:UBC           0 :         elog(ERROR, "could not delete unknown BufFile \"%s\"", name);
 2326 andres@anarazel.de        388                 :CBC         380 : }
                                389                 :                : 
                                390                 :                : /*
                                391                 :                :  * BufFileExportFileSet --- flush and make read-only, in preparation for sharing.
                                392                 :                :  */
                                393                 :                : void
  958 akapila@postgresql.o      394                 :            210 : BufFileExportFileSet(BufFile *file)
                                395                 :                : {
                                396                 :                :     /* Must be a file belonging to a FileSet. */
 2326 andres@anarazel.de        397         [ -  + ]:            210 :     Assert(file->fileset != NULL);
                                398                 :                : 
                                399                 :                :     /* It's probably a bug if someone calls this twice. */
                                400         [ -  + ]:            210 :     Assert(!file->readOnly);
                                401                 :                : 
                                402                 :            210 :     BufFileFlush(file);
                                403                 :            210 :     file->readOnly = true;
                                404                 :            210 : }
                                405                 :                : 
                                406                 :                : /*
                                407                 :                :  * Close a BufFile
                                408                 :                :  *
                                409                 :                :  * Like fclose(), this also implicitly FileCloses the underlying File.
                                410                 :                :  */
                                411                 :                : void
 8950 tgl@sss.pgh.pa.us         412                 :           6058 : BufFileClose(BufFile *file)
                                413                 :                : {
                                414                 :                :     int         i;
                                415                 :                : 
                                416                 :                :     /* flush any unwritten data */
                                417                 :           6058 :     BufFileFlush(file);
                                418                 :                :     /* close and delete the underlying file(s) */
 8947                           419         [ +  + ]:          12190 :     for (i = 0; i < file->numFiles; i++)
                                420                 :           6132 :         FileClose(file->files[i]);
                                421                 :                :     /* release the buffer space */
                                422                 :           6058 :     pfree(file->files);
 8950                           423                 :           6058 :     pfree(file);
                                424                 :           6058 : }
                                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                 :          53371 : BufFileLoadBuffer(BufFile *file)
                                435                 :                : {
                                436                 :                :     File        thisfile;
                                437                 :                :     instr_time  io_start;
                                438                 :                :     instr_time  io_time;
                                439                 :                : 
                                440                 :                :     /*
                                441                 :                :      * Advance to next component file if necessary and possible.
                                442                 :                :      */
                                443         [ -  + ]:          53371 :     if (file->curOffset >= MAX_PHYSICAL_FILESIZE &&
 8768 bruce@momjian.us          444         [ #  # ]:UBC           0 :         file->curFile + 1 < file->numFiles)
                                445                 :                :     {
 8950 tgl@sss.pgh.pa.us         446                 :              0 :         file->curFile++;
  382 peter@eisentraut.org      447                 :              0 :         file->curOffset = 0;
                                448                 :                :     }
                                449                 :                : 
  737 michael@paquier.xyz       450                 :CBC       53371 :     thisfile = file->files[file->curFile];
                                451                 :                : 
                                452         [ -  + ]:          53371 :     if (track_io_timing)
  737 michael@paquier.xyz       453                 :UBC           0 :         INSTR_TIME_SET_CURRENT(io_start);
                                454                 :                :     else
  450 andres@anarazel.de        455                 :CBC       53371 :         INSTR_TIME_SET_ZERO(io_start);
                                456                 :                : 
                                457                 :                :     /*
                                458                 :                :      * Read whatever we can get, up to a full bufferload.
                                459                 :                :      */
 2584 rhaas@postgresql.org      460                 :         106742 :     file->nbytes = FileRead(thisfile,
 2052 tgl@sss.pgh.pa.us         461                 :          53371 :                             file->buffer.data,
                                462                 :                :                             sizeof(file->buffer),
                                463                 :                :                             file->curOffset,
                                464                 :                :                             WAIT_EVENT_BUFFILE_READ);
 8950                           465         [ -  + ]:          53371 :     if (file->nbytes < 0)
                                466                 :                :     {
 8950 tgl@sss.pgh.pa.us         467                 :UBC           0 :         file->nbytes = 0;
 1398 tmunro@postgresql.or      468         [ #  # ]:              0 :         ereport(ERROR,
                                469                 :                :                 (errcode_for_file_access(),
                                470                 :                :                  errmsg("could not read file \"%s\": %m",
                                471                 :                :                         FilePathName(thisfile))));
                                472                 :                :     }
                                473                 :                : 
  737 michael@paquier.xyz       474         [ -  + ]:CBC       53371 :     if (track_io_timing)
                                475                 :                :     {
  737 michael@paquier.xyz       476                 :UBC           0 :         INSTR_TIME_SET_CURRENT(io_time);
  381 andres@anarazel.de        477                 :              0 :         INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_read_time, io_time, io_start);
                                478                 :                :     }
                                479                 :                : 
                                480                 :                :     /* we choose not to advance curOffset here */
                                481                 :                : 
 2357 rhaas@postgresql.org      482         [ +  + ]:CBC       53371 :     if (file->nbytes > 0)
                                483                 :          51662 :         pgBufferUsage.temp_blks_read++;
 8950 tgl@sss.pgh.pa.us         484                 :          53371 : }
                                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                 :          59703 : BufFileDumpBuffer(BufFile *file)
                                495                 :                : {
                                496                 :          59703 :     int         wpos = 0;
                                497                 :                :     int         bytestowrite;
                                498                 :                :     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         [ +  + ]:         119406 :     while (wpos < file->nbytes)
                                505                 :                :     {
                                506                 :                :         off_t       availbytes;
                                507                 :                :         instr_time  io_start;
                                508                 :                :         instr_time  io_time;
                                509                 :                : 
                                510                 :                :         /*
                                511                 :                :          * Advance to next component file if necessary and possible.
                                512                 :                :          */
 2341 andres@anarazel.de        513         [ -  + ]:          59703 :         if (file->curOffset >= MAX_PHYSICAL_FILESIZE)
                                514                 :                :         {
 8768 bruce@momjian.us          515         [ #  # ]:UBC           0 :             while (file->curFile + 1 >= file->numFiles)
 8947 tgl@sss.pgh.pa.us         516                 :              0 :                 extendBufFile(file);
 8950                           517                 :              0 :             file->curFile++;
  382 peter@eisentraut.org      518                 :              0 :             file->curOffset = 0;
                                519                 :                :         }
                                520                 :                : 
                                521                 :                :         /*
                                522                 :                :          * Determine how much we need to write into this file.
                                523                 :                :          */
 8950 tgl@sss.pgh.pa.us         524                 :CBC       59703 :         bytestowrite = file->nbytes - wpos;
 2341 andres@anarazel.de        525                 :          59703 :         availbytes = MAX_PHYSICAL_FILESIZE - file->curOffset;
                                526                 :                : 
                                527         [ -  + ]:          59703 :         if ((off_t) bytestowrite > availbytes)
 2341 andres@anarazel.de        528                 :UBC           0 :             bytestowrite = (int) availbytes;
                                529                 :                : 
 8947 tgl@sss.pgh.pa.us         530                 :CBC       59703 :         thisfile = file->files[file->curFile];
                                531                 :                : 
  737 michael@paquier.xyz       532         [ -  + ]:          59703 :         if (track_io_timing)
  737 michael@paquier.xyz       533                 :UBC           0 :             INSTR_TIME_SET_CURRENT(io_start);
                                534                 :                :         else
  450 andres@anarazel.de        535                 :CBC       59703 :             INSTR_TIME_SET_ZERO(io_start);
                                536                 :                : 
 2584 rhaas@postgresql.org      537                 :         119406 :         bytestowrite = FileWrite(thisfile,
 2052 tgl@sss.pgh.pa.us         538                 :          59703 :                                  file->buffer.data + wpos,
                                539                 :                :                                  bytestowrite,
                                540                 :                :                                  file->curOffset,
                                541                 :                :                                  WAIT_EVENT_BUFFILE_WRITE);
 8950                           542         [ -  + ]:          59703 :         if (bytestowrite <= 0)
 1398 tmunro@postgresql.or      543         [ #  # ]:UBC           0 :             ereport(ERROR,
                                544                 :                :                     (errcode_for_file_access(),
                                545                 :                :                      errmsg("could not write to file \"%s\": %m",
                                546                 :                :                             FilePathName(thisfile))));
                                547                 :                : 
  737 michael@paquier.xyz       548         [ -  + ]:CBC       59703 :         if (track_io_timing)
                                549                 :                :         {
  737 michael@paquier.xyz       550                 :UBC           0 :             INSTR_TIME_SET_CURRENT(io_time);
  381 andres@anarazel.de        551                 :              0 :             INSTR_TIME_ACCUM_DIFF(pgBufferUsage.temp_blk_write_time, io_time, io_start);
                                552                 :                :         }
                                553                 :                : 
 8950 tgl@sss.pgh.pa.us         554                 :CBC       59703 :         file->curOffset += bytestowrite;
                                555                 :          59703 :         wpos += bytestowrite;
                                556                 :                : 
 5234 rhaas@postgresql.org      557                 :          59703 :         pgBufferUsage.temp_blks_written++;
                                558                 :                :     }
 8950 tgl@sss.pgh.pa.us         559                 :          59703 :     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                 :          59703 :     file->curOffset -= (file->nbytes - file->pos);
                                568         [ -  + ]:          59703 :     if (file->curOffset < 0)  /* handle possible segment crossing */
                                569                 :                :     {
 8950 tgl@sss.pgh.pa.us         570                 :UBC           0 :         file->curFile--;
                                571         [ #  # ]:              0 :         Assert(file->curFile >= 0);
                                572                 :              0 :         file->curOffset += MAX_PHYSICAL_FILESIZE;
                                573                 :                :     }
                                574                 :                : 
                                575                 :                :     /*
                                576                 :                :      * Now we can set the buffer empty without changing the logical position
                                577                 :                :      */
 8950 tgl@sss.pgh.pa.us         578                 :CBC       59703 :     file->pos = 0;
                                579                 :          59703 :     file->nbytes = 0;
                                580                 :          59703 : }
                                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
  454 peter@eisentraut.org      593                 :       16483484 : BufFileReadCommon(BufFile *file, void *ptr, size_t size, bool exact, bool eofOK)
                                594                 :                : {
                                595                 :       16483484 :     size_t      start_size = size;
 8950 tgl@sss.pgh.pa.us         596                 :       16483484 :     size_t      nread = 0;
                                597                 :                :     size_t      nthistime;
                                598                 :                : 
 1398 tmunro@postgresql.or      599                 :       16483484 :     BufFileFlush(file);
                                600                 :                : 
 8950 tgl@sss.pgh.pa.us         601         [ +  + ]:       32981012 :     while (size > 0)
                                602                 :                :     {
                                603         [ +  + ]:       16499237 :         if (file->pos >= file->nbytes)
                                604                 :                :         {
                                605                 :                :             /* Try to load more data into buffer. */
                                606                 :          53371 :             file->curOffset += file->pos;
                                607                 :          53371 :             file->pos = 0;
                                608                 :          53371 :             file->nbytes = 0;
                                609                 :          53371 :             BufFileLoadBuffer(file);
                                610         [ +  + ]:          53371 :             if (file->nbytes <= 0)
                                611                 :           1709 :                 break;          /* no more data available */
                                612                 :                :         }
                                613                 :                : 
                                614                 :       16497528 :         nthistime = file->nbytes - file->pos;
                                615         [ +  + ]:       16497528 :         if (nthistime > size)
                                616                 :       16447872 :             nthistime = size;
                                617         [ -  + ]:       16497528 :         Assert(nthistime > 0);
                                618                 :                : 
 2052                           619                 :       16497528 :         memcpy(ptr, file->buffer.data + file->pos, nthistime);
                                620                 :                : 
 8950                           621                 :       16497528 :         file->pos += nthistime;
  471 peter@eisentraut.org      622                 :       16497528 :         ptr = (char *) ptr + nthistime;
 8950 tgl@sss.pgh.pa.us         623                 :       16497528 :         size -= nthistime;
                                624                 :       16497528 :         nread += nthistime;
                                625                 :                :     }
                                626                 :                : 
  454 peter@eisentraut.org      627   [ +  -  +  + ]:       16483484 :     if (exact &&
                                628   [ +  -  -  + ]:           1709 :         (nread != start_size && !(nread == 0 && eofOK)))
  454 peter@eisentraut.org      629   [ #  #  #  # ]:UBC           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                 :                : 
 8950 tgl@sss.pgh.pa.us         637                 :CBC    16483484 :     return nread;
                                638                 :                : }
                                639                 :                : 
                                640                 :                : /*
                                641                 :                :  * Legacy interface where the caller needs to check for end of file or short
                                642                 :                :  * reads.
                                643                 :                :  */
                                644                 :                : size_t
  454 peter@eisentraut.org      645                 :UBC           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
  454 peter@eisentraut.org      654                 :CBC    10258830 : BufFileReadExact(BufFile *file, void *ptr, size_t size)
                                655                 :                : {
                                656                 :       10258830 :     BufFileReadCommon(file, ptr, size, true, false);
                                657                 :       10258830 : }
                                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                 :        6224654 : BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
                                665                 :                : {
                                666                 :        6224654 :     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
  471                           676                 :       14112025 : BufFileWrite(BufFile *file, const void *ptr, size_t size)
                                677                 :                : {
                                678                 :                :     size_t      nthistime;
                                679                 :                : 
 2326 andres@anarazel.de        680         [ -  + ]:       14112025 :     Assert(!file->readOnly);
                                681                 :                : 
 8950 tgl@sss.pgh.pa.us         682         [ +  + ]:       28249557 :     while (size > 0)
                                683                 :                :     {
                                684         [ +  + ]:       14137532 :         if (file->pos >= BLCKSZ)
                                685                 :                :         {
                                686                 :                :             /* Buffer full, dump it out */
                                687         [ +  + ]:          39336 :             if (file->dirty)
                                688                 :          39093 :                 BufFileDumpBuffer(file);
                                689                 :                :             else
                                690                 :                :             {
                                691                 :                :                 /* Hmm, went directly from reading to writing? */
                                692                 :            243 :                 file->curOffset += file->pos;
                                693                 :            243 :                 file->pos = 0;
                                694                 :            243 :                 file->nbytes = 0;
                                695                 :                :             }
                                696                 :                :         }
                                697                 :                : 
                                698                 :       14137532 :         nthistime = BLCKSZ - file->pos;
                                699         [ +  + ]:       14137532 :         if (nthistime > size)
                                700                 :       14080111 :             nthistime = size;
                                701         [ -  + ]:       14137532 :         Assert(nthistime > 0);
                                702                 :                : 
 2052                           703                 :       14137532 :         memcpy(file->buffer.data + file->pos, ptr, nthistime);
                                704                 :                : 
 8950                           705                 :       14137532 :         file->dirty = true;
                                706                 :       14137532 :         file->pos += nthistime;
                                707         [ +  + ]:       14137532 :         if (file->nbytes < file->pos)
                                708                 :       14135600 :             file->nbytes = file->pos;
  471 peter@eisentraut.org      709                 :       14137532 :         ptr = (const char *) ptr + nthistime;
 8950 tgl@sss.pgh.pa.us         710                 :       14137532 :         size -= nthistime;
                                711                 :                :     }
                                712                 :       14112025 : }
                                713                 :                : 
                                714                 :                : /*
                                715                 :                :  * BufFileFlush
                                716                 :                :  *
                                717                 :                :  * Like fflush(), except that I/O errors are reported with ereport().
                                718                 :                :  */
                                719                 :                : static void
                                720                 :       16515929 : BufFileFlush(BufFile *file)
                                721                 :                : {
                                722         [ +  + ]:       16515929 :     if (file->dirty)
                                723                 :          20610 :         BufFileDumpBuffer(file);
                                724                 :                : 
 1398 tmunro@postgresql.or      725         [ -  + ]:       16515929 :     Assert(!file->dirty);
 8950 tgl@sss.pgh.pa.us         726                 :       16515929 : }
                                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 off_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
 5879                           740                 :          53559 : BufFileSeek(BufFile *file, int fileno, off_t offset, int whence)
                                741                 :                : {
                                742                 :                :     int         newFile;
                                743                 :                :     off_t       newOffset;
                                744                 :                : 
 8950                           745   [ +  +  +  - ]:          53559 :     switch (whence)
                                746                 :                :     {
                                747                 :          53193 :         case SEEK_SET:
 8944                           748         [ -  + ]:          53193 :             if (fileno < 0)
 8950 tgl@sss.pgh.pa.us         749                 :UBC           0 :                 return EOF;
 8950 tgl@sss.pgh.pa.us         750                 :CBC       53193 :             newFile = fileno;
                                751                 :          53193 :             newOffset = offset;
                                752                 :          53193 :             break;
                                753                 :             15 :         case SEEK_CUR:
                                754                 :                : 
                                755                 :                :             /*
                                756                 :                :              * Relative seek considers only the signed offset, ignoring
                                757                 :                :              * fileno. Note that large offsets (> 1 GB) risk overflow in this
                                758                 :                :              * add, unless we have 64-bit off_t.
                                759                 :                :              */
                                760                 :             15 :             newFile = file->curFile;
                                761                 :             15 :             newOffset = (file->curOffset + file->pos) + offset;
                                762                 :             15 :             break;
                                763                 :            351 :         case SEEK_END:
                                764                 :                : 
                                765                 :                :             /*
                                766                 :                :              * The file size of the last file gives us the end offset of that
                                767                 :                :              * file.
                                768                 :                :              */
 1327 akapila@postgresql.o      769                 :            351 :             newFile = file->numFiles - 1;
                                770                 :            351 :             newOffset = FileSize(file->files[file->numFiles - 1]);
                                771         [ -  + ]:            351 :             if (newOffset < 0)
 1327 akapila@postgresql.o      772         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                773                 :                :                         (errcode_for_file_access(),
                                774                 :                :                          errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
                                775                 :                :                                 FilePathName(file->files[file->numFiles - 1]),
                                776                 :                :                                 file->name)));
 8950 tgl@sss.pgh.pa.us         777                 :CBC         351 :             break;
 8950 tgl@sss.pgh.pa.us         778                 :UBC           0 :         default:
 7570                           779         [ #  # ]:              0 :             elog(ERROR, "invalid whence: %d", whence);
                                780                 :                :             return EOF;
                                781                 :                :     }
 8950 tgl@sss.pgh.pa.us         782         [ -  + ]:CBC       53559 :     while (newOffset < 0)
                                783                 :                :     {
 8950 tgl@sss.pgh.pa.us         784         [ #  # ]:UBC           0 :         if (--newFile < 0)
                                785                 :              0 :             return EOF;
                                786                 :              0 :         newOffset += MAX_PHYSICAL_FILESIZE;
                                787                 :                :     }
 8950 tgl@sss.pgh.pa.us         788         [ +  + ]:CBC       53559 :     if (newFile == file->curFile &&
                                789         [ +  + ]:          53485 :         newOffset >= file->curOffset &&
                                790         [ +  + ]:          39010 :         newOffset <= file->curOffset + file->nbytes)
                                791                 :                :     {
                                792                 :                :         /*
                                793                 :                :          * Seek is to a point within existing buffer; we can just adjust
                                794                 :                :          * pos-within-buffer, without flushing buffer.  Note this is OK
                                795                 :                :          * whether reading or writing, but buffer remains dirty if we were
                                796                 :                :          * writing.
                                797                 :                :          */
                                798                 :          27382 :         file->pos = (int) (newOffset - file->curOffset);
                                799                 :          27382 :         return 0;
                                800                 :                :     }
                                801                 :                :     /* Otherwise, must reposition buffer, so flush any dirty data */
 1398 tmunro@postgresql.or      802                 :          26177 :     BufFileFlush(file);
                                803                 :                : 
                                804                 :                :     /*
                                805                 :                :      * At this point and no sooner, check for seek past last segment. The
                                806                 :                :      * above flush could have created a new segment, so checking sooner would
                                807                 :                :      * not work (at least not with this code).
                                808                 :                :      */
                                809                 :                : 
                                810                 :                :     /* convert seek to "start of next seg" to "end of last seg" */
 2341 andres@anarazel.de        811   [ -  +  -  - ]:          26177 :     if (newFile == file->numFiles && newOffset == 0)
                                812                 :                :     {
 2341 andres@anarazel.de        813                 :UBC           0 :         newFile--;
                                814                 :              0 :         newOffset = MAX_PHYSICAL_FILESIZE;
                                815                 :                :     }
 2341 andres@anarazel.de        816         [ -  + ]:CBC       26177 :     while (newOffset > MAX_PHYSICAL_FILESIZE)
                                817                 :                :     {
 2341 andres@anarazel.de        818         [ #  # ]:UBC           0 :         if (++newFile >= file->numFiles)
                                819                 :              0 :             return EOF;
                                820                 :              0 :         newOffset -= MAX_PHYSICAL_FILESIZE;
                                821                 :                :     }
 8944 tgl@sss.pgh.pa.us         822         [ -  + ]:CBC       26177 :     if (newFile >= file->numFiles)
 8944 tgl@sss.pgh.pa.us         823                 :UBC           0 :         return EOF;
                                824                 :                :     /* Seek is OK! */
 8950 tgl@sss.pgh.pa.us         825                 :CBC       26177 :     file->curFile = newFile;
                                826                 :          26177 :     file->curOffset = newOffset;
                                827                 :          26177 :     file->pos = 0;
                                828                 :          26177 :     file->nbytes = 0;
                                829                 :          26177 :     return 0;
                                830                 :                : }
                                831                 :                : 
                                832                 :                : void
 5879                           833                 :          88642 : BufFileTell(BufFile *file, int *fileno, off_t *offset)
                                834                 :                : {
 8950                           835                 :          88642 :     *fileno = file->curFile;
                                836                 :          88642 :     *offset = file->curOffset + file->pos;
                                837                 :          88642 : }
                                838                 :                : 
                                839                 :                : /*
                                840                 :                :  * BufFileSeekBlock --- block-oriented seek
                                841                 :                :  *
                                842                 :                :  * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
                                843                 :                :  * the file.  Note that users of this interface will fail if their files
                                844                 :                :  * exceed BLCKSZ * PG_INT64_MAX bytes, but that is quite a lot; we don't
                                845                 :                :  * work with tables bigger than that, either...
                                846                 :                :  *
                                847                 :                :  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
                                848                 :                :  * impossible seek is attempted.
                                849                 :                :  */
                                850                 :                : int
  149 michael@paquier.xyz       851                 :GNC       51366 : BufFileSeekBlock(BufFile *file, int64 blknum)
                                852                 :                : {
 8947 tgl@sss.pgh.pa.us         853                 :CBC      102732 :     return BufFileSeek(file,
 5879                           854                 :          51366 :                        (int) (blknum / BUFFILE_SEG_SIZE),
                                855                 :          51366 :                        (off_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
                                856                 :                :                        SEEK_SET);
                                857                 :                : }
                                858                 :                : 
                                859                 :                : /*
                                860                 :                :  * Return the current fileset based BufFile size.
                                861                 :                :  *
                                862                 :                :  * Counts any holes left behind by BufFileAppend as part of the size.
                                863                 :                :  * ereport()s on failure.
                                864                 :                :  */
                                865                 :                : int64
 2263 rhaas@postgresql.org      866                 :            146 : BufFileSize(BufFile *file)
                                867                 :                : {
                                868                 :                :     int64       lastFileSize;
                                869                 :                : 
 1964 pg@bowt.ie                870         [ -  + ]:            146 :     Assert(file->fileset != NULL);
                                871                 :                : 
                                872                 :                :     /* Get the size of the last physical file. */
 1985 tmunro@postgresql.or      873                 :            146 :     lastFileSize = FileSize(file->files[file->numFiles - 1]);
 2174 heikki.linnakangas@i      874         [ -  + ]:            146 :     if (lastFileSize < 0)
 1964 pg@bowt.ie                875         [ #  # ]:UBC           0 :         ereport(ERROR,
                                876                 :                :                 (errcode_for_file_access(),
                                877                 :                :                  errmsg("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m",
                                878                 :                :                         FilePathName(file->files[file->numFiles - 1]),
                                879                 :                :                         file->name)));
                                880                 :                : 
 1977 tmunro@postgresql.or      881                 :CBC         146 :     return ((file->numFiles - 1) * (int64) MAX_PHYSICAL_FILESIZE) +
                                882                 :                :         lastFileSize;
                                883                 :                : }
                                884                 :                : 
                                885                 :                : /*
                                886                 :                :  * Append the contents of source file (managed within fileset) to
                                887                 :                :  * end of target file (managed within same fileset).
                                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
 2263 rhaas@postgresql.org      905                 :             74 : BufFileAppend(BufFile *target, BufFile *source)
                                906                 :                : {
  146 michael@paquier.xyz       907                 :GNC          74 :     int64       startBlock = (int64) target->numFiles * BUFFILE_SEG_SIZE;
 2263 rhaas@postgresql.org      908                 :CBC          74 :     int         newNumFiles = target->numFiles + source->numFiles;
                                909                 :                :     int         i;
                                910                 :                : 
                                911         [ -  + ]:             74 :     Assert(target->fileset != NULL);
                                912         [ -  + ]:             74 :     Assert(source->readOnly);
                                913         [ -  + ]:             74 :     Assert(!source->dirty);
                                914         [ -  + ]:             74 :     Assert(source->fileset != NULL);
                                915                 :                : 
                                916         [ -  + ]:             74 :     if (target->resowner != source->resowner)
 2263 rhaas@postgresql.org      917         [ #  # ]:UBC           0 :         elog(ERROR, "could not append BufFile with non-matching resource owner");
                                918                 :                : 
 2263 rhaas@postgresql.org      919                 :CBC          74 :     target->files = (File *)
                                920                 :             74 :         repalloc(target->files, sizeof(File) * newNumFiles);
                                921         [ +  + ]:            148 :     for (i = target->numFiles; i < newNumFiles; i++)
                                922                 :             74 :         target->files[i] = source->files[i - target->numFiles];
                                923                 :             74 :     target->numFiles = newNumFiles;
                                924                 :                : 
                                925                 :             74 :     return startBlock;
                                926                 :                : }
                                927                 :                : 
                                928                 :                : /*
                                929                 :                :  * Truncate a BufFile created by BufFileCreateFileSet up to the given fileno
                                930                 :                :  * and the offset.
                                931                 :                :  */
                                932                 :                : void
  958 akapila@postgresql.o      933                 :              9 : BufFileTruncateFileSet(BufFile *file, int fileno, off_t offset)
                                934                 :                : {
 1327                           935                 :              9 :     int         numFiles = file->numFiles;
                                936                 :              9 :     int         newFile = fileno;
                                937                 :              9 :     off_t       newOffset = file->curOffset;
                                938                 :                :     char        segment_name[MAXPGPATH];
                                939                 :                :     int         i;
                                940                 :                : 
                                941                 :                :     /*
                                942                 :                :      * Loop over all the files up to the given fileno and remove the files
                                943                 :                :      * that are greater than the fileno and truncate the given file up to the
                                944                 :                :      * offset. Note that we also remove the given fileno if the offset is 0
                                945                 :                :      * provided it is not the first file in which we truncate it.
                                946                 :                :      */
                                947         [ +  + ]:             18 :     for (i = file->numFiles - 1; i >= fileno; i--)
                                948                 :                :     {
                                949   [ +  -  -  +  :              9 :         if ((i != fileno || offset == 0) && i != 0)
                                              -  - ]
                                950                 :                :         {
  958 akapila@postgresql.o      951                 :UBC           0 :             FileSetSegmentName(segment_name, file->name, i);
 1327                           952                 :              0 :             FileClose(file->files[i]);
  958                           953         [ #  # ]:              0 :             if (!FileSetDelete(file->fileset, segment_name, true))
 1327                           954         [ #  # ]:              0 :                 ereport(ERROR,
                                955                 :                :                         (errcode_for_file_access(),
                                956                 :                :                          errmsg("could not delete fileset \"%s\": %m",
                                957                 :                :                                 segment_name)));
                                958                 :              0 :             numFiles--;
                                959                 :              0 :             newOffset = MAX_PHYSICAL_FILESIZE;
                                960                 :                : 
                                961                 :                :             /*
                                962                 :                :              * This is required to indicate that we have deleted the given
                                963                 :                :              * fileno.
                                964                 :                :              */
                                965         [ #  # ]:              0 :             if (i == fileno)
                                966                 :              0 :                 newFile--;
                                967                 :                :         }
                                968                 :                :         else
                                969                 :                :         {
 1327 akapila@postgresql.o      970         [ -  + ]:CBC           9 :             if (FileTruncate(file->files[i], offset,
                                971                 :                :                              WAIT_EVENT_BUFFILE_TRUNCATE) < 0)
 1327 akapila@postgresql.o      972         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                973                 :                :                         (errcode_for_file_access(),
                                974                 :                :                          errmsg("could not truncate file \"%s\": %m",
                                975                 :                :                                 FilePathName(file->files[i]))));
 1327 akapila@postgresql.o      976                 :CBC           9 :             newOffset = offset;
                                977                 :                :         }
                                978                 :                :     }
                                979                 :                : 
                                980                 :              9 :     file->numFiles = numFiles;
                                981                 :                : 
                                982                 :                :     /*
                                983                 :                :      * If the truncate point is within existing buffer then we can just adjust
                                984                 :                :      * pos within buffer.
                                985                 :                :      */
                                986         [ +  - ]:              9 :     if (newFile == file->curFile &&
                                987         [ +  - ]:              9 :         newOffset >= file->curOffset &&
                                988         [ -  + ]:              9 :         newOffset <= file->curOffset + file->nbytes)
                                989                 :                :     {
                                990                 :                :         /* No need to reset the current pos if the new pos is greater. */
 1327 akapila@postgresql.o      991         [ #  # ]:UBC           0 :         if (newOffset <= file->curOffset + file->pos)
                                992                 :              0 :             file->pos = (int) (newOffset - file->curOffset);
                                993                 :                : 
                                994                 :                :         /* Adjust the nbytes for the current buffer. */
                                995                 :              0 :         file->nbytes = (int) (newOffset - file->curOffset);
                                996                 :                :     }
 1327 akapila@postgresql.o      997         [ +  - ]:CBC           9 :     else if (newFile == file->curFile &&
                                998         [ -  + ]:              9 :              newOffset < file->curOffset)
                                999                 :                :     {
                               1000                 :                :         /*
                               1001                 :                :          * The truncate point is within the existing file but prior to the
                               1002                 :                :          * current position, so we can forget the current buffer and reset the
                               1003                 :                :          * current position.
                               1004                 :                :          */
 1327 akapila@postgresql.o     1005                 :UBC           0 :         file->curOffset = newOffset;
                               1006                 :              0 :         file->pos = 0;
                               1007                 :              0 :         file->nbytes = 0;
                               1008                 :                :     }
 1327 akapila@postgresql.o     1009         [ -  + ]:CBC           9 :     else if (newFile < file->curFile)
                               1010                 :                :     {
                               1011                 :                :         /*
                               1012                 :                :          * The truncate point is prior to the current file, so need to reset
                               1013                 :                :          * the current position accordingly.
                               1014                 :                :          */
 1327 akapila@postgresql.o     1015                 :UBC           0 :         file->curFile = newFile;
                               1016                 :              0 :         file->curOffset = newOffset;
                               1017                 :              0 :         file->pos = 0;
                               1018                 :              0 :         file->nbytes = 0;
                               1019                 :                :     }
                               1020                 :                :     /* Nothing to do, if the truncate point is beyond current file. */
 1327 akapila@postgresql.o     1021                 :CBC           9 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622