After the various recent discussions on list, I present what I believe to be a working patch implementing 16-but checksums on all buffer pages.
page_checksums = on | off (default) There are no required block changes; checksums are optional and some blocks may have a checksum, others not. This means that the patch will allow pg_upgrade. That capability also limits us to 16-bit checksums. Fletcher's 16 is used in this patch and seems rather quick, though that is easily replaceable/tuneable if desired, perhaps even as a parameter enum. This patch is a step on the way to 32-bit checksums in a future redesign of the page layout, though that is not a required future change, nor does this prevent that. Checksum is set whenever the buffer is flushed to disk, and checked when the page is read in from disk. It is not set at other times, and for much of the time may not be accurate. This follows earlier discussions from 2010-12-22, and is discussed in detail in patch comments. Note it works with buffer manager pages, which includes shared and local data buffers, but not SLRU pages (yet? an easy addition but needs other discussion around contention). Note that all this does is detect bit errors on the page, it doesn't identify where the error is, how bad and definitely not what caused it or when it happened. The main body of the patch involves changes to bufpage.c/.h so this differs completely from the VMware patch, for technical reasons. Also included are facilities to LockBufferForHints() with usage in various AMs, to avoid the case where hints are set during calculation of the checksum. In my view this is a fully working, committable patch but I'm not in a hurry to do so given the holiday season. Hopefully its a gift not a turkey, and therefore a challenge for some to prove that wrong. Enjoy either way, Merry Christmas, -- Simon Riggs http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/access/hash/hash.c --- b/src/backend/access/hash/hash.c *************** *** 282,288 **** hashgettuple(PG_FUNCTION_ARGS) --- 282,290 ---- /* * Yes, so mark it by setting the LP_DEAD state in the item flags. */ + LockBufferForHints(buf); ItemIdMarkDead(PageGetItemId(page, offnum)); + UnlockBufferForHints(buf); /* * Since this can be redone later if needed, it's treated the same *** a/src/backend/access/heap/pruneheap.c --- b/src/backend/access/heap/pruneheap.c *************** *** 259,266 **** heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, --- 259,268 ---- if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid || PageIsFull(page)) { + LockBufferForHints(buffer); ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid; PageClearFull(page); + UnlockBufferForHints(buffer); SetBufferCommitInfoNeedsSave(buffer); } } *** a/src/backend/access/nbtree/nbtinsert.c --- b/src/backend/access/nbtree/nbtinsert.c *************** *** 403,415 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, --- 403,427 ---- * everyone, so we may as well mark the index entry * killed. */ + if (nbuf != InvalidBuffer) + LockBufferForHints(nbuf); + else + LockBufferForHints(buf); + ItemIdMarkDead(curitemid); opaque->btpo_flags |= BTP_HAS_GARBAGE; + /* be sure to mark the proper buffer dirty... */ if (nbuf != InvalidBuffer) + { + UnlockBufferForHints(nbuf); SetBufferCommitInfoNeedsSave(nbuf); + } else + { + UnlockBufferForHints(buf); SetBufferCommitInfoNeedsSave(buf); + } } } } *** a/src/backend/access/nbtree/nbtree.c --- b/src/backend/access/nbtree/nbtree.c *************** *** 1040,1046 **** restart: --- 1040,1048 ---- if (vstate->cycleid != 0 && opaque->btpo_cycleid == vstate->cycleid) { + LockBufferForHints(buf); opaque->btpo_cycleid = 0; + UnlockBufferForHints(buf); SetBufferCommitInfoNeedsSave(buf); } } *** a/src/backend/commands/sequence.c --- b/src/backend/commands/sequence.c *************** *** 1092,1101 **** read_info(SeqTable elm, Relation rel, Buffer *buf) --- 1092,1103 ---- */ if (HeapTupleHeaderGetXmax(tuple.t_data) != InvalidTransactionId) { + LockBufferForHints(*buf); HeapTupleHeaderSetXmax(tuple.t_data, InvalidTransactionId); tuple.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED; tuple.t_data->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(*buf); + UnlockBufferForHints(*buf); } seq = (Form_pg_sequence) GETSTRUCT(&tuple); *** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** *** 440,446 **** ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, smgrread(smgr, forkNum, blockNum, (char *) bufBlock); /* check for garbage data */ ! if (!PageHeaderIsValid((PageHeader) bufBlock)) { if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages) { --- 440,446 ---- smgrread(smgr, forkNum, blockNum, (char *) bufBlock); /* check for garbage data */ ! if (!PageIsVerified((Page) bufBlock)) { if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages) { *************** *** 1860,1865 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) --- 1860,1866 ---- { XLogRecPtr recptr; ErrorContextCallback errcontext; + Block bufBlock; /* * Acquire the buffer's io_in_progress lock. If StartBufferIO returns *************** *** 1907,1912 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) --- 1908,1928 ---- buf->flags &= ~BM_JUST_DIRTIED; UnlockBufHdr(buf); + /* + * Set page verification info immediately before we write the buffer to disk. + * Once we have flushed the buffer is marked clean again, meaning it can + * be replaced quickly and silently with another data block, so we must + * write verification info now. For efficiency, the process of cleaning + * and page replacement is asynchronous, so we can't do this *only* when + * we are about to replace the buffer, we need to do this for every flush. + * + * Note that the buffer must not be written to while we set verification, + * otherwise it would invalidate the page on-disk. So we must ensure + * writes are prevented, see comments for LockBufferForHints(). + */ + bufBlock = buf->buf_id < 0 ? LocalBufHdrGetBlock(buf) : BufHdrGetBlock(buf); + PageSetVerificationInfo((Page) bufBlock); + smgrwrite(reln, buf->tag.forkNum, buf->tag.blockNum, *************** *** 1921,1926 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) --- 1937,1960 ---- */ TerminateBufferIO(buf, true, 0); + #ifdef USE_ASSERT_CHECKING + /* + * Confirm that all AMs have protected their hints with LockBufferForHints() + */ + if (page_checksums) + { + bool just_dirtied; + + LockBufHdr(buf); + just_dirtied = ((buf->flags & BM_JUST_DIRTIED) == BM_JUST_DIRTIED); + UnlockBufHdr(buf); + + Assert(!just_dirtied); + } + #endif + + /* XXX Assert(buf is not BM_JUST_DIRTIED) */ + TRACE_POSTGRESQL_BUFFER_FLUSH_DONE(buf->tag.forkNum, buf->tag.blockNum, reln->smgr_rnode.node.spcNode, *************** *** 2628,2633 **** WaitIO(volatile BufferDesc *buf) --- 2662,2742 ---- } /* + * Lock buffer to allow hints to be set, for use by all AMs. + * + * Many writes to PostgreSQL data pages are referred to as hints because + * those changes are not critical and we have elected to avoid WAL-logging + * the changes. As an optimisation, PostgreSQL allows hints to be set on + * pages locked in shared mode. When we are adding checksums prior to flush + * the page musn't change, so we hold the io_in_progress lock exclusively + * for the buffer prior to the smgrwrite. Thus setting hints must wait + * when we are flushing the buffer. The alternative would be to lock the + * whole buffer in exclusive mode when we wanted to set hints, which would + * cause more contention that simply holding the io_in_progress lock. + * + * It might seem possible to handle this optimistically and flush the + * buffer and then check to see if the buffer was just dirtied. However, + * that might result in the on-disk block having page checksums that don't + * match its contents and thus we would need to train ourselves to ignore + * "some" page errors. If you feel that is a path to take, then turn off + * checksums and be happy. + */ + void + LockBufferForHints(Buffer buffer) + { + volatile BufferDesc *buf; + + Assert(BufferIsValid(buffer)); + + /* + * If we aren't writing a checksum for buffers then the answer is true. + */ + if (!page_checksums) + return; + + /* + * If its a local buffer then it can't be IO in progress, since we'd be the + * one writing it and not checking to see if it was in progress. + */ + if (BufferIsLocal(buffer)) + return; + + buf = &BufferDescriptors[buffer - 1]; + + LWLockAcquire(buf->io_in_progress_lock, LW_SHARED); + } + + /* + * UnLock buffer to prevent hints being set. + * + * Don't do a CHECK_INTERRUPTS between BufferLockForHints() and BufferUnlockForHints() + */ + void + UnlockBufferForHints(Buffer buffer) + { + volatile BufferDesc *buf; + + Assert(BufferIsValid(buffer)); + + /* + * If we aren't writing a checksum for buffers, ignore. + */ + if (!page_checksums) + return; + + /* + * If its a local buffer then it can't be IO in progress, since we'd be the + * one writing it and not checking to see if it was in progress. + */ + if (BufferIsLocal(buffer)) + return; + + buf = &BufferDescriptors[buffer - 1]; + + LWLockRelease(buf->io_in_progress_lock); + } + + /* * StartBufferIO: begin I/O on this buffer * (Assumptions) * My process is executing no IO *** a/src/backend/storage/page/bufpage.c --- b/src/backend/storage/page/bufpage.c *************** *** 16,21 **** --- 16,25 ---- #include "access/htup.h" + bool page_checksums = false; + + static bool PageVerificationInfoOK(Page page); + static uint16 PageCalcChecksum16(Page page); /* ---------------------------------------------------------------- * Page support functions *************** *** 25,30 **** --- 29,38 ---- /* * PageInit * Initializes the contents of a page. + * Note that we don't automatically add a checksum, or flag that the + * page has a checksum field. We start with a normal page layout and defer + * the decision on what page verification will be written just before + * we writethe block to disk later. */ void PageInit(Page page, Size pageSize, Size specialSize) *************** *** 67,86 **** PageInit(Page page, Size pageSize, Size specialSize) * will clean up such a page and make it usable. */ bool ! PageHeaderIsValid(PageHeader page) { char *pagebytes; int i; /* Check normal case */ ! if (PageGetPageSize(page) == BLCKSZ && ! PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION && ! (page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && ! page->pd_lower >= SizeOfPageHeaderData && ! page->pd_lower <= page->pd_upper && ! page->pd_upper <= page->pd_special && ! page->pd_special <= BLCKSZ && ! page->pd_special == MAXALIGN(page->pd_special)) return true; /* Check all-zeroes case */ --- 75,94 ---- * will clean up such a page and make it usable. */ bool ! PageIsVerified(Page page) { + PageHeader p = (PageHeader) page; char *pagebytes; int i; /* Check normal case */ ! if (PageVerificationInfoOK(page) && ! (p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && ! p->pd_lower >= SizeOfPageHeaderData && ! p->pd_lower <= p->pd_upper && ! p->pd_upper <= p->pd_special && ! p->pd_special <= BLCKSZ && ! p->pd_special == MAXALIGN(p->pd_special)) return true; /* Check all-zeroes case */ *************** *** 827,829 **** PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems) --- 835,979 ---- pfree(itemidbase); } + + /* + * Test whether the page verification information is correct or not. + * + * IMPORTANT NOTE - + * Verification info is not valid at all times on a data page. We set + * verification info before we flush page/buffer, and implicitly invalidate + * verification info when we write to the page. A heavily accessed buffer + * might then spend most of its life with invalid page verification info, + * so testing verification info on random pages in the buffer pool will tell + * you nothing. The reason for this is that page verification info protects + * Postgres data from errors on the filesystems on which we rely. We do not + * protect buffers against uncorrectable memory errors, since these have a + * very low measured incidence according to research on large server farms, + * http://www.google.com/research/pubs/archive/35162.pdf, discussed 2010/12/22. + * + * To confirm your understanding that means that WAL-logged changes to a page + * do NOT update the page verification info, so full page images may not have + * correct verification information on them. But those page images have the + * WAL CRC covering them and so are verified separately from this mechanism. + * WAL replay ignores page verification info unless it writes out or reads in + * blocks from disk; restoring full page writes does not check verification + * info via this function. + * + * The best way to understand this is that WAL CRCs protect records entering + * the WAL stream, and page verification protects blocks entering and leaving + * the buffer pool. They are similar in purpose, yet completely separate. + * Together they ensure we are able to detect errors in data leaving and + * re-entering PostgreSQL controlled memory. + * + * Note also that the verification mechanism can vary from page to page. + * All we do here is look at what the page itself says is the verification + * mechanism and then apply that test. This allows us to run without the CPU + * cost of verification if we choose, as well as to provide an upgrade path + * for anyone doing direct upgrades using pg_upgrade. + * + * There is some concern that trusting page data to say how to check page + * data is dangerously self-referential. To ensure no mistakes we set two + * non-adjacent bits to signify that the page has a checksum and + * should be verified when that block is read back into a buffer. + * We use two bits in case a multiple bit error removes one of the checksum + * flags *and* destroys data, which would lead to skipping the checksum check + * and silently accepting bad data. + * + * Note also that this returns a boolean, not a full damage assessment. + */ + static bool + PageVerificationInfoOK(Page page) + { + PageHeader p = (PageHeader) page; + + /* + * We set two non-adjacent bits to signify that the page has a checksum and + * should be verified against that block is read back into a buffer. + * We use two bits in case a multiple bit error removes one of the checksum + * flags and destroys data, which would lead to skipping the checksum check + * and silently accepting bad data. + */ + if (PageHasChecksumFlag1(p) && PageHasChecksumFlag2(p)) + { + if (PageCalcChecksum16(page) == p->pd_verify.pd_checksum16) + return true; + } + else if (!PageHasChecksumFlag1(p) && !PageHasChecksumFlag2(p)) + { + if (PageGetPageLayoutVersion(p) == PG_PAGE_LAYOUT_VERSION && + PageGetPageSize(p) == BLCKSZ) + return true; + } + + return false; + } + + /* + * Set verification info for page. + * + * Either we set a new checksum, or we set the standard watermark. We must + * not leave an old checksum in place. Note that the verification info is + * not WAL logged, whereas the data changes to pages are, so data is safe + * whether or not we have page_checksums enabled. The purpose of checksums + * is to detect page corruption to allow replacement from backup. + */ + void + PageSetVerificationInfo(Page page) + { + PageHeader p = (PageHeader) page; + + if (page_checksums) + { + p->pd_flags |= PD_CHECKSUM; + p->pd_verify.pd_checksum16 = PageCalcChecksum16(page); + } + else if (PageHasChecksumFlag1(p) || PageHasChecksumFlag2(p)) + { + /* ensure any older checksum info is overwritten with watermark */ + p->pd_flags &= ~PD_CHECKSUM; + PageSetPageSizeAndVersion(p, BLCKSZ, PG_PAGE_LAYOUT_VERSION); + } + } + + /* + * Calculate checksum for a PostgreSQL Page. We do this in 3 steps, first + * we calculate the checksum for the header, avoiding the verification + * info, which will be added afterwards. Next, we add the line pointers up to + * the hole in the middle of the block at pd_lower. Last, we add the tail + * of the page from pd_upper to the end of page. + */ + static uint16 + PageCalcChecksum16(Page page) + { + #define PAGE_VERIFICATION_USES_FLETCHER16 (true) + #ifdef PAGE_VERIFICATION_USES_FLETCHER16 + /* + * Following calculation is a Flecther's 16 checksum. The calc is isolated + * here and tuning and/or replacement algorithms are possible. + * + * XXX present implementation is raw, untuned calculation, please tweak + */ + PageHeader p = (PageHeader) page; + uint16 sum1 = 0; + uint16 sum2 = 0; + int i; + + #define COMP_F16(from, to) \ + for (i = from; i < to; i++) \ + { \ + sum1 = (sum1 + page[i]) % 255; \ + sum2 = (sum1 + sum2) % 255; \ + } while (0) + + COMP_F16(0, + offsetof(PageHeaderData, pd_special) + sizeof(LocationIndex)); + + COMP_F16(offsetof(PageHeaderData, pd_prune_xid), + p->pd_upper); + + COMP_F16(p->pd_upper, + (int) BLCKSZ); + + return ((sum2 << 8) | sum1); + #endif + } *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 830,835 **** static struct config_bool ConfigureNamesBool[] = --- 830,849 ---- NULL, NULL, NULL }, { + {"page_checksums", PGC_SIGHUP, WAL_SETTINGS, + gettext_noop("Marks database blocks with a checksum before writing them to disk. "), + gettext_noop("When enabled all database blocks will be marked with a checksums before writing to disk. " + "When we read a database block from disk the checksum is checked, if it exists. " + "If there is no checksum marked yet then no check is performed, though a " + "checksum will be added later when we re-write the database block. " + "When disabled checksums will be ignored, even if the block was marked " + "with checksum. When disabled checksums will not be added to database blocks.") + }, + &page_checksums, + true, + NULL, NULL, NULL + }, + { {"full_page_writes", PGC_SIGHUP, WAL_SETTINGS, gettext_noop("Writes full pages to WAL when first modified after a checkpoint."), gettext_noop("A page write in process during an operating system crash might be " *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 150,164 **** #------------------------------------------------------------------------------ ! # WRITE AHEAD LOG #------------------------------------------------------------------------------ ! # - Settings - ! #wal_level = minimal # minimal, archive, or hot_standby ! # (change requires restart) #fsync = on # turns forced synchronization on or off #synchronous_commit = on # synchronization level; on, off, or local #wal_sync_method = fsync # the default is the first option # supported by the operating system: # open_datasync --- 150,170 ---- #------------------------------------------------------------------------------ ! # WRITE AHEAD LOG & RELIABILITY #------------------------------------------------------------------------------ ! # - Reliability - ! #page_checksums = off # calculate checksum before database I/O ! #full_page_writes = on # recover from partial page writes #fsync = on # turns forced synchronization on or off + #synchronous_commit = on # synchronization level; on, off, or local + + # - Write Ahead Log - + + #wal_level = minimal # minimal, archive, or hot_standby + # (change requires restart) #wal_sync_method = fsync # the default is the first option # supported by the operating system: # open_datasync *************** *** 166,172 **** # fsync # fsync_writethrough # open_sync - #full_page_writes = on # recover from partial page writes #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # (change requires restart) #wal_writer_delay = 200ms # 1-10000 milliseconds --- 172,177 ---- *** a/src/backend/utils/time/tqual.c --- b/src/backend/utils/time/tqual.c *************** *** 119,125 **** SetHintBits(HeapTupleHeader tuple, Buffer buffer, --- 119,127 ---- return; /* not flushed yet, so don't set hint */ } + LockBufferForHints(buffer); tuple->t_infomask |= infomask; + UnlockBufferForHints(buffer); SetBufferCommitInfoNeedsSave(buffer); } *** a/src/include/storage/bufmgr.h --- b/src/include/storage/bufmgr.h *************** *** 209,214 **** extern bool ConditionalLockBuffer(Buffer buffer); --- 209,216 ---- extern void LockBufferForCleanup(Buffer buffer); extern bool ConditionalLockBufferForCleanup(Buffer buffer); extern bool HoldingBufferPinThatDelaysRecovery(void); + extern void LockBufferForHints(Buffer buffer); + extern void UnlockBufferForHints(Buffer buffer); extern void AbortBufferIO(void); *** a/src/include/storage/bufpage.h --- b/src/include/storage/bufpage.h *************** *** 18,23 **** --- 18,25 ---- #include "storage/item.h" #include "storage/off.h" + extern bool page_checksums; + /* * A postgres disk page is an abstraction layered on top of a postgres * disk block (which is simply a unit of i/o, see block.h). *************** *** 130,136 **** typedef struct PageHeaderData LocationIndex pd_lower; /* offset to start of free space */ 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[1]; /* beginning of line pointer array */ } PageHeaderData; --- 132,144 ---- LocationIndex pd_lower; /* offset to start of free space */ LocationIndex pd_upper; /* offset to end of free space */ LocationIndex pd_special; /* offset to start of special space */ ! ! union ! { ! uint16 pd_pagesize_version; ! uint16 pd_checksum16; ! } pd_verify; /* page verification data */ ! TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ ItemIdData pd_linp[1]; /* beginning of line pointer array */ } PageHeaderData; *************** *** 155,161 **** typedef PageHeaderData *PageHeader; #define PD_ALL_VISIBLE 0x0004 /* all tuples on page are visible to * everyone */ ! #define PD_VALID_FLAG_BITS 0x0007 /* OR of all valid pd_flags bits */ /* * Page layout version number 0 is for pre-7.3 Postgres releases. --- 163,178 ---- #define PD_ALL_VISIBLE 0x0004 /* all tuples on page are visible to * everyone */ ! #define PD_VALID_FLAG_BITS 0x800F /* OR of all non-checksum pd_flags bits */ ! ! #define PD_CHECKSUM1 0x0008 /* First checksum bit */ ! #define PD_CHECKSUM2 0x8000 /* Second checksum bit */ ! #define PD_CHECKSUM 0x8008 /* OR of both checksum flags */ ! ! #define PageHasChecksumFlag1(page) \ ! ((((PageHeader) (page))->pd_flags & PD_CHECKSUM1) == PD_CHECKSUM1) ! #define PageHasChecksumFlag2(page) \ ! ((((PageHeader) (page))->pd_flags & PD_CHECKSUM2) == PD_CHECKSUM2) /* * Page layout version number 0 is for pre-7.3 Postgres releases. *************** *** 165,170 **** typedef PageHeaderData *PageHeader; --- 182,189 ---- * Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and * added the pd_flags field (by stealing some bits from pd_tli), * as well as adding the pd_prune_xid field (which enlarges the header). + * Release 9.2 uses 4 as well, though with changed meaning of verification bits. + * We deliberately don't bump the page version for that, to allow upgrades. */ #define PG_PAGE_LAYOUT_VERSION 4 *************** *** 231,249 **** typedef PageHeaderData *PageHeader; * PageGetPageSize * Returns the page size of a page. * ! * this can only be called on a formatted page (unlike ! * BufferGetPageSize, which can be called on an unformatted page). ! * however, it can be called on a page that is not stored in a buffer. */ ! #define PageGetPageSize(page) \ ! ((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00)) /* * PageGetPageLayoutVersion * Returns the page layout version of a page. */ #define PageGetPageLayoutVersion(page) \ ! (((PageHeader) (page))->pd_pagesize_version & 0x00FF) /* * PageSetPageSizeAndVersion --- 250,271 ---- * PageGetPageSize * Returns the page size of a page. * ! * Since PageSizeIsValid() when pagesize == BLCKSZ, just written BLCKSZ. ! * This can be called on any page, initialised or not, in or out of buffers. ! * You might think this can vary at runtime but you'd be wrong, since pages ! * frequently need to occupy buffers and pages are copied from one to another ! * so there are many hidden assumptions that this simple definition is true. */ ! #define PageGetPageSize(page) (BLCKSZ) /* * PageGetPageLayoutVersion * Returns the page layout version of a page. + * + * Must not be used on a page that is flagged for checksums. */ #define PageGetPageLayoutVersion(page) \ ! (((PageHeader) (page))->pd_verify.pd_pagesize_version & 0x00FF) /* * PageSetPageSizeAndVersion *************** *** 251,262 **** typedef PageHeaderData *PageHeader; * * We could support setting these two values separately, but there's * no real need for it at the moment. */ #define PageSetPageSizeAndVersion(page, size, version) \ ( \ AssertMacro(((size) & 0xFF00) == (size)), \ AssertMacro(((version) & 0x00FF) == (version)), \ ! ((PageHeader) (page))->pd_pagesize_version = (size) | (version) \ ) /* ---------------- --- 273,286 ---- * * We could support setting these two values separately, but there's * no real need for it at the moment. + * + * Must not be used on a page that is flagged for checksums. */ #define PageSetPageSizeAndVersion(page, size, version) \ ( \ AssertMacro(((size) & 0xFF00) == (size)), \ AssertMacro(((version) & 0x00FF) == (version)), \ ! ((PageHeader) (page))->pd_verify.pd_pagesize_version = (size) | (version) \ ) /* ---------------- *************** *** 368,374 **** do { \ */ extern void PageInit(Page page, Size pageSize, Size specialSize); ! extern bool PageHeaderIsValid(PageHeader page); extern OffsetNumber PageAddItem(Page page, Item item, Size size, OffsetNumber offsetNumber, bool overwrite, bool is_heap); extern Page PageGetTempPage(Page page); --- 392,398 ---- */ extern void PageInit(Page page, Size pageSize, Size specialSize); ! extern bool PageIsVerified(Page page); extern OffsetNumber PageAddItem(Page page, Item item, Size size, OffsetNumber offsetNumber, bool overwrite, bool is_heap); extern Page PageGetTempPage(Page page); *************** *** 381,385 **** extern Size PageGetExactFreeSpace(Page page); --- 405,410 ---- extern Size PageGetHeapFreeSpace(Page page); extern void PageIndexTupleDelete(Page page, OffsetNumber offset); extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems); + extern void PageSetVerificationInfo(Page page); #endif /* BUFPAGE_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers