PostgreSQL Source Code  git master
freespace.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "storage/freespace.h"
#include "storage/fsm_internals.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
Include dependency graph for freespace.c:

Go to the source code of this file.

Data Structures

struct  FSMAddress
 

Macros

#define FSM_CATEGORIES   256
 
#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)
 
#define MaxFSMRequestSize   MaxHeapTupleSize
 
#define FSM_TREE_DEPTH   ((SlotsPerFSMPage >= 1626) ? 3 : 4)
 
#define FSM_ROOT_LEVEL   (FSM_TREE_DEPTH - 1)
 
#define FSM_BOTTOM_LEVEL   0
 

Functions

static FSMAddress fsm_get_child (FSMAddress parent, uint16 slot)
 
static FSMAddress fsm_get_parent (FSMAddress child, uint16 *slot)
 
static FSMAddress fsm_get_location (BlockNumber heapblk, uint16 *slot)
 
static BlockNumber fsm_get_heap_blk (FSMAddress addr, uint16 slot)
 
static BlockNumber fsm_logical_to_physical (FSMAddress addr)
 
static Buffer fsm_readbuf (Relation rel, FSMAddress addr, bool extend)
 
static Buffer fsm_extend (Relation rel, BlockNumber fsm_nblocks)
 
static uint8 fsm_space_avail_to_cat (Size avail)
 
static uint8 fsm_space_needed_to_cat (Size needed)
 
static Size fsm_space_cat_to_avail (uint8 cat)
 
static int fsm_set_and_search (Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
 
static BlockNumber fsm_search (Relation rel, uint8 min_cat)
 
static uint8 fsm_vacuum_page (Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof_p)
 
BlockNumber GetPageWithFreeSpace (Relation rel, Size spaceNeeded)
 
BlockNumber RecordAndGetPageWithFreeSpace (Relation rel, BlockNumber oldPage, Size oldSpaceAvail, Size spaceNeeded)
 
void RecordPageWithFreeSpace (Relation rel, BlockNumber heapBlk, Size spaceAvail)
 
void XLogRecordPageWithFreeSpace (RelFileLocator rlocator, BlockNumber heapBlk, Size spaceAvail)
 
Size GetRecordedFreeSpace (Relation rel, BlockNumber heapBlk)
 
BlockNumber FreeSpaceMapPrepareTruncateRel (Relation rel, BlockNumber nblocks)
 
void FreeSpaceMapVacuum (Relation rel)
 
void FreeSpaceMapVacuumRange (Relation rel, BlockNumber start, BlockNumber end)
 

Variables

static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0}
 

Macro Definition Documentation

◆ FSM_BOTTOM_LEVEL

#define FSM_BOTTOM_LEVEL   0

Definition at line 78 of file freespace.c.

◆ FSM_CAT_STEP

#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)

Definition at line 65 of file freespace.c.

◆ FSM_CATEGORIES

#define FSM_CATEGORIES   256

Definition at line 64 of file freespace.c.

◆ FSM_ROOT_LEVEL

#define FSM_ROOT_LEVEL   (FSM_TREE_DEPTH - 1)

Definition at line 77 of file freespace.c.

◆ FSM_TREE_DEPTH

#define FSM_TREE_DEPTH   ((SlotsPerFSMPage >= 1626) ? 3 : 4)

Definition at line 75 of file freespace.c.

◆ MaxFSMRequestSize

#define MaxFSMRequestSize   MaxHeapTupleSize

Definition at line 66 of file freespace.c.

Function Documentation

◆ FreeSpaceMapPrepareTruncateRel()

BlockNumber FreeSpaceMapPrepareTruncateRel ( Relation  rel,
BlockNumber  nblocks 
)

Definition at line 263 of file freespace.c.

