LCOV - code coverage report
Current view: top level - src/backend/storage/buffer - localbuf.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.7 % 406 360
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 55.4 % 242 134

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * localbuf.c
       4                 :             :  *        local buffer manager. Fast buffer manager for temporary tables,
       5                 :             :  *        which never need to be WAL-logged or checkpointed, etc.
       6                 :             :  *
       7                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8                 :             :  * Portions Copyright (c) 1994-5, Regents of the University of California
       9                 :             :  *
      10                 :             :  *
      11                 :             :  * IDENTIFICATION
      12                 :             :  *        src/backend/storage/buffer/localbuf.c
      13                 :             :  *
      14                 :             :  *-------------------------------------------------------------------------
      15                 :             :  */
      16                 :             : #include "postgres.h"
      17                 :             : 
      18                 :             : #include "access/parallel.h"
      19                 :             : #include "executor/instrument.h"
      20                 :             : #include "pgstat.h"
      21                 :             : #include "storage/aio.h"
      22                 :             : #include "storage/buf_internals.h"
      23                 :             : #include "storage/bufmgr.h"
      24                 :             : #include "storage/fd.h"
      25                 :             : #include "utils/guc_hooks.h"
      26                 :             : #include "utils/memdebug.h"
      27                 :             : #include "utils/memutils.h"
      28                 :             : #include "utils/rel.h"
      29                 :             : #include "utils/resowner.h"
      30                 :             : 
      31                 :             : 
      32                 :             : /*#define LBDEBUG*/
      33                 :             : 
      34                 :             : /* entry for buffer lookup hashtable */
      35                 :             : typedef struct
      36                 :             : {
      37                 :             :         BufferTag       key;                    /* Tag of a disk page */
      38                 :             :         int                     id;                             /* Associated local buffer's index */
      39                 :             : } LocalBufferLookupEnt;
      40                 :             : 
      41                 :             : /* Note: this macro only works on local buffers, not shared ones! */
      42                 :             : #define LocalBufHdrGetBlock(bufHdr) \
      43                 :             :         LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
      44                 :             : 
      45                 :             : int                     NLocBuffer = 0;         /* until buffers are initialized */
      46                 :             : 
      47                 :             : BufferDesc *LocalBufferDescriptors = NULL;
      48                 :             : Block      *LocalBufferBlockPointers = NULL;
      49                 :             : int32      *LocalRefCount = NULL;
      50                 :             : 
      51                 :             : static int      nextFreeLocalBufId = 0;
      52                 :             : 
      53                 :             : static HTAB *LocalBufHash = NULL;
      54                 :             : 
      55                 :             : /* number of local buffers pinned at least once */
      56                 :             : static int      NLocalPinnedBuffers = 0;
      57                 :             : 
      58                 :             : 
      59                 :             : static void InitLocalBuffers(void);
      60                 :             : static Block GetLocalBufferStorage(void);
      61                 :             : static Buffer GetLocalVictimBuffer(void);
      62                 :             : 
      63                 :             : 
      64                 :             : /*
      65                 :             :  * PrefetchLocalBuffer -
      66                 :             :  *        initiate asynchronous read of a block of a relation
      67                 :             :  *
      68                 :             :  * Do PrefetchBuffer's work for temporary relations.
      69                 :             :  * No-op if prefetching isn't compiled in.
      70                 :             :  */
      71                 :             : PrefetchBufferResult
      72                 :         250 : PrefetchLocalBuffer(SMgrRelation smgr, ForkNumber forkNum,
      73                 :             :                                         BlockNumber blockNum)
      74                 :             : {
      75                 :         250 :         PrefetchBufferResult result = {InvalidBuffer, false};
      76                 :         250 :         BufferTag       newTag;                 /* identity of requested block */
      77                 :         250 :         LocalBufferLookupEnt *hresult;
      78                 :             : 
      79                 :         250 :         InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
      80                 :             : 
      81                 :             :         /* Initialize local buffers if first request in this session */
      82         [ +  - ]:         250 :         if (LocalBufHash == NULL)
      83                 :           0 :                 InitLocalBuffers();
      84                 :             : 
      85                 :             :         /* See if the desired buffer already exists */
      86                 :         250 :         hresult = (LocalBufferLookupEnt *)
      87                 :         250 :                 hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
      88                 :             : 
      89         [ +  - ]:         250 :         if (hresult)
      90                 :             :         {
      91                 :             :                 /* Yes, so nothing to do */
      92                 :         250 :                 result.recent_buffer = -hresult->id - 1;
      93                 :         250 :         }
      94                 :             :         else
      95                 :             :         {
      96                 :             : #ifdef USE_PREFETCH
      97                 :             :                 /* Not in buffers, so initiate prefetch */
      98   [ #  #  #  # ]:           0 :                 if ((io_direct_flags & IO_DIRECT_DATA) == 0 &&
      99                 :           0 :                         smgrprefetch(smgr, forkNum, blockNum, 1))
     100                 :             :                 {
     101                 :           0 :                         result.initiated_io = true;
     102                 :           0 :                 }
     103                 :             : #endif                                                  /* USE_PREFETCH */
     104                 :             :         }
     105                 :             : 
     106                 :             :         return result;
     107                 :         250 : }
     108                 :             : 
     109                 :             : 
     110                 :             : /*
     111                 :             :  * LocalBufferAlloc -
     112                 :             :  *        Find or create a local buffer for the given page of the given relation.
     113                 :             :  *
     114                 :             :  * API is similar to bufmgr.c's BufferAlloc, except that we do not need to do
     115                 :             :  * any locking since this is all local.  We support only default access
     116                 :             :  * strategy (hence, usage_count is always advanced).
     117                 :             :  */
     118                 :             : BufferDesc *
     119                 :      353699 : LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
     120                 :             :                                  bool *foundPtr)
     121                 :             : {
     122                 :      353699 :         BufferTag       newTag;                 /* identity of requested block */
     123                 :      353699 :         LocalBufferLookupEnt *hresult;
     124                 :      353699 :         BufferDesc *bufHdr;
     125                 :      353699 :         Buffer          victim_buffer;
     126                 :      353699 :         int                     bufid;
     127                 :      353699 :         bool            found;
     128                 :             : 
     129                 :      353699 :         InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
     130                 :             : 
     131                 :             :         /* Initialize local buffers if first request in this session */
     132         [ +  + ]:      353699 :         if (LocalBufHash == NULL)
     133                 :           4 :                 InitLocalBuffers();
     134                 :             : 
     135                 :      353699 :         ResourceOwnerEnlarge(CurrentResourceOwner);
     136                 :             : 
     137                 :             :         /* See if the desired buffer already exists */
     138                 :      353699 :         hresult = (LocalBufferLookupEnt *)
     139                 :      353699 :                 hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
     140                 :             : 
     141         [ +  + ]:      353699 :         if (hresult)
     142                 :             :         {
     143                 :      350930 :                 bufid = hresult->id;
     144                 :      350930 :                 bufHdr = GetLocalBufferDescriptor(bufid);
     145         [ +  - ]:      350930 :                 Assert(BufferTagsEqual(&bufHdr->tag, &newTag));
     146                 :             : 
     147                 :      350930 :                 *foundPtr = PinLocalBuffer(bufHdr, true);
     148                 :      350930 :         }
     149                 :             :         else
     150                 :             :         {
     151                 :        2769 :                 uint64          buf_state;
     152                 :             : 
     153                 :        2769 :                 victim_buffer = GetLocalVictimBuffer();
     154                 :        2769 :                 bufid = -victim_buffer - 1;
     155                 :        2769 :                 bufHdr = GetLocalBufferDescriptor(bufid);
     156                 :             : 
     157                 :        2769 :                 hresult = (LocalBufferLookupEnt *)
     158                 :        2769 :                         hash_search(LocalBufHash, &newTag, HASH_ENTER, &found);
     159         [ +  - ]:        2769 :                 if (found)                              /* shouldn't happen */
     160   [ #  #  #  # ]:           0 :                         elog(ERROR, "local buffer hash table corrupted");
     161                 :        2769 :                 hresult->id = bufid;
     162                 :             : 
     163                 :             :                 /*
     164                 :             :                  * it's all ours now.
     165                 :             :                  */
     166                 :        2769 :                 bufHdr->tag = newTag;
     167                 :             : 
     168                 :        2769 :                 buf_state = pg_atomic_read_u64(&bufHdr->state);
     169                 :        2769 :                 buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
     170                 :        2769 :                 buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
     171                 :        2769 :                 pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
     172                 :             : 
     173                 :        2769 :                 *foundPtr = false;
     174                 :        2769 :         }
     175                 :             : 
     176                 :      707398 :         return bufHdr;
     177                 :      353699 : }
     178                 :             : 
     179                 :             : /*
     180                 :             :  * Like FlushBuffer(), just for local buffers.
     181                 :             :  */
     182                 :             : void
     183                 :         940 : FlushLocalBuffer(BufferDesc *bufHdr, SMgrRelation reln)
     184                 :             : {
     185                 :         940 :         instr_time      io_start;
     186                 :         940 :         Page            localpage = (char *) LocalBufHdrGetBlock(bufHdr);
     187                 :             : 
     188         [ +  - ]:         940 :         Assert(LocalRefCount[-BufferDescriptorGetBuffer(bufHdr) - 1] > 0);
     189                 :             : 
     190                 :             :         /*
     191                 :             :          * Try to start an I/O operation.  There currently are no reasons for
     192                 :             :          * StartLocalBufferIO to return false, so we raise an error in that case.
     193                 :             :          */
     194         [ +  - ]:         940 :         if (!StartLocalBufferIO(bufHdr, false, false))
     195   [ #  #  #  # ]:           0 :                 elog(ERROR, "failed to start write IO on local buffer");
     196                 :             : 
     197                 :             :         /* Find smgr relation for buffer */
     198         [ +  + ]:         940 :         if (reln == NULL)
     199                 :        1680 :                 reln = smgropen(BufTagGetRelFileLocator(&bufHdr->tag),
     200                 :         840 :                                                 MyProcNumber);
     201                 :             : 
     202                 :         940 :         PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
     203                 :             : 
     204                 :         940 :         io_start = pgstat_prepare_io_time(track_io_timing);
     205                 :             : 
     206                 :             :         /* And write... */
     207                 :        1880 :         smgrwrite(reln,
     208                 :         940 :                           BufTagGetForkNum(&bufHdr->tag),
     209                 :         940 :                           bufHdr->tag.blockNum,
     210                 :         940 :                           localpage,
     211                 :             :                           false);
     212                 :             : 
     213                 :             :         /* Temporary table I/O does not use Buffer Access Strategies */
     214                 :         940 :         pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL,
     215                 :             :                                                         IOOP_WRITE, io_start, 1, BLCKSZ);
     216                 :             : 
     217                 :             :         /* Mark not-dirty */
     218                 :         940 :         TerminateLocalBufferIO(bufHdr, true, 0, false);
     219                 :             : 
     220                 :         940 :         pgBufferUsage.local_blks_written++;
     221                 :         940 : }
     222                 :             : 
     223                 :             : static Buffer
     224                 :        6964 : GetLocalVictimBuffer(void)
     225                 :             : {
     226                 :        6964 :         int                     victim_bufid;
     227                 :        6964 :         int                     trycounter;
     228                 :        6964 :         BufferDesc *bufHdr;
     229                 :             : 
     230                 :        6964 :         ResourceOwnerEnlarge(CurrentResourceOwner);
     231                 :             : 
     232                 :             :         /*
     233                 :             :          * Need to get a new buffer.  We use a clock-sweep algorithm (essentially
     234                 :             :          * the same as what freelist.c does now...)
     235                 :             :          */
     236                 :        6964 :         trycounter = NLocBuffer;
     237                 :       31635 :         for (;;)
     238                 :             :         {
     239                 :       31635 :                 victim_bufid = nextFreeLocalBufId;
     240                 :             : 
     241         [ +  + ]:       31635 :                 if (++nextFreeLocalBufId >= NLocBuffer)
     242                 :         265 :                         nextFreeLocalBufId = 0;
     243                 :             : 
     244                 :       31635 :                 bufHdr = GetLocalBufferDescriptor(victim_bufid);
     245                 :             : 
     246         [ +  + ]:       31635 :                 if (LocalRefCount[victim_bufid] == 0)
     247                 :             :                 {
     248                 :       11413 :                         uint64          buf_state = pg_atomic_read_u64(&bufHdr->state);
     249                 :             : 
     250         [ +  + ]:       11413 :                         if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0)
     251                 :             :                         {
     252                 :        4451 :                                 buf_state -= BUF_USAGECOUNT_ONE;
     253                 :        4451 :                                 pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
     254                 :        4451 :                                 trycounter = NLocBuffer;
     255                 :        4451 :                         }
     256         [ -  + ]:        6962 :                         else if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
     257                 :             :                         {
     258                 :             :                                 /*
     259                 :             :                                  * This can be reached if the backend initiated AIO for this
     260                 :             :                                  * buffer and then errored out.
     261                 :             :                                  */
     262                 :           0 :                         }
     263                 :             :                         else
     264                 :             :                         {
     265                 :             :                                 /* Found a usable buffer */
     266                 :        6962 :                                 PinLocalBuffer(bufHdr, false);
     267                 :        6962 :                                 break;
     268                 :             :                         }
     269      [ +  -  + ]:       11413 :                 }
     270         [ +  + ]:       20222 :                 else if (--trycounter == 0)
     271   [ +  -  +  - ]:           2 :                         ereport(ERROR,
     272                 :             :                                         (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
     273                 :             :                                          errmsg("no empty local buffer available")));
     274                 :             :         }
     275                 :             : 
     276                 :             :         /*
     277                 :             :          * lazy memory allocation: allocate space on first use of a buffer.
     278                 :             :          */
     279         [ +  + ]:        6962 :         if (LocalBufHdrGetBlock(bufHdr) == NULL)
     280                 :             :         {
     281                 :             :                 /* Set pointer for use by BufferGetBlock() macro */
     282                 :        4826 :                 LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
     283                 :        4826 :         }
     284                 :             : 
     285                 :             :         /*
     286                 :             :          * this buffer is not referenced but it might still be dirty. if that's
     287                 :             :          * the case, write it out before reusing it!
     288                 :             :          */
     289         [ +  + ]:        6962 :         if (pg_atomic_read_u64(&bufHdr->state) & BM_DIRTY)
     290                 :         840 :                 FlushLocalBuffer(bufHdr, NULL);
     291                 :             : 
     292                 :             :         /*
     293                 :             :          * Remove the victim buffer from the hashtable and mark as invalid.
     294                 :             :          */
     295         [ +  + ]:        6962 :         if (pg_atomic_read_u64(&bufHdr->state) & BM_TAG_VALID)
     296                 :             :         {
     297                 :        1871 :                 InvalidateLocalBuffer(bufHdr, false);
     298                 :             : 
     299                 :        1871 :                 pgstat_count_io_op(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EVICT, 1, 0);
     300                 :        1871 :         }
     301                 :             : 
     302                 :       13924 :         return BufferDescriptorGetBuffer(bufHdr);
     303                 :        6962 : }
     304                 :             : 
     305                 :             : /* see GetPinLimit() */
     306                 :             : uint32
     307                 :        2197 : GetLocalPinLimit(void)
     308                 :             : {
     309                 :             :         /* Every backend has its own temporary buffers, and can pin them all. */
     310                 :        2197 :         return num_temp_buffers;
     311                 :             : }
     312                 :             : 
     313                 :             : /* see GetAdditionalPinLimit() */
     314                 :             : uint32
     315                 :        7726 : GetAdditionalLocalPinLimit(void)
     316                 :             : {
     317         [ +  - ]:        7726 :         Assert(NLocalPinnedBuffers <= num_temp_buffers);
     318                 :        7726 :         return num_temp_buffers - NLocalPinnedBuffers;
     319                 :             : }
     320                 :             : 
     321                 :             : /* see LimitAdditionalPins() */
     322                 :             : void
     323                 :        3132 : LimitAdditionalLocalPins(uint32 *additional_pins)
     324                 :             : {
     325                 :        3132 :         uint32          max_pins;
     326                 :             : 
     327         [ +  + ]:        3132 :         if (*additional_pins <= 1)
     328                 :        3033 :                 return;
     329                 :             : 
     330                 :             :         /*
     331                 :             :          * In contrast to LimitAdditionalPins() other backends don't play a role
     332                 :             :          * here. We can allow up to NLocBuffer pins in total, but it might not be
     333                 :             :          * initialized yet so read num_temp_buffers.
     334                 :             :          */
     335                 :          99 :         max_pins = (num_temp_buffers - NLocalPinnedBuffers);
     336                 :             : 
     337         [ +  - ]:          99 :         if (*additional_pins >= max_pins)
     338                 :           0 :                 *additional_pins = max_pins;
     339         [ -  + ]:        3132 : }
     340                 :             : 
     341                 :             : /*
     342                 :             :  * Implementation of ExtendBufferedRelBy() and ExtendBufferedRelTo() for
     343                 :             :  * temporary buffers.
     344                 :             :  */
     345                 :             : BlockNumber
     346                 :        3132 : ExtendBufferedRelLocal(BufferManagerRelation bmr,
     347                 :             :                                            ForkNumber fork,
     348                 :             :                                            uint32 flags,
     349                 :             :                                            uint32 extend_by,
     350                 :             :                                            BlockNumber extend_upto,
     351                 :             :                                            Buffer *buffers,
     352                 :             :                                            uint32 *extended_by)
     353                 :             : {
     354                 :        3132 :         BlockNumber first_block;
     355                 :        3132 :         instr_time      io_start;
     356                 :             : 
     357                 :             :         /* Initialize local buffers if first request in this session */
     358         [ +  + ]:        3132 :         if (LocalBufHash == NULL)
     359                 :          73 :                 InitLocalBuffers();
     360                 :             : 
     361                 :        3132 :         LimitAdditionalLocalPins(&extend_by);
     362                 :             : 
     363         [ +  + ]:        7325 :         for (uint32 i = 0; i < extend_by; i++)
     364                 :             :         {
     365                 :        4193 :                 BufferDesc *buf_hdr;
     366                 :        4193 :                 Block           buf_block;
     367                 :             : 
     368                 :        4193 :                 buffers[i] = GetLocalVictimBuffer();
     369                 :        4193 :                 buf_hdr = GetLocalBufferDescriptor(-buffers[i] - 1);
     370                 :        4193 :                 buf_block = LocalBufHdrGetBlock(buf_hdr);
     371                 :             : 
     372                 :             :                 /* new buffers are zero-filled */
     373   [ +  -  +  -  :        4193 :                 MemSet(buf_block, 0, BLCKSZ);
          +  -  +  -  #  
                      # ]
     374                 :        4193 :         }
     375                 :             : 
     376         [ +  - ]:        3132 :         first_block = smgrnblocks(BMR_GET_SMGR(bmr), fork);
     377                 :             : 
     378         [ +  + ]:        3132 :         if (extend_upto != InvalidBlockNumber)
     379                 :             :         {
     380                 :             :                 /*
     381                 :             :                  * In contrast to shared relations, nothing could change the relation
     382                 :             :                  * size concurrently. Thus we shouldn't end up finding that we don't
     383                 :             :                  * need to do anything.
     384                 :             :                  */
     385         [ +  - ]:          43 :                 Assert(first_block <= extend_upto);
     386                 :             : 
     387         [ +  - ]:          43 :                 Assert((uint64) first_block + extend_by <= extend_upto);
     388                 :          43 :         }
     389                 :             : 
     390                 :             :         /* Fail if relation is already at maximum possible length */
     391         [ +  - ]:        3132 :         if ((uint64) first_block + extend_by >= MaxBlockNumber)
     392   [ #  #  #  #  :           0 :                 ereport(ERROR,
          #  #  #  #  #  
                #  #  # ]
     393                 :             :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     394                 :             :                                  errmsg("cannot extend relation %s beyond %u blocks",
     395                 :             :                                                 relpath(BMR_GET_SMGR(bmr)->smgr_rlocator, fork).str,
     396                 :             :                                                 MaxBlockNumber)));
     397                 :             : 
     398         [ +  + ]:        7325 :         for (uint32 i = 0; i < extend_by; i++)
     399                 :             :         {
     400                 :        4193 :                 int                     victim_buf_id;
     401                 :        4193 :                 BufferDesc *victim_buf_hdr;
     402                 :        4193 :                 BufferTag       tag;
     403                 :        4193 :                 LocalBufferLookupEnt *hresult;
     404                 :        4193 :                 bool            found;
     405                 :             : 
     406                 :        4193 :                 victim_buf_id = -buffers[i] - 1;
     407                 :        4193 :                 victim_buf_hdr = GetLocalBufferDescriptor(victim_buf_id);
     408                 :             : 
     409                 :             :                 /* in case we need to pin an existing buffer below */
     410                 :        4193 :                 ResourceOwnerEnlarge(CurrentResourceOwner);
     411                 :             : 
     412         [ +  - ]:        4193 :                 InitBufferTag(&tag, &BMR_GET_SMGR(bmr)->smgr_rlocator.locator, fork,
     413                 :        4193 :                                           first_block + i);
     414                 :             : 
     415                 :        4193 :                 hresult = (LocalBufferLookupEnt *)
     416                 :        4193 :                         hash_search(LocalBufHash, &tag, HASH_ENTER, &found);
     417         [ -  + ]:        4193 :                 if (found)
     418                 :             :                 {
     419                 :           0 :                         BufferDesc *existing_hdr;
     420                 :           0 :                         uint64          buf_state;
     421                 :             : 
     422                 :           0 :                         UnpinLocalBuffer(BufferDescriptorGetBuffer(victim_buf_hdr));
     423                 :             : 
     424                 :           0 :                         existing_hdr = GetLocalBufferDescriptor(hresult->id);
     425                 :           0 :                         PinLocalBuffer(existing_hdr, false);
     426                 :           0 :                         buffers[i] = BufferDescriptorGetBuffer(existing_hdr);
     427                 :             : 
     428                 :             :                         /*
     429                 :             :                          * Clear the BM_VALID bit, do StartLocalBufferIO() and proceed.
     430                 :             :                          */
     431                 :           0 :                         buf_state = pg_atomic_read_u64(&existing_hdr->state);
     432         [ #  # ]:           0 :                         Assert(buf_state & BM_TAG_VALID);
     433         [ #  # ]:           0 :                         Assert(!(buf_state & BM_DIRTY));
     434                 :           0 :                         buf_state &= ~BM_VALID;
     435                 :           0 :                         pg_atomic_unlocked_write_u64(&existing_hdr->state, buf_state);
     436                 :             : 
     437                 :             :                         /* no need to loop for local buffers */
     438                 :           0 :                         StartLocalBufferIO(existing_hdr, true, false);
     439                 :           0 :                 }
     440                 :             :                 else
     441                 :             :                 {
     442                 :        4193 :                         uint64          buf_state = pg_atomic_read_u64(&victim_buf_hdr->state);
     443                 :             : 
     444         [ -  + ]:        4193 :                         Assert(!(buf_state & (BM_VALID | BM_TAG_VALID | BM_DIRTY | BM_JUST_DIRTIED)));
     445                 :             : 
     446                 :        4193 :                         victim_buf_hdr->tag = tag;
     447                 :             : 
     448                 :        4193 :                         buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
     449                 :             : 
     450                 :        4193 :                         pg_atomic_unlocked_write_u64(&victim_buf_hdr->state, buf_state);
     451                 :             : 
     452                 :        4193 :                         hresult->id = victim_buf_id;
     453                 :             : 
     454                 :        4193 :                         StartLocalBufferIO(victim_buf_hdr, true, false);
     455                 :        4193 :                 }
     456                 :        4193 :         }
     457                 :             : 
     458                 :        3132 :         io_start = pgstat_prepare_io_time(track_io_timing);
     459                 :             : 
     460                 :             :         /* actually extend relation */
     461         [ -  + ]:        3132 :         smgrzeroextend(BMR_GET_SMGR(bmr), fork, first_block, extend_by, false);
     462                 :             : 
     463                 :        3132 :         pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EXTEND,
     464                 :        3132 :                                                         io_start, 1, extend_by * BLCKSZ);
     465                 :             : 
     466         [ +  + ]:        7325 :         for (uint32 i = 0; i < extend_by; i++)
     467                 :             :         {
     468                 :        4193 :                 Buffer          buf = buffers[i];
     469                 :        4193 :                 BufferDesc *buf_hdr;
     470                 :        4193 :                 uint64          buf_state;
     471                 :             : 
     472                 :        4193 :                 buf_hdr = GetLocalBufferDescriptor(-buf - 1);
     473                 :             : 
     474                 :        4193 :                 buf_state = pg_atomic_read_u64(&buf_hdr->state);
     475                 :        4193 :                 buf_state |= BM_VALID;
     476                 :        4193 :                 pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
     477                 :        4193 :         }
     478                 :             : 
     479                 :        3132 :         *extended_by = extend_by;
     480                 :             : 
     481                 :        3132 :         pgBufferUsage.local_blks_written += extend_by;
     482                 :             : 
     483                 :        6264 :         return first_block;
     484                 :        3132 : }
     485                 :             : 
     486                 :             : /*
     487                 :             :  * MarkLocalBufferDirty -
     488                 :             :  *        mark a local buffer dirty
     489                 :             :  */
     490                 :             : void
     491                 :      537520 : MarkLocalBufferDirty(Buffer buffer)
     492                 :             : {
     493                 :      537520 :         int                     bufid;
     494                 :      537520 :         BufferDesc *bufHdr;
     495                 :      537520 :         uint64          buf_state;
     496                 :             : 
     497         [ +  - ]:      537520 :         Assert(BufferIsLocal(buffer));
     498                 :             : 
     499                 :             : #ifdef LBDEBUG
     500                 :             :         fprintf(stderr, "LB DIRTY %d\n", buffer);
     501                 :             : #endif
     502                 :             : 
     503                 :      537520 :         bufid = -buffer - 1;
     504                 :             : 
     505         [ +  - ]:      537520 :         Assert(LocalRefCount[bufid] > 0);
     506                 :             : 
     507                 :      537520 :         bufHdr = GetLocalBufferDescriptor(bufid);
     508                 :             : 
     509                 :      537520 :         buf_state = pg_atomic_read_u64(&bufHdr->state);
     510                 :             : 
     511         [ +  + ]:      537520 :         if (!(buf_state & BM_DIRTY))
     512                 :        4268 :                 pgBufferUsage.local_blks_dirtied++;
     513                 :             : 
     514                 :      537520 :         buf_state |= BM_DIRTY;
     515                 :             : 
     516                 :      537520 :         pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
     517                 :      537520 : }
     518                 :             : 
     519                 :             : /*
     520                 :             :  * Like StartBufferIO, but for local buffers
     521                 :             :  */
     522                 :             : bool
     523                 :        7902 : StartLocalBufferIO(BufferDesc *bufHdr, bool forInput, bool nowait)
     524                 :             : {
     525                 :        7902 :         uint64          buf_state;
     526                 :             : 
     527                 :             :         /*
     528                 :             :          * With AIO the buffer could have IO in progress, e.g. when there are two
     529                 :             :          * scans of the same relation. Either wait for the other IO or return
     530                 :             :          * false.
     531                 :             :          */
     532         [ +  - ]:        7902 :         if (pgaio_wref_valid(&bufHdr->io_wref))
     533                 :             :         {
     534                 :           0 :                 PgAioWaitRef iow = bufHdr->io_wref;
     535                 :             : 
     536         [ #  # ]:           0 :                 if (nowait)
     537                 :           0 :                         return false;
     538                 :             : 
     539                 :           0 :                 pgaio_wref_wait(&iow);
     540         [ #  # ]:           0 :         }
     541                 :             : 
     542                 :             :         /* Once we get here, there is definitely no I/O active on this buffer */
     543                 :             : 
     544                 :             :         /* Check if someone else already did the I/O */
     545                 :        7902 :         buf_state = pg_atomic_read_u64(&bufHdr->state);
     546   [ +  +  +  + ]:        7902 :         if (forInput ? (buf_state & BM_VALID) : !(buf_state & BM_DIRTY))
     547                 :             :         {
     548                 :        1880 :                 return false;
     549                 :             :         }
     550                 :             : 
     551                 :             :         /* BM_IO_IN_PROGRESS isn't currently used for local buffers */
     552                 :             : 
     553                 :             :         /* local buffers don't track IO using resowners */
     554                 :             : 
     555                 :        7902 :         return true;
     556                 :        9782 : }
     557                 :             : 
     558                 :             : /*
     559                 :             :  * Like TerminateBufferIO, but for local buffers
     560                 :             :  */
     561                 :             : void
     562                 :        3709 : TerminateLocalBufferIO(BufferDesc *bufHdr, bool clear_dirty, uint64 set_flag_bits,
     563                 :             :                                            bool release_aio)
     564                 :             : {
     565                 :             :         /* Only need to adjust flags */
     566                 :        3709 :         uint64          buf_state = pg_atomic_read_u64(&bufHdr->state);
     567                 :             : 
     568                 :             :         /* BM_IO_IN_PROGRESS isn't currently used for local buffers */
     569                 :             : 
     570                 :             :         /* Clear earlier errors, if this IO failed, it'll be marked again */
     571                 :        3709 :         buf_state &= ~BM_IO_ERROR;
     572                 :             : 
     573         [ +  + ]:        3709 :         if (clear_dirty)
     574                 :         940 :                 buf_state &= ~BM_DIRTY;
     575                 :             : 
     576         [ +  + ]:        3709 :         if (release_aio)
     577                 :             :         {
     578                 :             :                 /* release pin held by IO subsystem, see also buffer_stage_common() */
     579         [ +  - ]:        2769 :                 Assert(BUF_STATE_GET_REFCOUNT(buf_state) > 0);
     580                 :        2769 :                 buf_state -= BUF_REFCOUNT_ONE;
     581                 :        2769 :                 pgaio_wref_clear(&bufHdr->io_wref);
     582                 :        2769 :         }
     583                 :             : 
     584                 :        3709 :         buf_state |= set_flag_bits;
     585                 :        3709 :         pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
     586                 :             : 
     587                 :             :         /* local buffers don't track IO using resowners */
     588                 :             : 
     589                 :             :         /* local buffers don't use the IO CV, as no other process can see buffer */
     590                 :             : 
     591                 :             :         /* local buffers don't use BM_PIN_COUNT_WAITER, so no need to wake */
     592                 :        3709 : }
     593                 :             : 
     594                 :             : /*
     595                 :             :  * InvalidateLocalBuffer -- mark a local buffer invalid.
     596                 :             :  *
     597                 :             :  * If check_unreferenced is true, error out if the buffer is still
     598                 :             :  * pinned. Passing false is appropriate when calling InvalidateLocalBuffer()
     599                 :             :  * as part of changing the identity of a buffer, instead of just dropping the
     600                 :             :  * buffer.
     601                 :             :  *
     602                 :             :  * See also InvalidateBuffer().
     603                 :             :  */
     604                 :             : void
     605                 :        6962 : InvalidateLocalBuffer(BufferDesc *bufHdr, bool check_unreferenced)
     606                 :             : {
     607                 :        6962 :         Buffer          buffer = BufferDescriptorGetBuffer(bufHdr);
     608                 :        6962 :         int                     bufid = -buffer - 1;
     609                 :        6962 :         uint64          buf_state;
     610                 :        6962 :         LocalBufferLookupEnt *hresult;
     611                 :             : 
     612                 :             :         /*
     613                 :             :          * It's possible that we started IO on this buffer before e.g. aborting
     614                 :             :          * the transaction that created a table. We need to wait for that IO to
     615                 :             :          * complete before removing / reusing the buffer.
     616                 :             :          */
     617         [ +  - ]:        6962 :         if (pgaio_wref_valid(&bufHdr->io_wref))
     618                 :             :         {
     619                 :           0 :                 PgAioWaitRef iow = bufHdr->io_wref;
     620                 :             : 
     621                 :           0 :                 pgaio_wref_wait(&iow);
     622         [ #  # ]:           0 :                 Assert(!pgaio_wref_valid(&bufHdr->io_wref));
     623                 :           0 :         }
     624                 :             : 
     625                 :        6962 :         buf_state = pg_atomic_read_u64(&bufHdr->state);
     626                 :             : 
     627                 :             :         /*
     628                 :             :          * We need to test not just LocalRefCount[bufid] but also the BufferDesc
     629                 :             :          * itself, as the latter is used to represent a pin by the AIO subsystem.
     630                 :             :          * This can happen if AIO is initiated and then the query errors out.
     631                 :             :          */
     632         [ +  + ]:       12053 :         if (check_unreferenced &&
     633         [ +  - ]:        5091 :                 (LocalRefCount[bufid] != 0 || BUF_STATE_GET_REFCOUNT(buf_state) != 0))
     634   [ #  #  #  # ]:           0 :                 elog(ERROR, "block %u of %s is still referenced (local %d)",
     635                 :             :                          bufHdr->tag.blockNum,
     636                 :             :                          relpathbackend(BufTagGetRelFileLocator(&bufHdr->tag),
     637                 :             :                                                         MyProcNumber,
     638                 :             :                                                         BufTagGetForkNum(&bufHdr->tag)).str,
     639                 :             :                          LocalRefCount[bufid]);
     640                 :             : 
     641                 :             :         /* Remove entry from hashtable */
     642                 :        6962 :         hresult = (LocalBufferLookupEnt *)
     643                 :        6962 :                 hash_search(LocalBufHash, &bufHdr->tag, HASH_REMOVE, NULL);
     644         [ +  - ]:        6962 :         if (!hresult)                           /* shouldn't happen */
     645   [ #  #  #  # ]:           0 :                 elog(ERROR, "local buffer hash table corrupted");
     646                 :             :         /* Mark buffer invalid */
     647                 :        6962 :         ClearBufferTag(&bufHdr->tag);
     648                 :        6962 :         buf_state &= ~BUF_FLAG_MASK;
     649                 :        6962 :         buf_state &= ~BUF_USAGECOUNT_MASK;
     650                 :        6962 :         pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
     651                 :        6962 : }
     652                 :             : 
     653                 :             : /*
     654                 :             :  * DropRelationLocalBuffers
     655                 :             :  *              This function removes from the buffer pool all the pages of the
     656                 :             :  *              specified relation that have block numbers >= firstDelBlock.
     657                 :             :  *              (In particular, with firstDelBlock = 0, all pages are removed.)
     658                 :             :  *              Dirty pages are simply dropped, without bothering to write them
     659                 :             :  *              out first.  Therefore, this is NOT rollback-able, and so should be
     660                 :             :  *              used only with extreme caution!
     661                 :             :  *
     662                 :             :  *              See DropRelationBuffers in bufmgr.c for more notes.
     663                 :             :  */
     664                 :             : void
     665                 :         123 : DropRelationLocalBuffers(RelFileLocator rlocator, ForkNumber *forkNum,
     666                 :             :                                                  int nforks, BlockNumber *firstDelBlock)
     667                 :             : {
     668                 :         123 :         int                     i;
     669                 :         123 :         int                     j;
     670                 :             : 
     671         [ +  + ]:      102523 :         for (i = 0; i < NLocBuffer; i++)
     672                 :             :         {
     673                 :      102400 :                 BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
     674                 :      102400 :                 uint64          buf_state;
     675                 :             : 
     676                 :      102400 :                 buf_state = pg_atomic_read_u64(&bufHdr->state);
     677                 :             : 
     678   [ +  +  +  + ]:      102400 :                 if (!(buf_state & BM_TAG_VALID) ||
     679                 :        9432 :                         !BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator))
     680                 :      102123 :                         continue;
     681                 :             : 
     682         [ +  + ]:         314 :                 for (j = 0; j < nforks; j++)
     683                 :             :                 {
     684   [ +  +  +  + ]:         303 :                         if (BufTagGetForkNum(&bufHdr->tag) == forkNum[j] &&
     685                 :         276 :                                 bufHdr->tag.blockNum >= firstDelBlock[j])
     686                 :             :                         {
     687                 :         266 :                                 InvalidateLocalBuffer(bufHdr, true);
     688                 :         266 :                                 break;
     689                 :             :                         }
     690                 :          37 :                 }
     691      [ -  +  + ]:      102400 :         }
     692                 :         123 : }
     693                 :             : 
     694                 :             : /*
     695                 :             :  * DropRelationAllLocalBuffers
     696                 :             :  *              This function removes from the buffer pool all pages of all forks
     697                 :             :  *              of the specified relation.
     698                 :             :  *
     699                 :             :  *              See DropRelationsAllBuffers in bufmgr.c for more notes.
     700                 :             :  */
     701                 :             : void
     702                 :        1012 : DropRelationAllLocalBuffers(RelFileLocator rlocator)
     703                 :             : {
     704                 :        1012 :         int                     i;
     705                 :             : 
     706         [ +  + ]:      959852 :         for (i = 0; i < NLocBuffer; i++)
     707                 :             :         {
     708                 :      958840 :                 BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
     709                 :      958840 :                 uint64          buf_state;
     710                 :             : 
     711                 :      958840 :                 buf_state = pg_atomic_read_u64(&bufHdr->state);
     712                 :             : 
     713   [ +  +  +  + ]:      958840 :                 if ((buf_state & BM_TAG_VALID) &&
     714                 :       74894 :                         BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator))
     715                 :             :                 {
     716                 :        4825 :                         InvalidateLocalBuffer(bufHdr, true);
     717                 :        4825 :                 }
     718                 :      958840 :         }
     719                 :        1012 : }
     720                 :             : 
     721                 :             : /*
     722                 :             :  * InitLocalBuffers -
     723                 :             :  *        init the local buffer cache. Since most queries (esp. multi-user ones)
     724                 :             :  *        don't involve local buffers, we delay allocating actual memory for the
     725                 :             :  *        buffers until we need them; just make the buffer headers here.
     726                 :             :  */
     727                 :             : static void
     728                 :          77 : InitLocalBuffers(void)
     729                 :             : {
     730                 :          77 :         int                     nbufs = num_temp_buffers;
     731                 :          77 :         HASHCTL         info;
     732                 :          77 :         int                     i;
     733                 :             : 
     734                 :             :         /*
     735                 :             :          * Parallel workers can't access data in temporary tables, because they
     736                 :             :          * have no visibility into the local buffers of their leader.  This is a
     737                 :             :          * convenient, low-cost place to provide a backstop check for that.  Note
     738                 :             :          * that we don't wish to prevent a parallel worker from accessing catalog
     739                 :             :          * metadata about a temp table, so checks at higher levels would be
     740                 :             :          * inappropriate.
     741                 :             :          */
     742         [ +  - ]:          77 :         if (IsParallelWorker())
     743   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     744                 :             :                                 (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
     745                 :             :                                  errmsg("cannot access temporary tables during a parallel operation")));
     746                 :             : 
     747                 :             :         /* Allocate and zero buffer headers and auxiliary arrays */
     748                 :          77 :         LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
     749                 :          77 :         LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
     750                 :          77 :         LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
     751         [ +  - ]:          77 :         if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
     752   [ #  #  #  # ]:           0 :                 ereport(FATAL,
     753                 :             :                                 (errcode(ERRCODE_OUT_OF_MEMORY),
     754                 :             :                                  errmsg("out of memory")));
     755                 :             : 
     756                 :          77 :         nextFreeLocalBufId = 0;
     757                 :             : 
     758                 :             :         /* initialize fields that need to start off nonzero */
     759         [ +  + ]:       77077 :         for (i = 0; i < nbufs; i++)
     760                 :             :         {
     761                 :       77000 :                 BufferDesc *buf = GetLocalBufferDescriptor(i);
     762                 :             : 
     763                 :             :                 /*
     764                 :             :                  * negative to indicate local buffer. This is tricky: shared buffers
     765                 :             :                  * start with 0. We have to start with -2. (Note that the routine
     766                 :             :                  * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
     767                 :             :                  * is -1.)
     768                 :             :                  */
     769                 :       77000 :                 buf->buf_id = -i - 2;
     770                 :             : 
     771                 :       77000 :                 pgaio_wref_clear(&buf->io_wref);
     772                 :             : 
     773                 :             :                 /*
     774                 :             :                  * Intentionally do not initialize the buffer's atomic variable
     775                 :             :                  * (besides zeroing the underlying memory above). That way we get
     776                 :             :                  * errors on platforms without atomics, if somebody (re-)introduces
     777                 :             :                  * atomic operations for local buffers.
     778                 :             :                  */
     779                 :       77000 :         }
     780                 :             : 
     781                 :             :         /* Create the lookup hash table */
     782                 :          77 :         info.keysize = sizeof(BufferTag);
     783                 :          77 :         info.entrysize = sizeof(LocalBufferLookupEnt);
     784                 :             : 
     785                 :          77 :         LocalBufHash = hash_create("Local Buffer Lookup Table",
     786                 :          77 :                                                            nbufs,
     787                 :             :                                                            &info,
     788                 :             :                                                            HASH_ELEM | HASH_BLOBS);
     789                 :             : 
     790         [ +  - ]:          77 :         if (!LocalBufHash)
     791   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not initialize local buffer hash table");
     792                 :             : 
     793                 :             :         /* Initialization done, mark buffers allocated */
     794                 :          77 :         NLocBuffer = nbufs;
     795                 :          77 : }
     796                 :             : 
     797                 :             : /*
     798                 :             :  * XXX: We could have a slightly more efficient version of PinLocalBuffer()
     799                 :             :  * that does not support adjusting the usagecount - but so far it does not
     800                 :             :  * seem worth the trouble.
     801                 :             :  *
     802                 :             :  * Note that ResourceOwnerEnlarge() must have been done already.
     803                 :             :  */
     804                 :             : bool
     805                 :      357992 : PinLocalBuffer(BufferDesc *buf_hdr, bool adjust_usagecount)
     806                 :             : {
     807                 :      357992 :         uint64          buf_state;
     808                 :      357992 :         Buffer          buffer = BufferDescriptorGetBuffer(buf_hdr);
     809                 :      357992 :         int                     bufid = -buffer - 1;
     810                 :             : 
     811                 :      357992 :         buf_state = pg_atomic_read_u64(&buf_hdr->state);
     812                 :             : 
     813         [ +  + ]:      357992 :         if (LocalRefCount[bufid] == 0)
     814                 :             :         {
     815                 :      336018 :                 NLocalPinnedBuffers++;
     816                 :      336018 :                 buf_state += BUF_REFCOUNT_ONE;
     817   [ +  +  +  + ]:      336018 :                 if (adjust_usagecount &&
     818                 :      328956 :                         BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)
     819                 :             :                 {
     820                 :       18831 :                         buf_state += BUF_USAGECOUNT_ONE;
     821                 :       18831 :                 }
     822                 :      336018 :                 pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
     823                 :             : 
     824                 :             :                 /*
     825                 :             :                  * See comment in PinBuffer().
     826                 :             :                  *
     827                 :             :                  * If the buffer isn't allocated yet, it'll be marked as defined in
     828                 :             :                  * GetLocalBufferStorage().
     829                 :             :                  */
     830         [ +  + ]:      336018 :                 if (LocalBufHdrGetBlock(buf_hdr) != NULL)
     831                 :      331192 :                         VALGRIND_MAKE_MEM_DEFINED(LocalBufHdrGetBlock(buf_hdr), BLCKSZ);
     832                 :      336018 :         }
     833                 :      357992 :         LocalRefCount[bufid]++;
     834                 :      715984 :         ResourceOwnerRememberBuffer(CurrentResourceOwner,
     835                 :      357992 :                                                                 BufferDescriptorGetBuffer(buf_hdr));
     836                 :             : 
     837                 :      715984 :         return buf_state & BM_VALID;
     838                 :      357992 : }
     839                 :             : 
     840                 :             : void
     841                 :      471623 : UnpinLocalBuffer(Buffer buffer)
     842                 :             : {
     843                 :      471623 :         UnpinLocalBufferNoOwner(buffer);
     844                 :      471623 :         ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
     845                 :      471623 : }
     846                 :             : 
     847                 :             : void
     848                 :      472629 : UnpinLocalBufferNoOwner(Buffer buffer)
     849                 :             : {
     850                 :      472629 :         int                     buffid = -buffer - 1;
     851                 :             : 
     852         [ +  - ]:      472629 :         Assert(BufferIsLocal(buffer));
     853         [ +  - ]:      472629 :         Assert(LocalRefCount[buffid] > 0);
     854         [ +  - ]:      472629 :         Assert(NLocalPinnedBuffers > 0);
     855                 :             : 
     856         [ +  + ]:      472629 :         if (--LocalRefCount[buffid] == 0)
     857                 :             :         {
     858                 :      336018 :                 BufferDesc *buf_hdr = GetLocalBufferDescriptor(buffid);
     859                 :      336018 :                 uint64          buf_state;
     860                 :             : 
     861                 :      336018 :                 NLocalPinnedBuffers--;
     862                 :             : 
     863                 :      336018 :                 buf_state = pg_atomic_read_u64(&buf_hdr->state);
     864         [ +  - ]:      336018 :                 Assert(BUF_STATE_GET_REFCOUNT(buf_state) > 0);
     865                 :      336018 :                 buf_state -= BUF_REFCOUNT_ONE;
     866                 :      336018 :                 pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
     867                 :             : 
     868                 :             :                 /* see comment in UnpinBufferNoOwner */
     869                 :      336018 :                 VALGRIND_MAKE_MEM_NOACCESS(LocalBufHdrGetBlock(buf_hdr), BLCKSZ);
     870                 :      336018 :         }
     871                 :      472629 : }
     872                 :             : 
     873                 :             : /*
     874                 :             :  * GUC check_hook for temp_buffers
     875                 :             :  */
     876                 :             : bool
     877                 :           8 : check_temp_buffers(int *newval, void **extra, GucSource source)
     878                 :             : {
     879                 :             :         /*
     880                 :             :          * Once local buffers have been initialized, it's too late to change this.
     881                 :             :          * However, if this is only a test call, allow it.
     882                 :             :          */
     883   [ +  -  -  +  :           8 :         if (source != PGC_S_TEST && NLocBuffer && NLocBuffer != *newval)
                   #  # ]
     884                 :             :         {
     885                 :           0 :                 GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temporary tables have been accessed in the session.");
     886                 :           0 :                 return false;
     887                 :             :         }
     888                 :           8 :         return true;
     889                 :           8 : }
     890                 :             : 
     891                 :             : /*
     892                 :             :  * GetLocalBufferStorage - allocate memory for a local buffer
     893                 :             :  *
     894                 :             :  * The idea of this function is to aggregate our requests for storage
     895                 :             :  * so that the memory manager doesn't see a whole lot of relatively small
     896                 :             :  * requests.  Since we'll never give back a local buffer once it's created
     897                 :             :  * within a particular process, no point in burdening memmgr with separately
     898                 :             :  * managed chunks.
     899                 :             :  */
     900                 :             : static Block
     901                 :        4826 : GetLocalBufferStorage(void)
     902                 :             : {
     903                 :             :         static char *cur_block = NULL;
     904                 :             :         static int      next_buf_in_block = 0;
     905                 :             :         static int      num_bufs_in_block = 0;
     906                 :             :         static int      total_bufs_allocated = 0;
     907                 :             :         static MemoryContext LocalBufferContext = NULL;
     908                 :             : 
     909                 :        4826 :         char       *this_buf;
     910                 :             : 
     911         [ +  - ]:        4826 :         Assert(total_bufs_allocated < NLocBuffer);
     912                 :             : 
     913         [ +  + ]:        4826 :         if (next_buf_in_block >= num_bufs_in_block)
     914                 :             :         {
     915                 :             :                 /* Need to make a new request to memmgr */
     916                 :         123 :                 int                     num_bufs;
     917                 :             : 
     918                 :             :                 /*
     919                 :             :                  * We allocate local buffers in a context of their own, so that the
     920                 :             :                  * space eaten for them is easily recognizable in MemoryContextStats
     921                 :             :                  * output.  Create the context on first use.
     922                 :             :                  */
     923         [ +  + ]:         123 :                 if (LocalBufferContext == NULL)
     924                 :          77 :                         LocalBufferContext =
     925                 :          77 :                                 AllocSetContextCreate(TopMemoryContext,
     926                 :             :                                                                           "LocalBufferContext",
     927                 :             :                                                                           ALLOCSET_DEFAULT_SIZES);
     928                 :             : 
     929                 :             :                 /* Start with a 16-buffer request; subsequent ones double each time */
     930         [ +  + ]:         123 :                 num_bufs = Max(num_bufs_in_block * 2, 16);
     931                 :             :                 /* But not more than what we need for all remaining local bufs */
     932         [ +  + ]:         123 :                 num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
     933                 :             :                 /* And don't overflow MaxAllocSize, either */
     934         [ +  - ]:         123 :                 num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
     935                 :             : 
     936                 :             :                 /* Buffers should be I/O aligned. */
     937                 :         246 :                 cur_block = MemoryContextAllocAligned(LocalBufferContext,
     938                 :         123 :                                                                                           num_bufs * BLCKSZ,
     939                 :             :                                                                                           PG_IO_ALIGN_SIZE,
     940                 :             :                                                                                           0);
     941                 :             : 
     942                 :         123 :                 next_buf_in_block = 0;
     943                 :         123 :                 num_bufs_in_block = num_bufs;
     944                 :         123 :         }
     945                 :             : 
     946                 :             :         /* Allocate next buffer in current memory block */
     947                 :        4826 :         this_buf = cur_block + next_buf_in_block * BLCKSZ;
     948                 :        4826 :         next_buf_in_block++;
     949                 :        4826 :         total_bufs_allocated++;
     950                 :             : 
     951                 :             :         /*
     952                 :             :          * Caller's PinLocalBuffer() was too early for Valgrind updates, so do it
     953                 :             :          * here.  The block is actually undefined, but we want consistency with
     954                 :             :          * the regular case of not needing to allocate memory.  This is
     955                 :             :          * specifically needed when method_io_uring.c fills the block, because
     956                 :             :          * Valgrind doesn't recognize io_uring reads causing undefined memory to
     957                 :             :          * become defined.
     958                 :             :          */
     959                 :        4826 :         VALGRIND_MAKE_MEM_DEFINED(this_buf, BLCKSZ);
     960                 :             : 
     961                 :        9652 :         return (Block) this_buf;
     962                 :        4826 : }
     963                 :             : 
     964                 :             : /*
     965                 :             :  * CheckForLocalBufferLeaks - ensure this backend holds no local buffer pins
     966                 :             :  *
     967                 :             :  * This is just like CheckForBufferLeaks(), but for local buffers.
     968                 :             :  */
     969                 :             : static void
     970                 :       58720 : CheckForLocalBufferLeaks(void)
     971                 :             : {
     972                 :             : #ifdef USE_ASSERT_CHECKING
     973         [ +  + ]:       58720 :         if (LocalRefCount)
     974                 :             :         {
     975                 :       18834 :                 int                     RefCountErrors = 0;
     976                 :       18834 :                 int                     i;
     977                 :             : 
     978         [ +  + ]:    19236474 :                 for (i = 0; i < NLocBuffer; i++)
     979                 :             :                 {
     980         [ +  - ]:    19217640 :                         if (LocalRefCount[i] != 0)
     981                 :             :                         {
     982                 :           0 :                                 Buffer          b = -i - 1;
     983                 :           0 :                                 char       *s;
     984                 :             : 
     985                 :           0 :                                 s = DebugPrintBufferRefcount(b);
     986   [ #  #  #  # ]:           0 :                                 elog(WARNING, "local buffer refcount leak: %s", s);
     987                 :           0 :                                 pfree(s);
     988                 :             : 
     989                 :           0 :                                 RefCountErrors++;
     990                 :           0 :                         }
     991                 :    19217640 :                 }
     992         [ +  - ]:       18834 :                 Assert(RefCountErrors == 0);
     993                 :       18834 :         }
     994                 :             : #endif
     995                 :       58720 : }
     996                 :             : 
     997                 :             : /*
     998                 :             :  * AtEOXact_LocalBuffers - clean up at end of transaction.
     999                 :             :  *
    1000                 :             :  * This is just like AtEOXact_Buffers, but for local buffers.
    1001                 :             :  */
    1002                 :             : void
    1003                 :       57914 : AtEOXact_LocalBuffers(bool isCommit)
    1004                 :             : {
    1005                 :       57914 :         CheckForLocalBufferLeaks();
    1006                 :       57914 : }
    1007                 :             : 
    1008                 :             : /*
    1009                 :             :  * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
    1010                 :             :  *
    1011                 :             :  * This is just like AtProcExit_Buffers, but for local buffers.
    1012                 :             :  */
    1013                 :             : void
    1014                 :         806 : AtProcExit_LocalBuffers(void)
    1015                 :             : {
    1016                 :             :         /*
    1017                 :             :          * We shouldn't be holding any remaining pins; if we are, and assertions
    1018                 :             :          * aren't enabled, we'll fail later in DropRelationBuffers while trying to
    1019                 :             :          * drop the temp rels.
    1020                 :             :          */
    1021                 :         806 :         CheckForLocalBufferLeaks();
    1022                 :         806 : }
        

Generated by: LCOV version 2.3.2-1