codekingpro/portable-devtools
115k
1/*-------------------------------------------------------------------------2 *3 * bufpage.h4 * Standard POSTGRES buffer page definitions.5 *6 *7 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group8 * Portions Copyright (c) 1994, Regents of the University of California9 *10 * src/include/storage/bufpage.h11 *12 *-------------------------------------------------------------------------13 */14#ifndef BUFPAGE_H15#define BUFPAGE_H16 17#include "access/xlogdefs.h"18#include "storage/block.h"19#include "storage/item.h"20#include "storage/off.h"21 22/*23 * A postgres disk page is an abstraction layered on top of a postgres24 * disk block (which is simply a unit of i/o, see block.h).25 *26 * specifically, while a disk block can be unformatted, a postgres27 * disk page is always a slotted page of the form:28 *29 * +----------------+---------------------------------+30 * | PageHeaderData | linp1 linp2 linp3 ... |31 * +-----------+----+---------------------------------+32 * | ... linpN | |33 * +-----------+--------------------------------------+34 * | ^ pd_lower |35 * | |36 * | v pd_upper |37 * +-------------+------------------------------------+38 * | | tupleN ... |39 * +-------------+------------------+-----------------+40 * | ... tuple3 tuple2 tuple1 | "special space" |41 * +--------------------------------+-----------------+42 * ^ pd_special43 *44 * a page is full when nothing can be added between pd_lower and45 * pd_upper.46 *47 * all blocks written out by an access method must be disk pages.48 *49 * EXCEPTIONS:50 *51 * obviously, a page is not formatted before it is initialized by52 * a call to PageInit.53 *54 * NOTES:55 *56 * linp1..N form an ItemId (line pointer) array. ItemPointers point57 * to a physical block number and a logical offset (line pointer58 * number) within that block/page. Note that OffsetNumbers59 * conventionally start at 1, not 0.60 *61 * tuple1..N are added "backwards" on the page. Since an ItemPointer62 * offset is used to access an ItemId entry rather than an actual63 * byte-offset position, tuples can be physically shuffled on a page64 * whenever the need arises. This indirection also keeps crash recovery65 * relatively simple, because the low-level details of page space66 * management can be controlled by standard buffer page code during67 * logging, and during recovery.68 *69 * AM-generic per-page information is kept in PageHeaderData.70 *71 * AM-specific per-page data (if any) is kept in the area marked "special72 * space"; each AM has an "opaque" structure defined somewhere that is73 * stored as the page trailer. an access method should always74 * initialize its pages with PageInit and then set its own opaque75 * fields.76 */77 78typedef Pointer Page;79 80 81/*82 * location (byte offset) within a page.83 *84 * note that this is actually limited to 2^15 because we have limited85 * ItemIdData.lp_off and ItemIdData.lp_len to 15 bits (see itemid.h).86 */87typedef uint16 LocationIndex;88 89 90/*91 * For historical reasons, the 64-bit LSN value is stored as two 32-bit92 * values.93 */94typedef struct95{96 uint32 xlogid; /* high bits */97 uint32 xrecoff; /* low bits */98} PageXLogRecPtr;99 100static inline XLogRecPtr101PageXLogRecPtrGet(PageXLogRecPtr val)102{103 return (uint64) val.xlogid << 32 | val.xrecoff;104}105 106#define PageXLogRecPtrSet(ptr, lsn) \107 ((ptr).xlogid = (uint32) ((lsn) >> 32), (ptr).xrecoff = (uint32) (lsn))108 109/*110 * disk page organization111 *112 * space management information generic to any page113 *114 * pd_lsn - identifies xlog record for last change to this page.115 * pd_checksum - page checksum, if set.116 * pd_flags - flag bits.117 * pd_lower - offset to start of free space.118 * pd_upper - offset to end of free space.119 * pd_special - offset to start of special space.120 * pd_pagesize_version - size in bytes and page layout version number.121 * pd_prune_xid - oldest XID among potentially prunable tuples on page.122 *123 * The LSN is used by the buffer manager to enforce the basic rule of WAL:124 * "thou shalt write xlog before data". A dirty buffer cannot be dumped125 * to disk until xlog has been flushed at least as far as the page's LSN.126 *127 * pd_checksum stores the page checksum, if it has been set for this page;128 * zero is a valid value for a checksum. If a checksum is not in use then129 * we leave the field unset. This will typically mean the field is zero130 * though non-zero values may also be present if databases have been131 * pg_upgraded from releases prior to 9.3, when the same byte offset was132 * used to store the current timelineid when the page was last updated.133 * Note that there is no indication on a page as to whether the checksum134 * is valid or not, a deliberate design choice which avoids the problem135 * of relying on the page contents to decide whether to verify it. Hence136 * there are no flag bits relating to checksums.137 *138 * pd_prune_xid is a hint field that helps determine whether pruning will be139 * useful. It is currently unused in index pages.140 *141 * The page version number and page size are packed together into a single142 * uint16 field. This is for historical reasons: before PostgreSQL 7.3,143 * there was no concept of a page version number, and doing it this way144 * lets us pretend that pre-7.3 databases have page version number zero.145 * We constrain page sizes to be multiples of 256, leaving the low eight146 * bits available for a version number.147 *148 * Minimum possible page size is perhaps 64B to fit page header, opaque space149 * and a minimal tuple; of course, in reality you want it much bigger, so150 * the constraint on pagesize mod 256 is not an important restriction.151 * On the high end, we can only support pages up to 32KB because lp_off/lp_len152 * are 15 bits.153 */154 155typedef struct PageHeaderData156{157 /* XXX LSN is member of *any* block, not only page-organized ones */158 PageXLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog159 * record for last change to this page */160 uint16 pd_checksum; /* checksum */161 uint16 pd_flags; /* flag bits, see below */162 LocationIndex pd_lower; /* offset to start of free space */163 LocationIndex pd_upper; /* offset to end of free space */164 LocationIndex pd_special; /* offset to start of special space */165 uint16 pd_pagesize_version;166 TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */167 ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */168} PageHeaderData;169 170typedef PageHeaderData *PageHeader;171 172/*173 * pd_flags contains the following flag bits. Undefined bits are initialized174 * to zero and may be used in the future.175 *176 * PD_HAS_FREE_LINES is set if there are any LP_UNUSED line pointers before177 * pd_lower. This should be considered a hint rather than the truth, since178 * changes to it are not WAL-logged.179 *180 * PD_PAGE_FULL is set if an UPDATE doesn't find enough free space in the181 * page for its new tuple version; this suggests that a prune is needed.182 * Again, this is just a hint.183 */184#define PD_HAS_FREE_LINES 0x0001 /* are there any unused line pointers? */185#define PD_PAGE_FULL 0x0002 /* not enough free space for new tuple? */186#define PD_ALL_VISIBLE 0x0004 /* all tuples on page are visible to187 * everyone */188 189#define PD_VALID_FLAG_BITS 0x0007 /* OR of all valid pd_flags bits */190 191/*192 * Page layout version number 0 is for pre-7.3 Postgres releases.193 * Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.194 * Release 8.0 uses 2; it changed the HeapTupleHeader layout again.195 * Release 8.1 uses 3; it redefined HeapTupleHeader infomask bits.196 * Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and197 * added the pd_flags field (by stealing some bits from pd_tli),198 * as well as adding the pd_prune_xid field (which enlarges the header).199 *200 * As of Release 9.3, the checksum version must also be considered when201 * handling pages.202 */203#define PG_PAGE_LAYOUT_VERSION 4204#define PG_DATA_CHECKSUM_VERSION 1205 206/* ----------------------------------------------------------------207 * page support functions208 * ----------------------------------------------------------------209 */210 211/*212 * line pointer(s) do not count as part of header213 */214#define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))215 216/*217 * PageIsEmpty218 * returns true iff no itemid has been allocated on the page219 */220static inline bool221PageIsEmpty(Page page)222{223 return ((PageHeader) page)->pd_lower <= SizeOfPageHeaderData;224}225 226/*227 * PageIsNew228 * returns true iff page has not been initialized (by PageInit)229 */230static inline bool231PageIsNew(Page page)232{233 return ((PageHeader) page)->pd_upper == 0;234}235 236/*237 * PageGetItemId238 * Returns an item identifier of a page.239 */240static inline ItemId241PageGetItemId(Page page, OffsetNumber offsetNumber)242{243 return &((PageHeader) page)->pd_linp[offsetNumber - 1];244}245 246/*247 * PageGetContents248 * To be used in cases where the page does not contain line pointers.249 *250 * Note: prior to 8.3 this was not guaranteed to yield a MAXALIGN'd result.251 * Now it is. Beware of old code that might think the offset to the contents252 * is just SizeOfPageHeaderData rather than MAXALIGN(SizeOfPageHeaderData).253 */254static inline char *255PageGetContents(Page page)256{257 return (char *) page + MAXALIGN(SizeOfPageHeaderData);258}259 260/* ----------------261 * functions to access page size info262 * ----------------263 */264 265/*266 * PageGetPageSize267 * Returns the page size of a page.268 *269 * this can only be called on a formatted page (unlike270 * BufferGetPageSize, which can be called on an unformatted page).271 * however, it can be called on a page that is not stored in a buffer.272 */273static inline Size274PageGetPageSize(Page page)275{276 return (Size) (((PageHeader) page)->pd_pagesize_version & (uint16) 0xFF00);277}278 279/*280 * PageGetPageLayoutVersion281 * Returns the page layout version of a page.282 */283static inline uint8284PageGetPageLayoutVersion(Page page)285{286 return (((PageHeader) page)->pd_pagesize_version & 0x00FF);287}288 289/*290 * PageSetPageSizeAndVersion291 * Sets the page size and page layout version number of a page.292 *293 * We could support setting these two values separately, but there's294 * no real need for it at the moment.295 */296static inline void297PageSetPageSizeAndVersion(Page page, Size size, uint8 version)298{299 Assert((size & 0xFF00) == size);300 Assert((version & 0x00FF) == version);301 302 ((PageHeader) page)->pd_pagesize_version = size | version;303}304 305/* ----------------306 * page special data functions307 * ----------------308 */309/*310 * PageGetSpecialSize311 * Returns size of special space on a page.312 */313static inline uint16314PageGetSpecialSize(Page page)315{316 return (PageGetPageSize(page) - ((PageHeader) page)->pd_special);317}318 319/*320 * Using assertions, validate that the page special pointer is OK.321 *322 * This is intended to catch use of the pointer before page initialization.323 */324static inline void325PageValidateSpecialPointer(Page page)326{327 Assert(page);328 Assert(((PageHeader) page)->pd_special <= BLCKSZ);329 Assert(((PageHeader) page)->pd_special >= SizeOfPageHeaderData);330}331 332/*333 * PageGetSpecialPointer334 * Returns pointer to special space on a page.335 */336static inline char *337PageGetSpecialPointer(Page page)338{339 PageValidateSpecialPointer(page);340 return (char *) page + ((PageHeader) page)->pd_special;341}342 343/*344 * PageGetItem345 * Retrieves an item on the given page.346 *347 * Note:348 * This does not change the status of any of the resources passed.349 * The semantics may change in the future.350 */351static inline Item352PageGetItem(Page page, ItemId itemId)353{354 Assert(page);355 Assert(ItemIdHasStorage(itemId));356 357 return (Item) (((char *) page) + ItemIdGetOffset(itemId));358}359 360/*361 * PageGetMaxOffsetNumber362 * Returns the maximum offset number used by the given page.363 * Since offset numbers are 1-based, this is also the number364 * of items on the page.365 *366 * NOTE: if the page is not initialized (pd_lower == 0), we must367 * return zero to ensure sane behavior.368 */369static inline OffsetNumber370PageGetMaxOffsetNumber(Page page)371{372 PageHeader pageheader = (PageHeader) page;373 374 if (pageheader->pd_lower <= SizeOfPageHeaderData)375 return 0;376 else377 return (pageheader->pd_lower - SizeOfPageHeaderData) / sizeof(ItemIdData);378}379 380/*381 * Additional functions for access to page headers.382 */383static inline XLogRecPtr384PageGetLSN(Page page)385{386 return PageXLogRecPtrGet(((PageHeader) page)->pd_lsn);387}388static inline void389PageSetLSN(Page page, XLogRecPtr lsn)390{391 PageXLogRecPtrSet(((PageHeader) page)->pd_lsn, lsn);392}393 394static inline bool395PageHasFreeLinePointers(Page page)396{397 return ((PageHeader) page)->pd_flags & PD_HAS_FREE_LINES;398}399static inline void400PageSetHasFreeLinePointers(Page page)401{402 ((PageHeader) page)->pd_flags |= PD_HAS_FREE_LINES;403}404static inline void405PageClearHasFreeLinePointers(Page page)406{407 ((PageHeader) page)->pd_flags &= ~PD_HAS_FREE_LINES;408}409 410static inline bool411PageIsFull(Page page)412{413 return ((PageHeader) page)->pd_flags & PD_PAGE_FULL;414}415static inline void416PageSetFull(Page page)417{418 ((PageHeader) page)->pd_flags |= PD_PAGE_FULL;419}420static inline void421PageClearFull(Page page)422{423 ((PageHeader) page)->pd_flags &= ~PD_PAGE_FULL;424}425 426static inline bool427PageIsAllVisible(Page page)428{429 return ((PageHeader) page)->pd_flags & PD_ALL_VISIBLE;430}431static inline void432PageSetAllVisible(Page page)433{434 ((PageHeader) page)->pd_flags |= PD_ALL_VISIBLE;435}436static inline void437PageClearAllVisible(Page page)438{439 ((PageHeader) page)->pd_flags &= ~PD_ALL_VISIBLE;440}441 442/*443 * These two require "access/transam.h", so left as macros.444 */445#define PageSetPrunable(page, xid) \446do { \447 Assert(TransactionIdIsNormal(xid)); \448 if (!TransactionIdIsValid(((PageHeader) (page))->pd_prune_xid) || \449 TransactionIdPrecedes(xid, ((PageHeader) (page))->pd_prune_xid)) \450 ((PageHeader) (page))->pd_prune_xid = (xid); \451} while (0)452#define PageClearPrunable(page) \453 (((PageHeader) (page))->pd_prune_xid = InvalidTransactionId)454 455 456/* ----------------------------------------------------------------457 * extern declarations458 * ----------------------------------------------------------------459 */460 461/* flags for PageAddItemExtended() */462#define PAI_OVERWRITE (1 << 0)463#define PAI_IS_HEAP (1 << 1)464 465/* flags for PageIsVerifiedExtended() */466#define PIV_LOG_WARNING (1 << 0)467#define PIV_REPORT_STAT (1 << 1)468 469#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap) \470 PageAddItemExtended(page, item, size, offsetNumber, \471 ((overwrite) ? PAI_OVERWRITE : 0) | \472 ((is_heap) ? PAI_IS_HEAP : 0))473 474#define PageIsVerified(page, blkno) \475 PageIsVerifiedExtended(page, blkno, \476 PIV_LOG_WARNING | PIV_REPORT_STAT)477 478/*479 * Check that BLCKSZ is a multiple of sizeof(size_t). In480 * PageIsVerifiedExtended(), it is much faster to check if a page is481 * full of zeroes using the native word size. Note that this assertion482 * is kept within a header to make sure that StaticAssertDecl() works483 * across various combinations of platforms and compilers.484 */485StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),486 "BLCKSZ has to be a multiple of sizeof(size_t)");487 488extern void PageInit(Page page, Size pageSize, Size specialSize);489extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);490extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,491 OffsetNumber offsetNumber, int flags);492extern Page PageGetTempPage(Page page);493extern Page PageGetTempPageCopy(Page page);494extern Page PageGetTempPageCopySpecial(Page page);495extern void PageRestoreTempPage(Page tempPage, Page oldPage);496extern void PageRepairFragmentation(Page page);497extern void PageTruncateLinePointerArray(Page page);498extern Size PageGetFreeSpace(Page page);499extern Size PageGetFreeSpaceForMultipleTuples(Page page, int ntups);500extern Size PageGetExactFreeSpace(Page page);501extern Size PageGetHeapFreeSpace(Page page);502extern void PageIndexTupleDelete(Page page, OffsetNumber offnum);503extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);504extern void PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum);505extern bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum,506 Item newtup, Size newsize);507extern char *PageSetChecksumCopy(Page page, BlockNumber blkno);508extern void PageSetChecksumInplace(Page page, BlockNumber blkno);509 510#endif /* BUFPAGE_H */511 