264 {
265  BlockNumber new_nfsmblocks;
266  FSMAddress first_removed_address;
267  uint16 first_removed_slot;
268  Buffer buf;
269 
270  /*
271  * If no FSM has been created yet for this relation, there's nothing to
272  * truncate.
273  */
275  return InvalidBlockNumber;
276 
277  /* Get the location in the FSM of the first removed heap block */
278  first_removed_address = fsm_get_location(nblocks, &first_removed_slot);
279 
280  /*
281  * Zero out the tail of the last remaining FSM page. If the slot
282  * representing the first removed heap block is at a page boundary, as the
283  * first slot on the FSM page that first_removed_address points to, we can
284  * just truncate that page altogether.
285  */
286  if (first_removed_slot > 0)
287  {
288  buf = fsm_readbuf(rel, first_removed_address, false);
289  if (!BufferIsValid(buf))
290  return InvalidBlockNumber; /* nothing to do; the FSM was already
291  * smaller */
293 
294  /* NO EREPORT(ERROR) from here till changes are logged */
296 
297  fsm_truncate_avail(BufferGetPage(buf), first_removed_slot);
298 
299  /*
300  * Truncation of a relation is WAL-logged at a higher-level, and we
301  * will be called at WAL replay. But if checksums are enabled, we need
302  * to still write a WAL record to protect against a torn page, if the
303  * page is flushed to disk before the truncation WAL record. We cannot
304  * use MarkBufferDirtyHint here, because that will not dirty the page
305  * during recovery.
306  */
309  log_newpage_buffer(buf, false);
310 
312 
314 
315  new_nfsmblocks = fsm_logical_to_physical(first_removed_address) + 1;
316  }
317  else
318  {
319  new_nfsmblocks = fsm_logical_to_physical(first_removed_address);
320  if (smgrnblocks(RelationGetSmgr(rel), FSM_FORKNUM) <= new_nfsmblocks)
321  return InvalidBlockNumber; /* nothing to do; the FSM was already
322  * smaller */
323  }
324 
325  return new_nfsmblocks;
326 }
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4578
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2190
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4796
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:159
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:301
unsigned short uint16
Definition: c.h:494
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:432
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:468
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:531
bool fsm_truncate_avail(Page page, int nslots)
Definition: fsmpage.c:313
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
static char * buf
Definition: pg_test_fsync.c:73
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:566
#define RelationNeedsWAL(relation)
Definition: rel.h:627
@ FSM_FORKNUM
Definition: relpath.h:51
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:658
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:401
#define XLogHintBitIsNeeded()
Definition: xlog.h:118
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1238
bool InRecovery
Definition: xlogutils.c:53

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), BufferIsValid(), END_CRIT_SECTION, FSM_FORKNUM, fsm_get_location(), fsm_logical_to_physical(), fsm_readbuf(), fsm_truncate_avail(), InRecovery, InvalidBlockNumber, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), RelationGetSmgr(), RelationNeedsWAL, smgrexists(), smgrnblocks(), START_CRIT_SECTION, UnlockReleaseBuffer(), and XLogHintBitIsNeeded.

Referenced by RelationTruncate(), and smgr_redo().

◆ FreeSpaceMapVacuum()

void FreeSpaceMapVacuum ( Relation  rel)

Definition at line 335 of file freespace.c.

336 {
337  bool dummy;
338 
339  /* Recursively scan the tree, starting at the root */
340  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS,
342  &dummy);
343 }
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof_p)
Definition: freespace.c:753
static const FSMAddress FSM_ROOT_ADDRESS
Definition: freespace.c:91

References FSM_ROOT_ADDRESS, fsm_vacuum_page(), and InvalidBlockNumber.

Referenced by brin_vacuum_scan(), and IndexFreeSpaceMapVacuum().

◆ FreeSpaceMapVacuumRange()

void FreeSpaceMapVacuumRange ( Relation  rel,
BlockNumber  start,
BlockNumber  end 
)

Definition at line 354 of file freespace.c.

355 {
356  bool dummy;
357 
358  /* Recursively scan the tree, starting at the root */
359  if (end > start)
360  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, start, end, &dummy);
361 }

References FSM_ROOT_ADDRESS, and fsm_vacuum_page().

Referenced by brin_doinsert(), brin_doupdate(), brin_getinsertbuffer(), lazy_scan_heap(), RelationAddBlocks(), RelationTruncate(), smgr_redo(), and terminate_brin_buildstate().

◆ fsm_extend()

static Buffer fsm_extend ( Relation  rel,
BlockNumber  fsm_nblocks 
)
static

Definition at line 606 of file freespace.c.

