diff --git a/contrib/pageinspect/expected/btree.out b/contrib/pageinspect/expected/btree.out index 67b103add3..7f92249c72 100644 --- a/contrib/pageinspect/expected/btree.out +++ b/contrib/pageinspect/expected/btree.out @@ -21,7 +21,7 @@ live_items | 1 dead_items | 0 avg_item_size | 16 page_size | 8192 -free_size | 8128 +free_size | 8132 btpo_prev | 0 btpo_next | 0 btpo | 0 diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out index 5edb650085..60780b4bf7 100644 --- a/contrib/pageinspect/expected/page.out +++ b/contrib/pageinspect/expected/page.out @@ -65,19 +65,19 @@ SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bi SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0)); fsm_page_contents ------------------- - 0: 254 + - 1: 254 + - 3: 254 + - 7: 254 + - 15: 254 + - 31: 254 + - 63: 254 + - 127: 254 + - 255: 254 + - 511: 254 + - 1023: 254 + - 2047: 254 + - 4095: 254 + + 0: 253 + + 1: 253 + + 3: 253 + + 7: 253 + + 15: 253 + + 31: 253 + + 63: 253 + + 127: 253 + + 255: 253 + + 511: 253 + + 1023: 253 + + 2047: 253 + + 4095: 253 + fp_next_slot: 0 + (1 row) diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c index 3d4d4f6f93..4b3deb3f66 100644 --- a/contrib/pageinspect/rawpage.c +++ b/contrib/pageinspect/rawpage.c @@ -270,7 +270,24 @@ page_header(PG_FUNCTION_ARGS) values[5] = UInt16GetDatum(page->pd_special); values[6] = UInt16GetDatum(PageGetPageSize(page)); values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page)); - values[8] = TransactionIdGetDatum(page->pd_prune_xid); + if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HeapPageSpecialData))) + { + HeapPageSpecial pageSpecial = HeapPageGetSpecial(page); + if (pageSpecial->pd_magic == HEAP_PAGE_MAGIC || + pageSpecial->pd_magic == SEQ_PAGE_MAGIC) + { + values[8] = TransactionIdGetDatum(pageSpecial->pd_prune_xid); + nulls[8] = false; + } + else + { + nulls[8] = true; + } + } + else + { + nulls[8] = true; + } /* Build and return the tuple. */ diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index 75317b96a2..e8ac837236 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -620,7 +620,7 @@ pgstathashindex(PG_FUNCTION_ARGS) metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); metap = HashPageGetMeta(BufferGetPage(metabuf)); stats.version = metap->hashm_version; - stats.space_per_page = metap->hashm_bsize; + stats.space_per_page = BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(HashPageOpaqueData)); _hash_relbuf(rel, metabuf); /* Get the current relation length */ diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c index 57021f6ca1..d3808daaa1 100644 --- a/src/backend/access/common/bufmask.c +++ b/src/backend/access/common/bufmask.c @@ -45,11 +45,6 @@ mask_page_lsn_and_checksum(Page page) void mask_page_hint_bits(Page page) { - PageHeader phdr = (PageHeader) page; - - /* Ignore prune_xid (it's like a hint-bit) */ - phdr->pd_prune_xid = MASK_MARKER; - /* Ignore PD_PAGE_FULL and PD_HAS_FREE_LINES flags, they are just hints. */ PageClearFull(page); PageClearHasFreeLinePointers(page); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index dbc8f2d6c7..b061d2de85 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8426,7 +8426,8 @@ heap_xlog_insert(XLogReaderState *record) { buffer = XLogInitBufferForRedo(record, 0); page = BufferGetPage(buffer); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; action = BLK_NEEDS_REDO; } else @@ -8542,7 +8543,8 @@ heap_xlog_multi_insert(XLogReaderState *record) { buffer = XLogInitBufferForRedo(record, 0); page = BufferGetPage(buffer); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; action = BLK_NEEDS_REDO; } else @@ -8756,7 +8758,8 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) { nbuffer = XLogInitBufferForRedo(record, 0); page = (Page) BufferGetPage(nbuffer); - PageInit(page, BufferGetPageSize(nbuffer), 0); + PageInit(page, BufferGetPageSize(nbuffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; newaction = BLK_NEEDS_REDO; } else @@ -9243,6 +9246,10 @@ heap_mask(char *pagedata, BlockNumber blkno) mask_page_lsn_and_checksum(page); mask_page_hint_bits(page); + + /* Ignore prune_xid (it's like a hint-bit) */ + HeapPageGetSpecial(page)->pd_prune_xid = InvalidTransactionId; + mask_unused_space(page); for (off = 1; off <= PageGetMaxOffsetNumber(page); off++) diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 0d7bc68339..cc7d2c36cf 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -206,7 +206,8 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate) /* Extend by one page. */ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); page = BufferGetPage(buffer); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; MarkBufferDirty(buffer); blockNum = BufferGetBlockNumber(buffer); freespace = PageGetHeapFreeSpace(page); @@ -590,7 +591,8 @@ loop: BufferGetBlockNumber(buffer), RelationGetRelationName(relation)); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; if (len > PageGetHeapFreeSpace(page)) { diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index f67d7d15df..2620a3a1c5 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -245,7 +245,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, * Update the page's pd_prune_xid field to either zero, or the lowest * XID of any soon-prunable tuple. */ - ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid; + HeapPageGetSpecial(page)->pd_prune_xid = prstate.new_prune_xid; /* * Also clear the "page is full" flag, since there's no point in @@ -283,10 +283,10 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, * point in repeating the prune/defrag process until something else * happens to the page. */ - if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid || + if (HeapPageGetSpecial(page)->pd_prune_xid != prstate.new_prune_xid || PageIsFull(page)) { - ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid; + HeapPageGetSpecial(page)->pd_prune_xid = prstate.new_prune_xid; PageClearFull(page); MarkBufferDirtyHint(buffer, true); } diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 7d466c2588..5a2b7eea68 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -711,7 +711,8 @@ raw_heap_insert(RewriteState state, HeapTuple tup) if (!state->rs_buffer_valid) { /* Initialize a new empty page */ - PageInit(page, BLCKSZ, 0); + PageInit(page, BLCKSZ, sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; state->rs_buffer_valid = true; } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index ef3ca8c00b..a770423a15 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -53,16 +53,6 @@ */ #define SEQ_LOG_VALS 32 -/* - * The "special area" of a sequence's buffer page looks like this. - */ -#define SEQ_MAGIC 0x1717 - -typedef struct sequence_magic -{ - uint32 magic; -} sequence_magic; - /* * We store a SeqTable item for every sequence we have touched in the current * session. This is needed to hold onto nextval/currval state. (We can't @@ -336,10 +326,9 @@ ResetSequence(Oid seq_relid) static void fill_seq_with_data(Relation rel, HeapTuple tuple) { - Buffer buf; - Page page; - sequence_magic *sm; - OffsetNumber offnum; + Buffer buf; + Page page; + OffsetNumber offnum; /* Initialize first page of relation with special magic number */ @@ -348,9 +337,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple) page = BufferGetPage(buf); - PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer(page); - sm->magic = SEQ_MAGIC; + PageInit(page, BufferGetPageSize(buf), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = SEQ_PAGE_MAGIC; /* Now insert sequence tuple */ @@ -1161,18 +1149,18 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple) { Page page; ItemId lp; - sequence_magic *sm; + HeapPageSpecial pageSpecial; Form_pg_sequence_data seq; *buf = ReadBuffer(rel, 0); LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); page = BufferGetPage(*buf); - sm = (sequence_magic *) PageGetSpecialPointer(page); + pageSpecial = HeapPageGetSpecial(page); - if (sm->magic != SEQ_MAGIC) + if (pageSpecial->pd_magic != SEQ_PAGE_MAGIC) elog(ERROR, "bad magic number in sequence \"%s\": %08X", - RelationGetRelationName(rel), sm->magic); + RelationGetRelationName(rel), pageSpecial->pd_magic); lp = PageGetItemId(page, FirstOffsetNumber); Assert(ItemIdIsNormal(lp)); @@ -1873,7 +1861,6 @@ seq_redo(XLogReaderState *record) char *item; Size itemsz; xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record); - sequence_magic *sm; if (info != XLOG_SEQ_LOG) elog(PANIC, "seq_redo: unknown op code %u", info); @@ -1892,9 +1879,8 @@ seq_redo(XLogReaderState *record) */ localpage = (Page) palloc(BufferGetPageSize(buffer)); - PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer(localpage); - sm->magic = SEQ_MAGIC; + PageInit(localpage, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = SEQ_PAGE_MAGIC; item = (char *) xlrec + sizeof(xl_seq_rec); itemsz = XLogRecGetDataLen(record) - sizeof(xl_seq_rec); diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index cf7f5e1162..90ca4769c7 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -870,7 +870,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, ereport(WARNING, (errmsg("relation \"%s\" page %u is uninitialized --- fixing", relname, blkno))); - PageInit(page, BufferGetPageSize(buf), 0); + PageInit(page, BufferGetPageSize(buf), sizeof(HeapPageSpecialData)); + HeapPageGetSpecial(page)->pd_magic = HEAP_PAGE_MAGIC; empty_pages++; } freespace = PageGetHeapFreeSpace(page); diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index dfbda5458f..98ec6abd82 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -55,7 +55,6 @@ PageInit(Page page, Size pageSize, Size specialSize) p->pd_upper = pageSize - specialSize; p->pd_special = pageSize - specialSize; PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION); - /* p->pd_prune_xid = InvalidTransactionId; done by above MemSet */ } diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 85dd10c45a..af08a9afa3 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -110,7 +110,6 @@ typedef struct * pd_upper - offset to end of free space. * pd_special - offset to start of special space. * pd_pagesize_version - size in bytes and page layout version number. - * pd_prune_xid - oldest XID among potentially prunable tuples on page. * * The LSN is used by the buffer manager to enforce the basic rule of WAL: * "thou shalt write xlog before data". A dirty buffer cannot be dumped @@ -127,9 +126,6 @@ typedef struct * of relying on the page contents to decide whether to verify it. Hence * there are no flag bits relating to checksums. * - * pd_prune_xid is a hint field that helps determine whether pruning will be - * useful. It is currently unused in index pages. - * * The page version number and page size are packed together into a single * uint16 field. This is for historical reasons: before PostgreSQL 7.3, * there was no concept of a page version number, and doing it this way @@ -155,12 +151,47 @@ typedef struct PageHeaderData LocationIndex pd_upper; /* offset to end of free space */ LocationIndex pd_special; /* offset to start of special space */ uint16 pd_pagesize_version; - TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */ } PageHeaderData; typedef PageHeaderData *PageHeader; +/* + * HeapPageSpecialData -- data that stored at the end of each heap page. + * + * pd_prune_xid - oldest XID among potentially prunable tuples on page. + * pd_magic - magic number identifies type of page + * + * pd_prune_xid is a hint field that helps determine whether pruning will be + * useful. It is currently unused in index pages. + * + * pd_magic allows identified an type of object heap page belongs to. + * Currently, heap page may belong to an regular table heap or sequence heap. + */ +typedef struct HeapPageSpecialData +{ + TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ + uint32 pd_magic; /* magic number identifies type of page */ +} HeapPageSpecialData; + +/* + * Possible value for pd_magic field. + */ +#define HEAP_PAGE_MAGIC (0x1010) /* regular heap page */ +#define SEQ_PAGE_MAGIC (0x1717) /* sequence page */ + +typedef HeapPageSpecialData *HeapPageSpecial; + +/* + * Get pointer to HeapPageSpecialData without using pd_special of the page + * (for the sake of speed) assuming all heap pages have same size of special + * data. + */ +#define HeapPageGetSpecial(page) ( \ + AssertMacro(((PageHeader) page)->pd_special == BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData))), \ + (HeapPageSpecial) ((Pointer) page + BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData))) \ +) + /* * pd_flags contains the following flag bits. Undefined bits are initialized * to zero and may be used in the future. @@ -388,18 +419,18 @@ PageValidateSpecialPointer(Page page) #define PageIsPrunable(page, oldestxmin) \ ( \ AssertMacro(TransactionIdIsNormal(oldestxmin)), \ - TransactionIdIsValid(((PageHeader) (page))->pd_prune_xid) && \ - TransactionIdPrecedes(((PageHeader) (page))->pd_prune_xid, oldestxmin) \ + TransactionIdIsValid(HeapPageGetSpecial(page)->pd_prune_xid) && \ + TransactionIdPrecedes(HeapPageGetSpecial(page)->pd_prune_xid, oldestxmin) \ ) #define PageSetPrunable(page, xid) \ do { \ Assert(TransactionIdIsNormal(xid)); \ - if (!TransactionIdIsValid(((PageHeader) (page))->pd_prune_xid) || \ - TransactionIdPrecedes(xid, ((PageHeader) (page))->pd_prune_xid)) \ - ((PageHeader) (page))->pd_prune_xid = (xid); \ + if (!TransactionIdIsValid(HeapPageGetSpecial(page)->pd_prune_xid) || \ + TransactionIdPrecedes(xid, HeapPageGetSpecial(page)->pd_prune_xid)) \ + HeapPageGetSpecial(page)->pd_prune_xid = (xid); \ } while (0) #define PageClearPrunable(page) \ - (((PageHeader) (page))->pd_prune_xid = InvalidTransactionId) + (HeapPageGetSpecial(page)->pd_prune_xid = InvalidTransactionId) /* ----------------------------------------------------------------