607 {
608  return ExtendBufferedRelTo(BMR_REL(rel), FSM_FORKNUM, NULL,
611  fsm_nblocks,
613 }
Buffer ExtendBufferedRelTo(BufferManagerRelation bmr, ForkNumber fork, BufferAccessStrategy strategy, uint32 flags, BlockNumber extend_to, ReadBufferMode mode)
Definition: bufmgr.c:903
@ EB_CLEAR_SIZE_CACHE
Definition: bufmgr.h:88
@ EB_CREATE_FORK_IF_NEEDED
Definition: bufmgr.h:82
@ RBM_ZERO_ON_ERROR
Definition: bufmgr.h:49
#define BMR_REL(p_rel)
Definition: bufmgr.h:106

References BMR_REL, EB_CLEAR_SIZE_CACHE, EB_CREATE_FORK_IF_NEEDED, ExtendBufferedRelTo(), FSM_FORKNUM, and RBM_ZERO_ON_ERROR.

Referenced by fsm_readbuf().

◆ fsm_get_child()

static FSMAddress fsm_get_child ( FSMAddress  parent,
uint16  slot 
)
static

Definition at line 512 of file freespace.c.

513 {
514  FSMAddress child;
515 
516  Assert(parent.level > FSM_BOTTOM_LEVEL);
517 
518  child.level = parent.level - 1;
519  child.logpageno = parent.logpageno * SlotsPerFSMPage + slot;
520 
521  return child;
522 }
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:78
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
Assert(fmt[strlen(fmt) - 1] !='\n')
int logpageno
Definition: freespace.c:87
int level
Definition: freespace.c:86

References Assert(), FSM_BOTTOM_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and fsm_vacuum_page().

◆ fsm_get_heap_blk()

static BlockNumber fsm_get_heap_blk ( FSMAddress  addr,
uint16  slot 
)
static

Definition at line 483 of file freespace.c.

484 {
485  Assert(addr.level == FSM_BOTTOM_LEVEL);
486  return ((unsigned int) addr.logpageno) * SlotsPerFSMPage + slot;
487 }

References Assert(), FSM_BOTTOM_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and RecordAndGetPageWithFreeSpace().

◆ fsm_get_location()

static FSMAddress fsm_get_location ( BlockNumber  heapblk,
uint16 slot 
)
static

Definition at line 468 of file freespace.c.

469 {
470  FSMAddress addr;
471 
472  addr.level = FSM_BOTTOM_LEVEL;
473  addr.logpageno = heapblk / SlotsPerFSMPage;
474  *slot = heapblk % SlotsPerFSMPage;
475 
476  return addr;
477 }

References FSM_BOTTOM_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_vacuum_page(), GetRecordedFreeSpace(), RecordAndGetPageWithFreeSpace(), RecordPageWithFreeSpace(), and XLogRecordPageWithFreeSpace().

◆ fsm_get_parent()

static FSMAddress fsm_get_parent ( FSMAddress  child,
uint16 slot 
)
static

Definition at line 494 of file freespace.c.

495 {
496  FSMAddress parent;
497 
498  Assert(child.level < FSM_ROOT_LEVEL);
499 
500  parent.level = child.level + 1;
501  parent.logpageno = child.logpageno / SlotsPerFSMPage;
502  *slot = child.logpageno % SlotsPerFSMPage;
503 
504  return parent;
505 }
#define FSM_ROOT_LEVEL
Definition: freespace.c:77

References Assert(), FSM_ROOT_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and fsm_vacuum_page().

◆ fsm_logical_to_physical()

static BlockNumber fsm_logical_to_physical ( FSMAddress  addr)
static

Definition at line 432 of file freespace.c.

433 {
434  BlockNumber pages;
435  int leafno;
436  int l;
437 
438  /*
439  * Calculate the logical page number of the first leaf page below the
440  * given page.
441  */
442  leafno = addr.logpageno;
443  for (l = 0; l < addr.level; l++)
444  leafno *= SlotsPerFSMPage;
445 
446  /* Count upper level nodes required to address the leaf page */
447  pages = 0;
448  for (l = 0; l < FSM_TREE_DEPTH; l++)
449  {
450  pages += leafno + 1;
451  leafno /= SlotsPerFSMPage;
452  }
453 
454  /*
455  * If the page we were asked for wasn't at the bottom level, subtract the
456  * additional lower level pages we counted above.
457  */
458  pages -= addr.level;
459 
460  /* Turn the page count into 0-based block number */
461  return pages - 1;
462 }
#define FSM_TREE_DEPTH
Definition: freespace.c:75

References FSM_TREE_DEPTH, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_readbuf(), and XLogRecordPageWithFreeSpace().

◆ fsm_readbuf()

static Buffer fsm_readbuf ( Relation  rel,
FSMAddress  addr,
bool  extend 
)
static

Definition at line 531 of file freespace.c.

532 {
533  BlockNumber blkno = fsm_logical_to_physical(addr);
534  Buffer buf;
535  SMgrRelation reln = RelationGetSmgr(rel);
536 
537  /*
538  * If we haven't cached the size of the FSM yet, check it first. Also
539  * recheck if the requested block seems to be past end, since our cached
540  * value might be stale. (We send smgr inval messages on truncation, but
541  * not on extension.)
542  */
544  blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
545  {
546  /* Invalidate the cache so smgrnblocks asks the kernel. */
548  if (smgrexists(reln, FSM_FORKNUM))
549  smgrnblocks(reln, FSM_FORKNUM);
550  else
551  reln->smgr_cached_nblocks[FSM_FORKNUM] = 0;
552  }
553 
554  /*
555  * For reading we use ZERO_ON_ERROR mode, and initialize the page if
556  * necessary. The FSM information is not accurate anyway, so it's better
557  * to clear corrupt pages than error out. Since the FSM changes are not
558  * WAL-logged, the so-called torn page problem on crash can lead to pages
559  * with corrupt headers, for example.
560  *
561  * We use the same path below to initialize pages when extending the
562  * relation, as a concurrent extension can end up with vm_extend()
563  * returning an already-initialized page.
564  */
565  if (blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
566  {
567  if (extend)
568  buf = fsm_extend(rel, blkno + 1);
569  else
570  return InvalidBuffer;
571  }
572  else
573  buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR, NULL);
574 
575  /*
576  * Initializing the page when needed is trickier than it looks, because of
577  * the possibility of multiple backends doing this concurrently, and our
578  * desire to not uselessly take the buffer lock in the normal path where
579  * the page is OK. We must take the lock to initialize the page, so
580  * recheck page newness after we have the lock, in case someone else
581  * already did it. Also, because we initially check PageIsNew with no
582  * lock, it's possible to fall through and return the buffer while someone
583  * else is still initializing the page (i.e., we might see pd_upper as set
584  * but other page header fields are still zeroes). This is harmless for
585  * callers that will take a buffer lock themselves, but some callers
586  * inspect the page without any lock at all. The latter is OK only so
587  * long as it doesn't depend on the page header having correct contents.
588  * Current usage is safe because PageGetContents() does not require that.
589  */
591  {
594  PageInit(BufferGetPage(buf), BLCKSZ, 0);
596  }
597  return buf;
598 }
#define InvalidBuffer
Definition: buf.h:25
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:782
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:157
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42
static bool PageIsNew(Page page)
Definition: bufpage.h:230
static Buffer fsm_extend(Relation rel, BlockNumber fsm_nblocks)
Definition: freespace.c:606
BlockNumber smgr_cached_nblocks[MAX_FORKNUM+1]
Definition: smgr.h:46

References buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), fsm_extend(), FSM_FORKNUM, fsm_logical_to_physical(), InvalidBlockNumber, InvalidBuffer, LockBuffer(), PageInit(), PageIsNew(), RBM_ZERO_ON_ERROR, ReadBufferExtended(), RelationGetSmgr(), SMgrRelationData::smgr_cached_nblocks, smgrexists(), and smgrnblocks().

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_search(), fsm_set_and_search(), fsm_vacuum_page(), and GetRecordedFreeSpace().

◆ fsm_search()

static BlockNumber fsm_search ( Relation  rel,
uint8  min_cat 
)
static

Definition at line 655 of file freespace.c.

656 {
657  int restarts = 0;
659 
660  for (;;)
661  {
662  int slot;
663  Buffer buf;
664  uint8 max_avail = 0;
665 
666  /* Read the FSM page. */
667  buf = fsm_readbuf(rel, addr, false);
668 
669  /* Search within the page */
670  if (BufferIsValid(buf))
671  {
673  slot = fsm_search_avail(buf, min_cat,
674  (addr.level == FSM_BOTTOM_LEVEL),
675  false);
676  if (slot == -1)
677  max_avail = fsm_get_max_avail(BufferGetPage(buf));
679  }
680  else
681  slot = -1;
682 
683  if (slot != -1)
684  {
685  /*
686  * Descend the tree, or return the found block if we're at the
687  * bottom.
688  */
689  if (addr.level == FSM_BOTTOM_LEVEL)
690  return fsm_get_heap_blk(addr, slot);
691 
692  addr = fsm_get_child(addr, slot);
693  }
694  else if (addr.level == FSM_ROOT_LEVEL)
695  {
696  /*
697  * At the root, failure means there's no page with enough free
698  * space in the FSM. Give up.
699  */
700  return InvalidBlockNumber;
701  }
702  else
703  {
704  uint16 parentslot;
705  FSMAddress parent;
706 
707  /*
708  * At lower level, failure can happen if the value in the upper-
709  * level node didn't reflect the value on the lower page. Update
710  * the upper node, to avoid falling into the same trap again, and
711  * start over.
712  *
713  * There's a race condition here, if another backend updates this
714  * page right after we release it, and gets the lock on the parent
715  * page before us. We'll then update the parent page with the now
716  * stale information we had. It's OK, because it should happen
717  * rarely, and will be fixed by the next vacuum.
718  */
719  parent = fsm_get_parent(addr, &parentslot);
720  fsm_set_and_search(rel, parent, parentslot, max_avail, 0);
721 
722  /*
723  * If the upper pages are badly out of date, we might need to loop
724  * quite a few times, updating them as we go. Any inconsistencies
725  * should eventually be corrected and the loop should end. Looping
726  * indefinitely is nevertheless scary, so provide an emergency
727  * valve.
728  */
729  if (restarts++ > 10000)
730  return InvalidBlockNumber;
731 
732  /* Start search all over from the root */
733  addr = FSM_ROOT_ADDRESS;
734  }
735  }
736 }
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:158
unsigned char uint8
Definition: c.h:493
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition: freespace.c:512
static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot)
Definition: freespace.c:494
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:623
static BlockNumber fsm_get_heap_blk(FSMAddress addr, uint16 slot)
Definition: freespace.c:483
uint8 fsm_get_max_avail(Page page)
Definition: fsmpage.c:138
int fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext, bool exclusive_lock_held)
Definition: fsmpage.c:158

References buf, BUFFER_LOCK_SHARE, BufferGetPage(), BufferIsValid(), FSM_BOTTOM_LEVEL, fsm_get_child(), fsm_get_heap_blk(), fsm_get_max_avail(), fsm_get_parent(), fsm_readbuf(), FSM_ROOT_ADDRESS, FSM_ROOT_LEVEL, fsm_search_avail(), fsm_set_and_search(), InvalidBlockNumber, FSMAddress::level, LockBuffer(), and UnlockReleaseBuffer().

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

◆ fsm_set_and_search()

static int fsm_set_and_search ( Relation  rel,
FSMAddress  addr,
uint16  slot,
uint8  newValue,
uint8  minValue 
)
static

Definition at line 623 of file freespace.c.

625 {
626  Buffer buf;
627  Page page;
628  int newslot = -1;
629 
630  buf = fsm_readbuf(rel, addr, true);
632 
633  page = BufferGetPage(buf);
634 
635  if (fsm_set_avail(page, slot, newValue))
636  MarkBufferDirtyHint(buf, false);
637 
638  if (minValue != 0)
639  {
640  /* Search while we still hold the lock */
641  newslot = fsm_search_avail(buf, minValue,
642  addr.level == FSM_BOTTOM_LEVEL,
643  true);
644  }
645 
647 
648  return newslot;
649 }
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:4625
Pointer Page
Definition: bufpage.h:78
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition: fsmpage.c:63

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), FSM_BOTTOM_LEVEL, fsm_readbuf(), fsm_search_avail(), fsm_set_avail(), FSMAddress::level, LockBuffer(), MarkBufferDirtyHint(), and UnlockReleaseBuffer().

Referenced by fsm_search(), RecordAndGetPageWithFreeSpace(), and RecordPageWithFreeSpace().

◆ fsm_space_avail_to_cat()

static uint8 fsm_space_avail_to_cat ( Size  avail)
static

Definition at line 369 of file freespace.c.

370 {
371  int cat;
372 
373  Assert(avail < BLCKSZ);
374 
375  if (avail >= MaxFSMRequestSize)
376  return 255;
377 
378  cat = avail / FSM_CAT_STEP;
379 
380  /*
381  * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
382  * more.
383  */
384  if (cat > 254)
385  cat = 254;
386 
387  return (uint8) cat;
388 }
#define MaxFSMRequestSize
Definition: freespace.c:66
#define FSM_CAT_STEP
Definition: freespace.c:65

References Assert(), FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by RecordAndGetPageWithFreeSpace(), RecordPageWithFreeSpace(), and XLogRecordPageWithFreeSpace().

◆ fsm_space_cat_to_avail()

static Size fsm_space_cat_to_avail ( uint8  cat)
static

Definition at line 395 of file freespace.c.

396 {
397  /* The highest category represents exactly MaxFSMRequestSize bytes. */
398  if (cat == 255)
399  return MaxFSMRequestSize;
400  else
401  return cat * FSM_CAT_STEP;
402 }

References FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetRecordedFreeSpace().

◆ fsm_space_needed_to_cat()

static uint8 fsm_space_needed_to_cat ( Size  needed)
static

Definition at line 409 of file freespace.c.

410 {
411  int cat;
412 
413  /* Can't ask for more space than the highest category represents */
414  if (needed > MaxFSMRequestSize)
415  elog(ERROR, "invalid FSM request size %zu", needed);
416 
417  if (needed == 0)
418  return 1;
419 
420  cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
421 
422  if (cat > 255)
423  cat = 255;
424 
425  return (uint8) cat;
426 }
#define ERROR
Definition: elog.h:39

References elog(), ERROR, FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

◆ fsm_vacuum_page()

static uint8 fsm_vacuum_page ( Relation  rel,
FSMAddress  addr,
BlockNumber  start,
BlockNumber  end,
bool eof_p 
)
static

Definition at line 753 of file freespace.c.

756 {
757  Buffer buf;
758  Page page;
759  uint8 max_avail;
760 
761  /* Read the page if it exists, or return EOF */
762  buf = fsm_readbuf(rel, addr, false);
763  if (!BufferIsValid(buf))
764  {
765  *eof_p = true;
766  return 0;
767  }
768  else
769  *eof_p = false;
770 
771  page = BufferGetPage(buf);
772 
773  /*
774  * If we're above the bottom level, recurse into children, and fix the
775  * information stored about them at this level.
776  */
777  if (addr.level > FSM_BOTTOM_LEVEL)
778  {
779  FSMAddress fsm_start,
780  fsm_end;
781  uint16 fsm_start_slot,
782  fsm_end_slot;
783  int slot,
784  start_slot,
785  end_slot;
786  bool eof = false;
787 
788  /*
789  * Compute the range of slots we need to update on this page, given
790  * the requested range of heap blocks to consider. The first slot to
791  * update is the one covering the "start" block, and the last slot is
792  * the one covering "end - 1". (Some of this work will be duplicated
793  * in each recursive call, but it's cheap enough to not worry about.)
794  */
795  fsm_start = fsm_get_location(start, &fsm_start_slot);
796  fsm_end = fsm_get_location(end - 1, &fsm_end_slot);
797 
798  while (fsm_start.level < addr.level)
799  {
800  fsm_start = fsm_get_parent(fsm_start, &fsm_start_slot);
801  fsm_end = fsm_get_parent(fsm_end, &fsm_end_slot);
802  }
803  Assert(fsm_start.level == addr.level);
804 
805  if (fsm_start.logpageno == addr.logpageno)
806  start_slot = fsm_start_slot;
807  else if (fsm_start.logpageno > addr.logpageno)
808  start_slot = SlotsPerFSMPage; /* shouldn't get here... */
809  else
810  start_slot = 0;
811 
812  if (fsm_end.logpageno == addr.logpageno)
813  end_slot = fsm_end_slot;
814  else if (fsm_end.logpageno > addr.logpageno)
815  end_slot = SlotsPerFSMPage - 1;
816  else
817  end_slot = -1; /* shouldn't get here... */
818 
819  for (slot = start_slot; slot <= end_slot; slot++)
820  {
821  int child_avail;
822 
824 
825  /* After we hit end-of-file, just clear the rest of the slots */
826  if (!eof)
827  child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot),
828  start, end,
829  &eof);
830  else
831  child_avail = 0;
832 
833  /* Update information about the child */
834  if (fsm_get_avail(page, slot) != child_avail)
835  {
837  fsm_set_avail(page, slot, child_avail);
838  MarkBufferDirtyHint(buf, false);
840  }
841  }
842  }
843 
844  /* Now get the maximum value on the page, to return to caller */
845  max_avail = fsm_get_max_avail(page);
846 
847  /*
848  * Reset the next slot pointer. This encourages the use of low-numbered
849  * pages, increasing the chances that a later vacuum can truncate the
850  * relation. We don't bother with a lock here, nor with marking the page
851  * dirty if it wasn't already, since this is just a hint.
852  */
853  ((FSMPage) PageGetContents(page))->fp_next_slot = 0;
854 
856 
857  return max_avail;
858 }
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4561
static char * PageGetContents(Page page)
Definition: bufpage.h:254
FSMPageData * FSMPage
Definition: fsm_internals.h:45
uint8 fsm_get_avail(Page page, int slot)
Definition: fsmpage.c:122
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122

References Assert(), buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), CHECK_FOR_INTERRUPTS, FSM_BOTTOM_LEVEL, fsm_get_avail(), fsm_get_child(), fsm_get_location(), fsm_get_max_avail(), fsm_get_parent(), fsm_readbuf(), fsm_set_avail(), FSMAddress::level, LockBuffer(), FSMAddress::logpageno, MarkBufferDirtyHint(), PageGetContents(), ReleaseBuffer(), and SlotsPerFSMPage.

Referenced by FreeSpaceMapVacuum(), and FreeSpaceMapVacuumRange().

◆ GetPageWithFreeSpace()

BlockNumber GetPageWithFreeSpace ( Relation  rel,
Size  spaceNeeded 
)

Definition at line 133 of file freespace.c.

134 {
135  uint8 min_cat = fsm_space_needed_to_cat(spaceNeeded);
136 
137  return fsm_search(rel, min_cat);
138 }
static uint8 fsm_space_needed_to_cat(Size needed)
Definition: freespace.c:409
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition: freespace.c:655

References fsm_search(), and fsm_space_needed_to_cat().

Referenced by brin_getinsertbuffer(), GetFreeIndexPage(), and RelationGetBufferForTuple().

◆ GetRecordedFreeSpace()

Size GetRecordedFreeSpace ( Relation  rel,
BlockNumber  heapBlk 
)

Definition at line 232 of file freespace.c.

233 {
234  FSMAddress addr;
235  uint16 slot;
236  Buffer buf;
237  uint8 cat;
238 
239  /* Get the location of the FSM byte representing the heap block */
240  addr = fsm_get_location(heapBlk, &slot);
241 
242  buf = fsm_readbuf(rel, addr, false);
243  if (!BufferIsValid(buf))
244  return 0;
245  cat = fsm_get_avail(BufferGetPage(buf), slot);
247 
248  return fsm_space_cat_to_avail(cat);
249 }
static Size fsm_space_cat_to_avail(uint8 cat)
Definition: freespace.c:395

References buf, BufferGetPage(), BufferIsValid(), fsm_get_avail(), fsm_get_location(), fsm_readbuf(), fsm_space_cat_to_avail(), and ReleaseBuffer().

Referenced by lazy_scan_new_or_empty(), pg_freespace(), and statapprox_heap().

◆ RecordAndGetPageWithFreeSpace()

BlockNumber RecordAndGetPageWithFreeSpace ( Relation  rel,
BlockNumber  oldPage,
Size  oldSpaceAvail,
Size  spaceNeeded 
)

Definition at line 150 of file freespace.c.

152 {
153  int old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
154  int search_cat = fsm_space_needed_to_cat(spaceNeeded);
155  FSMAddress addr;
156  uint16 slot;
157  int search_slot;
158 
159  /* Get the location of the FSM byte representing the heap block */
160  addr = fsm_get_location(oldPage, &slot);
161 
162  search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);
163 
164  /*
165  * If fsm_set_and_search found a suitable new block, return that.
166  * Otherwise, search as usual.
167  */
168  if (search_slot != -1)
169  return fsm_get_heap_blk(addr, search_slot);
170  else
171  return fsm_search(rel, search_cat);
172 }
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:369

References fsm_get_heap_blk(), fsm_get_location(), fsm_search(), fsm_set_and_search(), fsm_space_avail_to_cat(), and fsm_space_needed_to_cat().

Referenced by brin_getinsertbuffer(), and RelationGetBufferForTuple().

◆ RecordPageWithFreeSpace()

void RecordPageWithFreeSpace ( Relation  rel,
BlockNumber  heapBlk,
Size  spaceAvail 
)

Definition at line 182 of file freespace.c.

183 {
184  int new_cat = fsm_space_avail_to_cat(spaceAvail);
185  FSMAddress addr;
186  uint16 slot;
187 
188  /* Get the location of the FSM byte representing the heap block */
189  addr = fsm_get_location(heapBlk, &slot);
190 
191  fsm_set_and_search(rel, addr, slot, new_cat, 0);
192 }

References fsm_get_location(), fsm_set_and_search(), and fsm_space_avail_to_cat().

Referenced by brin_doinsert(), brin_doupdate(), brin_initialize_empty_new_buffer(), brin_page_cleanup(), lazy_scan_heap(), lazy_scan_new_or_empty(), lazy_vacuum_heap_rel(), RecordFreeIndexPage(), RecordUsedIndexPage(), RelationAddBlocks(), RelationGetBufferForTuple(), and terminate_brin_buildstate().

◆ XLogRecordPageWithFreeSpace()

void XLogRecordPageWithFreeSpace ( RelFileLocator  rlocator,
BlockNumber  heapBlk,
Size  spaceAvail 
)

Definition at line 199 of file freespace.c.

201 {
202  int new_cat = fsm_space_avail_to_cat(spaceAvail);
203  FSMAddress addr;
204  uint16 slot;
205  BlockNumber blkno;
206  Buffer buf;
207  Page page;
208 
209  /* Get the location of the FSM byte representing the heap block */
210  addr = fsm_get_location(heapBlk, &slot);
211  blkno = fsm_logical_to_physical(addr);
212 
213  /* If the page doesn't exist already, extend */
214  buf = XLogReadBufferExtended(rlocator, FSM_FORKNUM, blkno,
217 
218  page = BufferGetPage(buf);
219  if (PageIsNew(page))
220  PageInit(page, BLCKSZ, 0);
221 
222  if (fsm_set_avail(page, slot, new_cat))
223  MarkBufferDirtyHint(buf, false);
225 }
Buffer XLogReadBufferExtended(RelFileLocator rlocator, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode, Buffer recent_buffer)
Definition: xlogutils.c:474

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), FSM_FORKNUM, fsm_get_location(), fsm_logical_to_physical(), fsm_set_avail(), fsm_space_avail_to_cat(), InvalidBuffer, LockBuffer(), MarkBufferDirtyHint(), PageInit(), PageIsNew(), RBM_ZERO_ON_ERROR, UnlockReleaseBuffer(), and XLogReadBufferExtended().

Referenced by heap_xlog_insert(), heap_xlog_multi_insert(), heap_xlog_prune(), heap_xlog_update(), heap_xlog_vacuum(), and heap_xlog_visible().

Variable Documentation

◆ FSM_ROOT_ADDRESS

const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0}
static

Definition at line 91 of file freespace.c.

Referenced by FreeSpaceMapVacuum(), FreeSpaceMapVacuumRange(), and fsm_search().