Created
December 12, 2017 12:29
-
-
Save lubennikovaav/6e99048ee20b7d697c41fb2cac648d30 to your computer and use it in GitHub Desktop.
ptrack_9.6.6_v1.4.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit 2dc545816d027f17a717b380b005bf84fed989dd | |
Author: Anastasia <a.lubennikova@postgrespro.ru> | |
Date: Fri Nov 24 14:35:34 2017 +0300 | |
ptrack_9.6.6_v1.4.patch | |
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c | |
index 706b3ee..d51835b 100644 | |
--- a/src/backend/access/brin/brin.c | |
+++ b/src/backend/access/brin/brin.c | |
@@ -22,6 +22,7 @@ | |
#include "access/reloptions.h" | |
#include "access/relscan.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/index.h" | |
#include "catalog/pg_am.h" | |
#include "miscadmin.h" | |
@@ -684,6 +685,7 @@ brinbuildempty(Relation index) | |
LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE); | |
/* Initialize and xlog metabuffer. */ | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuf)); | |
START_CRIT_SECTION(); | |
brin_metapage_init(BufferGetPage(metabuf), BrinGetPagesPerRange(index), | |
BRIN_CURRENT_VERSION); | |
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c | |
index bb30e41..6ca83f5 100644 | |
--- a/src/backend/access/brin/brin_pageops.c | |
+++ b/src/backend/access/brin/brin_pageops.c | |
@@ -15,6 +15,7 @@ | |
#include "access/brin_revmap.h" | |
#include "access/brin_xlog.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/bufmgr.h" | |
#include "storage/freespace.h" | |
@@ -181,6 +182,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, | |
UnlockReleaseBuffer(newbuf); | |
} | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(oldbuf)); | |
START_CRIT_SECTION(); | |
PageIndexDeleteNoCompact(oldpage, &oldoff, 1); | |
if (PageAddItemExtended(oldpage, (Item) newtup, newsz, oldoff, | |
@@ -241,6 +243,9 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, | |
revmapbuf = brinLockRevmapPageForUpdate(revmap, heapBlk); | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(newbuf)); | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(oldbuf)); | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(revmapbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -410,6 +415,8 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange, | |
blk = BufferGetBlockNumber(*buffer); | |
/* Execute the actual insertion */ | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(*buffer)); | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(revmapbuf)); | |
START_CRIT_SECTION(); | |
if (extended) | |
brin_page_init(BufferGetPage(*buffer), BRIN_PAGETYPE_REGULAR); | |
@@ -861,6 +868,7 @@ brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer) | |
"brin_initialize_empty_new_buffer: initializing blank page %u", | |
BufferGetBlockNumber(buffer))); | |
+ ptrack_add_block(idxrel, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
page = BufferGetPage(buffer); | |
brin_page_init(page, BRIN_PAGETYPE_REGULAR); | |
diff --git a/src/backend/access/brin/brin_revmap.c b/src/backend/access/brin/brin_revmap.c | |
index 853181b..465b752 100644 | |
--- a/src/backend/access/brin/brin_revmap.c | |
+++ b/src/backend/access/brin/brin_revmap.c | |
@@ -27,6 +27,7 @@ | |
#include "access/brin_xlog.h" | |
#include "access/rmgr.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/bufmgr.h" | |
#include "storage/lmgr.h" | |
@@ -475,6 +476,8 @@ revmap_physical_extend(BrinRevmap *revmap) | |
* Ok, we have now locked the metapage and the target block. Re-initialize | |
* it as a revmap page. | |
*/ | |
+ ptrack_add_block(irel, BufferGetBlockNumber(buf)); | |
+ ptrack_add_block(irel, BufferGetBlockNumber(revmap->rm_metaBuf)); | |
START_CRIT_SECTION(); | |
/* the rm_tids array is initialized to all invalid by PageInit */ | |
diff --git a/src/backend/access/brin/brin_xlog.c b/src/backend/access/brin/brin_xlog.c | |
index 27ba0a9..1a61a0b 100644 | |
--- a/src/backend/access/brin/brin_xlog.c | |
+++ b/src/backend/access/brin/brin_xlog.c | |
@@ -14,6 +14,7 @@ | |
#include "access/brin_pageops.h" | |
#include "access/brin_xlog.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
/* | |
@@ -26,6 +27,11 @@ brin_xlog_createidx(XLogReaderState *record) | |
xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record); | |
Buffer buf; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* create the index' metapage */ | |
buf = XLogInitBufferForRedo(record, 0); | |
@@ -50,6 +56,13 @@ brin_xlog_insert_update(XLogReaderState *record, | |
BlockNumber regpgno; | |
Page page; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* If we inserted the first and only tuple on the page, re-initialize the | |
@@ -137,9 +150,15 @@ brin_xlog_update(XLogReaderState *record) | |
xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record); | |
Buffer buffer; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* First remove the old tuple */ | |
action = XLogReadBufferForRedo(record, 2, &buffer); | |
+ | |
if (action == BLK_NEEDS_REDO) | |
{ | |
Page page; | |
@@ -174,9 +193,15 @@ brin_xlog_samepage_update(XLogReaderState *record) | |
xl_brin_samepage_update *xlrec; | |
Buffer buffer; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
xlrec = (xl_brin_samepage_update *) XLogRecGetData(record); | |
action = XLogReadBufferForRedo(record, 0, &buffer); | |
+ | |
if (action == BLK_NEEDS_REDO) | |
{ | |
Size tuplen; | |
@@ -220,14 +245,21 @@ brin_xlog_revmap_extend(XLogReaderState *record) | |
Page page; | |
BlockNumber targetBlk; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record); | |
- XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk); | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &targetBlk); | |
+ ptrack_add_block_redo(rnode, targetBlk); | |
+ | |
Assert(xlrec->targetBlk == targetBlk); | |
/* Update the metapage */ | |
action = XLogReadBufferForRedo(record, 0, &metabuf); | |
+ | |
if (action == BLK_NEEDS_REDO) | |
{ | |
Page metapg; | |
diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c | |
index a0afec4..9513422 100644 | |
--- a/src/backend/access/gin/ginbtree.c | |
+++ b/src/backend/access/gin/ginbtree.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "utils/memutils.h" | |
#include "utils/rel.h" | |
@@ -385,6 +386,9 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, | |
else if (rc == GPTP_INSERT) | |
{ | |
/* It will fit, perform the insertion */ | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(stack->buffer)); | |
+ if (BufferIsValid(childbuf)) | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(childbuf)); | |
START_CRIT_SECTION(); | |
if (RelationNeedsWAL(btree->index)) | |
@@ -534,6 +538,12 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, | |
* the new contents of the root. | |
*/ | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(rbuffer)); | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(stack->buffer)); | |
+ if (stack->parent == NULL) | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(lbuffer)); | |
+ if (BufferIsValid(childbuf)) | |
+ ptrack_add_block(btree->index, BufferGetBlockNumber(childbuf)); | |
START_CRIT_SECTION(); | |
MarkBufferDirty(rbuffer); | |
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c | |
index 276376a..a6cf119 100644 | |
--- a/src/backend/access/gin/gindatapage.c | |
+++ b/src/backend/access/gin/gindatapage.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "lib/ilist.h" | |
#include "miscadmin.h" | |
#include "utils/rel.h" | |
@@ -834,6 +835,7 @@ ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs) | |
computeLeafRecompressWALData(leaf); | |
/* Apply changes to page */ | |
+ ptrack_add_block(indexrel, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
dataPlaceToPageLeafRecompress(buffer, leaf); | |
@@ -1809,6 +1811,7 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems, | |
page = BufferGetPage(buffer); | |
blkno = BufferGetBlockNumber(buffer); | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
PageRestoreTempPage(tmppage, page); | |
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c | |
index 6b709db..718b7c6 100644 | |
--- a/src/backend/access/gin/ginfast.c | |
+++ b/src/backend/access/gin/ginfast.c | |
@@ -20,6 +20,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "access/xlog.h" | |
#include "commands/vacuum.h" | |
#include "catalog/pg_am.h" | |
@@ -67,6 +68,7 @@ writeListPage(Relation index, Buffer buffer, | |
/* workspace could be a local array; we use palloc for alignment */ | |
workspace = palloc(BLCKSZ); | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
GinInitBuffer(buffer, GIN_LIST); | |
@@ -293,6 +295,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) | |
/* | |
* Main list is empty, so just insert sublist as main list | |
*/ | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
START_CRIT_SECTION(); | |
metadata->head = sublist.head; | |
@@ -316,6 +319,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) | |
Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber); | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
GinPageGetOpaque(page)->rightlink = sublist.head; | |
@@ -358,6 +363,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) | |
if (needWal) | |
XLogBeginInsert(); | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -553,6 +560,10 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, | |
if (RelationNeedsWAL(index)) | |
XLogEnsureRecordSpace(data.ndeleted, 0); | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
+ for (i = 0; i < data.ndeleted; i++) | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffers[i])); | |
+ | |
START_CRIT_SECTION(); | |
metadata->head = blknoToDelete; | |
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c | |
index 4e09f76..49171d4 100644 | |
--- a/src/backend/access/gin/gininsert.c | |
+++ b/src/backend/access/gin/gininsert.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/index.h" | |
#include "miscadmin.h" | |
#include "storage/bufmgr.h" | |
@@ -335,6 +336,8 @@ ginbuild(Relation heap, Relation index, IndexInfo *indexInfo) | |
/* initialize the root page */ | |
RootBuffer = GinNewBuffer(index); | |
+ ptrack_add_block(index, BufferGetBlockNumber(MetaBuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(RootBuffer)); | |
START_CRIT_SECTION(); | |
GinInitMetabuffer(MetaBuffer); | |
MarkBufferDirty(MetaBuffer); | |
@@ -443,6 +446,8 @@ ginbuildempty(Relation index) | |
LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE); | |
/* Initialize and xlog metabuffer and root buffer. */ | |
+ ptrack_add_block(index, BufferGetBlockNumber(MetaBuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(RootBuffer)); | |
START_CRIT_SECTION(); | |
GinInitMetabuffer(MetaBuffer); | |
MarkBufferDirty(MetaBuffer); | |
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c | |
index d914648..f2f4fee 100644 | |
--- a/src/backend/access/gin/ginutil.c | |
+++ b/src/backend/access/gin/ginutil.c | |
@@ -17,6 +17,7 @@ | |
#include "access/gin_private.h" | |
#include "access/reloptions.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/pg_collation.h" | |
#include "catalog/pg_type.h" | |
#include "miscadmin.h" | |
@@ -636,6 +637,7 @@ ginUpdateStats(Relation index, const GinStatsData *stats) | |
metapage = BufferGetPage(metabuffer); | |
metadata = GinPageGetMeta(metapage); | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
START_CRIT_SECTION(); | |
metadata->nTotalPages = stats->nTotalPages; | |
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c | |
index 2685a1c..6dad7b1 100644 | |
--- a/src/backend/access/gin/ginvacuum.c | |
+++ b/src/backend/access/gin/ginvacuum.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "commands/vacuum.h" | |
#include "miscadmin.h" | |
#include "postmaster/autovacuum.h" | |
@@ -209,6 +210,9 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn | |
* LockBufferForCleanup() */ | |
LockBuffer(pBuffer, GIN_EXCLUSIVE); | |
+ ptrack_add_block(gvs->index, BufferGetBlockNumber(pBuffer)); | |
+ ptrack_add_block(gvs->index, BufferGetBlockNumber(lBuffer)); | |
+ ptrack_add_block(gvs->index, BufferGetBlockNumber(dBuffer)); | |
START_CRIT_SECTION(); | |
/* Unlink the page by changing left sibling's rightlink */ | |
@@ -603,6 +607,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, | |
if (resPage) | |
{ | |
+ ptrack_add_block(gvs.index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
PageRestoreTempPage(resPage, page); | |
MarkBufferDirty(buffer); | |
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c | |
index a40f168..11d63cd 100644 | |
--- a/src/backend/access/gin/ginxlog.c | |
+++ b/src/backend/access/gin/ginxlog.c | |
@@ -15,6 +15,7 @@ | |
#include "access/gin_private.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "utils/memutils.h" | |
static MemoryContext opCtx; /* working memory for operations */ | |
@@ -25,6 +26,11 @@ ginRedoClearIncompleteSplit(XLogReaderState *record, uint8 block_id) | |
XLogRecPtr lsn = record->EndRecPtr; | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, block_id, &buffer) == BLK_NEEDS_REDO) | |
{ | |
@@ -45,8 +51,12 @@ ginRedoCreateIndex(XLogReaderState *record) | |
Buffer RootBuffer, | |
MetaBuffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL); | |
MetaBuffer = XLogInitBufferForRedo(record, 0); | |
+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(MetaBuffer)); | |
Assert(BufferGetBlockNumber(MetaBuffer) == GIN_METAPAGE_BLKNO); | |
page = (Page) BufferGetPage(MetaBuffer); | |
@@ -56,6 +66,7 @@ ginRedoCreateIndex(XLogReaderState *record) | |
MarkBufferDirty(MetaBuffer); | |
RootBuffer = XLogInitBufferForRedo(record, 1); | |
+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(RootBuffer)); | |
Assert(BufferGetBlockNumber(RootBuffer) == GIN_ROOT_BLKNO); | |
page = (Page) BufferGetPage(RootBuffer); | |
@@ -76,8 +87,13 @@ ginRedoCreatePTree(XLogReaderState *record) | |
char *ptr; | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL); | |
buffer = XLogInitBufferForRedo(record, 0); | |
+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(buffer)); | |
+ | |
page = (Page) BufferGetPage(buffer); | |
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF | GIN_COMPRESSED); | |
@@ -329,6 +345,11 @@ ginRedoInsert(XLogReaderState *record) | |
#endif | |
BlockNumber rightChildBlkno = InvalidBlockNumber; | |
bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* First clear incomplete-split flag on child page if this finishes a | |
@@ -382,6 +403,18 @@ ginRedoSplit(XLogReaderState *record) | |
rootbuf; | |
bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0; | |
bool isRoot = (data->flags & GIN_SPLIT_ROOT) != 0; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if (isRoot) | |
+ { | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
/* | |
* First clear incomplete-split flag on child page if this finishes a | |
@@ -415,6 +448,11 @@ static void | |
ginRedoVacuumPage(XLogReaderState *record) | |
{ | |
Buffer buffer; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &buffer) != BLK_RESTORED) | |
{ | |
@@ -428,6 +466,11 @@ ginRedoVacuumDataLeafPage(XLogReaderState *record) | |
{ | |
XLogRecPtr lsn = record->EndRecPtr; | |
Buffer buffer; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) | |
{ | |
@@ -457,6 +500,15 @@ ginRedoDeletePage(XLogReaderState *record) | |
Buffer pbuffer; | |
Buffer lbuffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &dbuffer) == BLK_NEEDS_REDO) | |
{ | |
@@ -502,6 +554,13 @@ ginRedoUpdateMetapage(XLogReaderState *record) | |
Buffer metabuffer; | |
Page metapage; | |
Buffer buffer; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* Restore the metapage. This is essentially the same as a full-page | |
@@ -600,6 +659,11 @@ ginRedoInsertListPage(XLogReaderState *record) | |
char *payload; | |
IndexTuple tuples; | |
Size totaltupsize; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* We always re-initialize the page. */ | |
buffer = XLogInitBufferForRedo(record, 0); | |
@@ -649,6 +713,11 @@ ginRedoDeleteListPages(XLogReaderState *record) | |
Buffer metabuffer; | |
Page metapage; | |
int i; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
metabuffer = XLogInitBufferForRedo(record, 0); | |
Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO); | |
@@ -680,6 +749,9 @@ ginRedoDeleteListPages(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
+ XLogRecGetBlockTag(record, i+1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ | |
buffer = XLogInitBufferForRedo(record, i + 1); | |
page = BufferGetPage(buffer); | |
GinInitBuffer(buffer, GIN_DELETED); | |
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c | |
index f7f44b4..45dc9a0 100644 | |
--- a/src/backend/access/gist/gist.c | |
+++ b/src/backend/access/gist/gist.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gist_private.h" | |
#include "access/gistscan.h" | |
+#include "access/ptrack.h" | |
#include "catalog/pg_collation.h" | |
#include "miscadmin.h" | |
#include "utils/index_selfuncs.h" | |
@@ -121,6 +122,7 @@ gistbuildempty(Relation index) | |
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); | |
/* Initialize and xlog buffer */ | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
GISTInitBuffer(buffer, F_LEAF); | |
MarkBufferDirty(buffer); | |
@@ -446,6 +448,10 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, | |
if (RelationNeedsWAL(rel)) | |
XLogEnsureRecordSpace(npage, 1 + npage * 2); | |
+ for (ptr = dist; ptr; ptr = ptr->next) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(ptr->buffer)); | |
+ if (BufferIsValid(leftchildbuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(leftchildbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -495,6 +501,9 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, | |
/* | |
* Enough space. We also get here if ntuples==0. | |
*/ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buffer)); | |
+ if (BufferIsValid(leftchildbuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(leftchildbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -1518,6 +1527,7 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer) | |
if (ndeletable > 0) | |
{ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
PageIndexMultiDelete(page, deletable, ndeletable); | |
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c | |
index 4e43a69..66de58b 100644 | |
--- a/src/backend/access/gist/gistbuild.c | |
+++ b/src/backend/access/gist/gistbuild.c | |
@@ -19,6 +19,7 @@ | |
#include "access/genam.h" | |
#include "access/gist_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/index.h" | |
#include "miscadmin.h" | |
#include "optimizer/cost.h" | |
@@ -171,6 +172,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) | |
Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); | |
page = BufferGetPage(buffer); | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
GISTInitBuffer(buffer, F_LEAF); | |
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c | |
index 53e5cea..ea76beb 100644 | |
--- a/src/backend/access/gist/gistvacuum.c | |
+++ b/src/backend/access/gist/gistvacuum.c | |
@@ -16,6 +16,7 @@ | |
#include "access/genam.h" | |
#include "access/gist_private.h" | |
+#include "access/ptrack.h" | |
#include "commands/vacuum.h" | |
#include "miscadmin.h" | |
#include "storage/indexfsm.h" | |
@@ -212,6 +213,7 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, | |
if (ntodelete) | |
{ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
MarkBufferDirty(buffer); | |
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c | |
index 01c7ef7..1dd2ed7 100644 | |
--- a/src/backend/access/gist/gistxlog.c | |
+++ b/src/backend/access/gist/gistxlog.c | |
@@ -16,6 +16,7 @@ | |
#include "access/gist_private.h" | |
#include "access/xloginsert.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "utils/memutils.h" | |
static MemoryContext opCtx; /* working memory for operations */ | |
@@ -38,6 +39,11 @@ gistRedoClearFollowRight(XLogReaderState *record, uint8 block_id) | |
Buffer buffer; | |
Page page; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* Note that we still update the page even if it was restored from a full | |
@@ -68,6 +74,11 @@ gistRedoPageUpdateRecord(XLogReaderState *record) | |
gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record); | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) | |
{ | |
@@ -173,6 +184,7 @@ gistRedoPageSplitRecord(XLogReaderState *record) | |
int i; | |
bool isrootsplit = false; | |
+ | |
/* | |
* We must hold lock on the first-listed page throughout the action, | |
* including while updating the left child page (if any). We can unlock | |
@@ -190,8 +202,11 @@ gistRedoPageSplitRecord(XLogReaderState *record) | |
int num; | |
BlockNumber blkno; | |
IndexTuple *tuples; | |
+ RelFileNode rnode; | |
+ | |
+ XLogRecGetBlockTag(record, i + 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
- XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno); | |
if (blkno == GIST_ROOT_BLKNO) | |
{ | |
Assert(i == 0); | |
@@ -262,6 +277,11 @@ gistRedoCreateIndex(XLogReaderState *record) | |
XLogRecPtr lsn = record->EndRecPtr; | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
buffer = XLogInitBufferForRedo(record, 0); | |
Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); | |
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile | |
index b83d496..788c55c 100644 | |
--- a/src/backend/access/heap/Makefile | |
+++ b/src/backend/access/heap/Makefile | |
@@ -12,6 +12,6 @@ subdir = src/backend/access/heap | |
top_builddir = ../../../.. | |
include $(top_builddir)/src/Makefile.global | |
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o | |
+OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o ptrack.o | |
include $(top_srcdir)/src/backend/common.mk | |
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c | |
index 4327be8..e24b17e 100644 | |
--- a/src/backend/access/heap/heapam.c | |
+++ b/src/backend/access/heap/heapam.c | |
@@ -53,6 +53,7 @@ | |
#include "access/xlog.h" | |
#include "access/xloginsert.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/namespace.h" | |
#include "miscadmin.h" | |
@@ -2412,6 +2413,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, | |
CheckForSerializableConflictIn(relation, NULL, InvalidBuffer); | |
/* NO EREPORT(ERROR) from here till changes are logged */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
RelationPutHeapTuple(relation, buffer, heaptup, | |
@@ -2707,6 +2709,7 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples, | |
page = BufferGetPage(buffer); | |
/* NO EREPORT(ERROR) from here till changes are logged */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -3223,6 +3226,7 @@ l1: | |
xid, LockTupleExclusive, true, | |
&new_xmax, &new_infomask, &new_infomask2); | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -3955,6 +3959,7 @@ l2: | |
Assert(HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple)); | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* Clear obsolete visibility flags ... */ | |
@@ -4126,6 +4131,9 @@ l2: | |
old_key_tuple = ExtractReplicaIdentity(relation, &oldtup, !satisfies_id, &old_key_copied); | |
/* NO EREPORT(ERROR) from here till changes are logged */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
+ if (newbuf != buffer) | |
+ ptrack_add_block(relation, BufferGetBlockNumber(newbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -5118,6 +5126,7 @@ failed: | |
GetCurrentTransactionId(), mode, false, | |
&xid, &new_infomask, &new_infomask2); | |
+ ptrack_add_block(relation, BufferGetBlockNumber(*buffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -5891,6 +5900,7 @@ l4: | |
VISIBILITYMAP_ALL_FROZEN)) | |
cleared_all_frozen = true; | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
/* ... and set them */ | |
@@ -6046,6 +6056,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) | |
"invalid speculative token constant"); | |
/* NO EREPORT(ERROR) from here till changes are logged */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
Assert(HeapTupleHeaderIsSpeculative(tuple->t_data)); | |
@@ -6159,6 +6170,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple) | |
* do anything special with infomask bits. | |
*/ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -6292,6 +6304,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple) | |
elog(ERROR, "wrong tuple length"); | |
/* NO EREPORT(ERROR) from here till changes are logged */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
memcpy((char *) htup + htup->t_hoff, | |
@@ -7971,6 +7984,7 @@ heap_xlog_clean(XLogReaderState *record) | |
XLogRedoAction action; | |
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* We're about to remove tuples. In Hot Standby mode, ensure that there's | |
@@ -8062,6 +8076,7 @@ heap_xlog_visible(XLogReaderState *record) | |
XLogRedoAction action; | |
XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* If there are any Hot Standby transactions running that have an xmin | |
@@ -8172,6 +8187,11 @@ heap_xlog_freeze_page(XLogReaderState *record) | |
TransactionId cutoff_xid = xlrec->cutoff_xid; | |
Buffer buffer; | |
int ntup; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* In Hot Standby mode, ensure that there's no queries running which still | |
@@ -8257,6 +8277,7 @@ heap_xlog_delete(XLogReaderState *record) | |
ItemPointerData target_tid; | |
XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno); | |
+ ptrack_add_block_redo(target_node, blkno); | |
ItemPointerSetBlockNumber(&target_tid, blkno); | |
ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum); | |
@@ -8335,6 +8356,7 @@ heap_xlog_insert(XLogReaderState *record) | |
XLogRedoAction action; | |
XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno); | |
+ ptrack_add_block_redo(target_node, blkno); | |
ItemPointerSetBlockNumber(&target_tid, blkno); | |
ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum); | |
@@ -8457,6 +8479,7 @@ heap_xlog_multi_insert(XLogReaderState *record) | |
xlrec = (xl_heap_multi_insert *) XLogRecGetData(record); | |
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* The visibility map may need to be fixed even if the heap page is | |
@@ -8603,8 +8626,10 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) | |
oldtup.t_len = 0; | |
XLogRecGetBlockTag(record, 0, &rnode, NULL, &newblk); | |
+ ptrack_add_block_redo(rnode, newblk); | |
if (XLogRecGetBlockTag(record, 1, NULL, NULL, &oldblk)) | |
{ | |
+ ptrack_add_block_redo(rnode, oldblk); | |
/* HOT updates are never done across pages */ | |
Assert(!hot_update); | |
} | |
@@ -8850,6 +8875,11 @@ heap_xlog_confirm(XLogReaderState *record) | |
OffsetNumber offnum; | |
ItemId lp = NULL; | |
HeapTupleHeader htup; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) | |
{ | |
@@ -8886,6 +8916,11 @@ heap_xlog_lock(XLogReaderState *record) | |
OffsetNumber offnum; | |
ItemId lp = NULL; | |
HeapTupleHeader htup; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* The visibility map may need to be fixed even if the heap page is | |
@@ -8957,6 +8992,11 @@ heap_xlog_lock_updated(XLogReaderState *record) | |
OffsetNumber offnum; | |
ItemId lp = NULL; | |
HeapTupleHeader htup; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
xlrec = (xl_heap_lock_updated *) XLogRecGetData(record); | |
@@ -9019,6 +9059,11 @@ heap_xlog_inplace(XLogReaderState *record) | |
HeapTupleHeader htup; | |
uint32 oldlen; | |
Size newlen; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO) | |
{ | |
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c | |
index 6ff9251..0e30ef3 100644 | |
--- a/src/backend/access/heap/pruneheap.c | |
+++ b/src/backend/access/heap/pruneheap.c | |
@@ -19,6 +19,7 @@ | |
#include "access/transam.h" | |
#include "access/htup_details.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "miscadmin.h" | |
#include "pgstat.h" | |
@@ -228,6 +229,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, | |
} | |
/* Any error while applying the changes is critical */ | |
+ ptrack_add_block(relation, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* Have we found any prunable items? */ | |
diff --git a/src/backend/access/heap/ptrack.c b/src/backend/access/heap/ptrack.c | |
new file mode 100644 | |
index 0000000..4d8d71a | |
--- /dev/null | |
+++ b/src/backend/access/heap/ptrack.c | |
@@ -0,0 +1,648 @@ | |
+/*------------------------------------------------------------------------- | |
+ * | |
+ * ptrack.c | |
+ * bitmap for tracking updates of relation's pages | |
+ * | |
+ * IDENTIFICATION | |
+ * src/backend/access/heap/ptrack.c | |
+ * | |
+ * INTERFACE ROUTINES (PostgreSQL side) | |
+ * | |
+ * ptrack_add_block - set a bit to track dirtied page | |
+ * ptrack_add_block_redo - set a bit to track recovered page | |
+ * create_ptrack_init_file - create PTRACK_INIT_FILE | |
+ * in the given database directory | |
+ * | |
+ * EXTERNAL INTERFACE ROUTINES (Backup utility side) | |
+ * pg_ptrack_version() - Returns PTRACK version currently in use. | |
+ * pg_ptrack_control_lsn() - Gets LSN from ptrack_control file. | |
+ * pg_ptrack_clear() - Resets bits in all PTRACK files. | |
+ * This function must be called for each database in the cluster. | |
+ * pg_ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid) - | |
+ * Reads a PTRACK file for the given relation and resets it. | |
+ * Returns the PTRACK content as bytea. It is essential to receive and clear the map | |
+ * atomically in order to avoid losing PTRACK bits because of race conditions. | |
+ * (Imagine that your backup tool reads the map, then some blocks of the relation are | |
+ * updated and the ptrack bits are set, after that the backup tool cleans up the map | |
+ * and resets ptrack_clear_lsn. So, we may lose some of the updates). | |
+ * This function must be called for each database in the cluster. | |
+ * pg_ptrack_init_get_and_clear(Oid db_oid, Oid tablespace_oid) - | |
+ * Checks whether PTRACK_INIT_FILE exists in the given database and deletes it. | |
+ * Returns true if the file was found. This function is analogous to | |
+ * pg_ptrack_get_and_clear(), but it handles directory-level changes | |
+ * (i.e. CREATE DATABASE, ALTER DATABASE SET TABLESPACE). | |
+ * This function must be called for each database in the cluster. | |
+ * | |
+ */ | |
+ | |
+#include "postgres.h" | |
+ | |
+#include "access/heapam_xlog.h" | |
+#include "access/heapam.h" | |
+#include "access/ptrack.h" | |
+#include "access/xlog.h" | |
+#include "access/xlogutils.h" | |
+#include "access/skey.h" | |
+#include "access/genam.h" | |
+#include "access/generic_xlog.h" | |
+#include "catalog/pg_depend.h" | |
+#include "catalog/pg_tablespace.h" | |
+#include "access/htup_details.h" | |
+#include "miscadmin.h" | |
+#include "storage/bufmgr.h" | |
+#include "storage/lmgr.h" | |
+#include "storage/smgr.h" | |
+#include "utils/inval.h" | |
+#include "utils/array.h" | |
+#include "utils/relfilenodemap.h" | |
+#include "utils/builtins.h" | |
+#include "utils/pg_lsn.h" | |
+#include <unistd.h> | |
+#include <sys/stat.h> | |
+ | |
+/* Effective data size */ | |
+#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData)) | |
+ | |
+/* Number of heap blocks we can represent in one byte. */ | |
+#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / PTRACK_BITS_PER_HEAPBLOCK) | |
+ | |
+/* Number of heap blocks we can represent in one ptrack map page. */ | |
+#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE) | |
+ | |
+/* Mapping from heap block number to the right bit in the ptrack map */ | |
+#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE) | |
+#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE) | |
+/* NOTE If you're going to increase PTRACK_BITS_PER_HEAPBLOCK, update macro below */ | |
+#define HEAPBLK_TO_MAPBIT(x) ((x) % HEAPBLOCKS_PER_BYTE) | |
+ | |
+bool ptrack_enable = false; | |
+ | |
+static Buffer ptrack_readbuf(Relation rel, BlockNumber blkno, bool extend); | |
+static void ptrack_extend(Relation rel, BlockNumber nvmblocks); | |
+static void ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf); | |
+ | |
+void SetPtrackClearLSN(bool set_invalid); | |
+ | |
+Datum pg_ptrack_clear(PG_FUNCTION_ARGS); | |
+Datum pg_ptrack_get_and_clear(PG_FUNCTION_ARGS); | |
+Datum pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS); | |
+Datum pg_ptrack_control_lsn(PG_FUNCTION_ARGS); | |
+ | |
+static void drop_ptrack_init_file(char *dest_dir); | |
+ | |
+/* | |
+ * Mark tracked memory block during recovery. | |
+ * We should not miss any recovery actions, including | |
+ * recovery from full-page writes. | |
+ */ | |
+void | |
+ptrack_add_block_redo(RelFileNode rnode, BlockNumber heapBlk) | |
+{ | |
+ Relation reln; | |
+ reln = CreateFakeRelcacheEntry(rnode); | |
+ ptrack_add_block(reln, heapBlk); | |
+ FreeFakeRelcacheEntry(reln); | |
+} | |
+ | |
+/* Save tracked memory block inside critical zone */ | |
+void | |
+ptrack_add_block(Relation rel, BlockNumber heapBlk) | |
+{ | |
+ Buffer ptrackbuf = InvalidBuffer; | |
+ | |
+ /* | |
+ * Do not track changes for unlogged and temp relations, | |
+ * since we are not going to backup them anyway. | |
+ */ | |
+ if (rel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT) | |
+ return; | |
+ | |
+ if (ptrack_enable) | |
+ { | |
+ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); | |
+ ptrackbuf = ptrack_readbuf(rel, mapBlock, true); | |
+ ptrack_set(heapBlk, ptrackbuf); | |
+ ReleaseBuffer(ptrackbuf); | |
+ } | |
+} | |
+ | |
+/* Set one bit to buffer */ | |
+static void | |
+ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf) | |
+{ | |
+ uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk); | |
+ uint8 mapOffset = HEAPBLK_TO_MAPBIT(heapBlk); | |
+ Page page; | |
+ char *map; | |
+ | |
+ page = BufferGetPage(ptrackBuf); | |
+ map = PageGetContents(page); | |
+ LockBuffer(ptrackBuf, BUFFER_LOCK_SHARE); | |
+ | |
+ /* Check if the bit already set */ | |
+ if (!(map[mapByte] & (1 << mapOffset))) | |
+ { | |
+ /* Bad luck. Take an exclusive lock now after unlock share.*/ | |
+ LockBuffer(ptrackBuf, BUFFER_LOCK_UNLOCK); | |
+ LockBuffer(ptrackBuf, BUFFER_LOCK_EXCLUSIVE); | |
+ | |
+ /* The bit could have been set concurrently */ | |
+ if (!(map[mapByte] & (1 << mapOffset))) | |
+ { | |
+ START_CRIT_SECTION(); | |
+ | |
+ map[mapByte] |= (1 << mapOffset); | |
+ MarkBufferDirty(ptrackBuf); | |
+ | |
+ /* | |
+ * We don't have Xlog entry for ptrack, update pages | |
+ * on recovery instead. | |
+ */ | |
+ END_CRIT_SECTION(); | |
+ } | |
+ } | |
+ | |
+ LockBuffer(ptrackBuf, BUFFER_LOCK_UNLOCK); | |
+} | |
+ | |
+/* | |
+ * Read a ptrack map page. | |
+ * | |
+ * If the page doesn't exist, InvalidBuffer is returned, or if 'extend' is | |
+ * true, the ptrack map file is extended. | |
+ */ | |
+static Buffer | |
+ptrack_readbuf(Relation rel, BlockNumber blkno, bool extend) | |
+{ | |
+ Buffer buf; | |
+ | |
+ /* | |
+ * We might not have opened the relation at the smgr level yet, or we | |
+ * might have been forced to close it by a sinval message. The code below | |
+ * won't necessarily notice relation extension immediately when extend = | |
+ * false, so we rely on sinval messages to ensure that our ideas about the | |
+ * size of the map aren't too far out of date. | |
+ */ | |
+ RelationOpenSmgr(rel); | |
+ | |
+ /* | |
+ * If we haven't cached the size of the ptrack map fork yet, check it | |
+ * first. | |
+ */ | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ | |
+ /* Handle requests beyond EOF */ | |
+ if (blkno >= rel->rd_smgr->smgr_ptrack_nblocks) | |
+ { | |
+ if (extend) | |
+ ptrack_extend(rel, blkno + 1); | |
+ else | |
+ return InvalidBuffer; | |
+ } | |
+ | |
+ /* We should never miss updated pages, so error out if page is corrupted */ | |
+ buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, blkno, | |
+ RBM_NORMAL, NULL); | |
+ | |
+ if (PageIsNew(BufferGetPage(buf))) | |
+ PageInit(BufferGetPage(buf), BLCKSZ, 0); | |
+ | |
+ return buf; | |
+} | |
+ | |
+/* | |
+ * Ensure that the ptrack map fork is at least ptrack_nblocks long, extending | |
+ * it if necessary with zeroed pages. | |
+ */ | |
+static void | |
+ptrack_extend(Relation rel, BlockNumber ptrack_nblocks) | |
+{ | |
+ BlockNumber ptrack_nblocks_now; | |
+ Page pg; | |
+ | |
+ pg = (Page) palloc(BLCKSZ); | |
+ PageInit(pg, BLCKSZ, 0); | |
+ | |
+ /* | |
+ * We use the relation extension lock to lock out other backends trying to | |
+ * extend the ptrack map at the same time. It also locks out extension | |
+ * of the main fork, unnecessarily, but extending the ptrack map | |
+ * happens seldom enough that it doesn't seem worthwhile to have a | |
+ * separate lock tag type for it. | |
+ * | |
+ * Note that another backend might have extended or created the relation | |
+ * by the time we get the lock. | |
+ */ | |
+ LockRelationForExtension(rel, ExclusiveLock); | |
+ | |
+ /* Might have to re-open if a cache flush happened */ | |
+ RelationOpenSmgr(rel); | |
+ | |
+ /* | |
+ * Create the file first if it doesn't exist. If smgr_ptrack_nblocks is | |
+ * positive then it must exist, no need for an smgrexists call. | |
+ */ | |
+ if ((rel->rd_smgr->smgr_ptrack_nblocks == 0 || | |
+ rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) && | |
+ !smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ smgrcreate(rel->rd_smgr, PAGESTRACK_FORKNUM, false); | |
+ | |
+ ptrack_nblocks_now = smgrnblocks(rel->rd_smgr, PAGESTRACK_FORKNUM); | |
+ | |
+ /* Now extend the file */ | |
+ while (ptrack_nblocks_now < ptrack_nblocks) | |
+ { | |
+ PageSetChecksumInplace(pg, ptrack_nblocks_now); | |
+ | |
+ smgrextend(rel->rd_smgr, PAGESTRACK_FORKNUM, ptrack_nblocks_now, | |
+ (char *) pg, false); | |
+ ptrack_nblocks_now++; | |
+ } | |
+ | |
+ /* | |
+ * Send a shared-inval message to force other backends to close any smgr | |
+ * references they may have for this rel, which we are about to change. | |
+ * This is a useful optimization because it means that backends don't have | |
+ * to keep checking for creation or extension of the file, which happens | |
+ * infrequently. | |
+ */ | |
+ CacheInvalidateSmgr(rel->rd_smgr->smgr_rnode); | |
+ | |
+ /* Update local cache with the up-to-date size */ | |
+ rel->rd_smgr->smgr_ptrack_nblocks = ptrack_nblocks_now; | |
+ | |
+ UnlockRelationForExtension(rel, ExclusiveLock); | |
+ | |
+ pfree(pg); | |
+} | |
+ | |
+ | |
+ | |
+/* Clear all blocks of relation's ptrack map */ | |
+static void | |
+ptrack_clear_one_rel(Oid relid) | |
+{ | |
+ BlockNumber nblock; | |
+ Relation rel = relation_open(relid, AccessShareLock); | |
+ | |
+ RelationOpenSmgr(rel); | |
+ | |
+ if (rel->rd_smgr == NULL) | |
+ { | |
+ relation_close(rel, AccessShareLock); | |
+ return; | |
+ } | |
+ | |
+ LockRelationForExtension(rel, ExclusiveLock); | |
+ | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ | |
+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++) | |
+ { | |
+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, | |
+ nblock, RBM_ZERO_ON_ERROR, NULL); | |
+ Page page = BufferGetPage(buf); | |
+ char *map = PageGetContents(page); | |
+ | |
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); | |
+ | |
+ START_CRIT_SECTION(); | |
+ MemSet(map, 0, MAPSIZE); | |
+ MarkBufferDirty(buf); | |
+ END_CRIT_SECTION(); | |
+ | |
+ UnlockReleaseBuffer(buf); | |
+ } | |
+ | |
+ UnlockRelationForExtension(rel, ExclusiveLock); | |
+ relation_close(rel, AccessShareLock); | |
+ return; | |
+} | |
+ | |
+/* Clear all ptrack files */ | |
+void | |
+ptrack_clear(void) | |
+{ | |
+ HeapTuple tuple; | |
+ Relation catalog = heap_open(RelationRelationId, AccessShareLock); | |
+ SysScanDesc scan = systable_beginscan(catalog, InvalidOid, false, NULL, 0, NULL); | |
+ | |
+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) | |
+ { | |
+ ptrack_clear_one_rel(HeapTupleGetOid(tuple)); | |
+ } | |
+ | |
+ systable_endscan(scan); | |
+ heap_close(catalog, AccessShareLock); | |
+ | |
+ /* | |
+ * Update ptrack_enabled_lsn to know | |
+ * that we track all changes since this LSN. | |
+ */ | |
+ SetPtrackClearLSN(false); | |
+} | |
+ | |
+/* TODO Rewiew and clean the code | |
+ * Get ptrack file as bytea and clear it */ | |
+bytea * | |
+ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid) | |
+{ | |
+ bytea *result = NULL; | |
+ BlockNumber nblock; | |
+ Relation rel = RelationIdGetRelation(RelidByRelfilenode(tablespace_oid, table_oid)); | |
+ | |
+ if (table_oid == InvalidOid) | |
+ { | |
+ elog(WARNING, "InvalidOid"); | |
+ goto full_end; | |
+ } | |
+ | |
+ if (rel == InvalidRelation) | |
+ { | |
+ elog(WARNING, "InvalidRelation"); | |
+ goto full_end; | |
+ } | |
+ | |
+ RelationOpenSmgr(rel); | |
+ if (rel->rd_smgr == NULL) | |
+ goto end_rel; | |
+ | |
+ LockRelationForExtension(rel, ExclusiveLock); | |
+ | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == 0) | |
+ { | |
+ UnlockRelationForExtension(rel, ExclusiveLock); | |
+ goto end_rel; | |
+ } | |
+ result = (bytea *) palloc(rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ); | |
+ SET_VARSIZE(result, rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ); | |
+ | |
+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++) | |
+ { | |
+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, | |
+ nblock, RBM_ZERO_ON_ERROR, NULL); | |
+ Page page = BufferGetPage(buf); | |
+ char *map = PageGetContents(page); | |
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); | |
+ START_CRIT_SECTION(); | |
+ memcpy(VARDATA(result) + nblock*MAPSIZE, map, MAPSIZE); | |
+ MemSet(map, 0, MAPSIZE); | |
+ MarkBufferDirty(buf); | |
+ END_CRIT_SECTION(); | |
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK); | |
+ ReleaseBuffer(buf); | |
+ } | |
+ | |
+ UnlockRelationForExtension(rel, ExclusiveLock); | |
+ end_rel: | |
+ RelationClose(rel); | |
+ | |
+ /* | |
+ * Update ptrack_enabled_lsn to know | |
+ * that we track all changes since this LSN. | |
+ */ | |
+ SetPtrackClearLSN(false); | |
+ full_end: | |
+ if (result == NULL) | |
+ { | |
+ result = palloc0(VARHDRSZ); | |
+ SET_VARSIZE(result, VARHDRSZ); | |
+ } | |
+ | |
+ return result; | |
+} | |
+ | |
+ | |
+/* | |
+ * Reset LSN in ptrack_control file. | |
+ * If server started with ptrack_enable = off, | |
+ * set ptrack_enabled_lsn to InvalidXLogRecPtr, | |
+ * otherwise set it to current lsn. | |
+ * | |
+ * Also we update the value after ptrack_clear() call, | |
+ * to to know that we track all changes since this LSN. | |
+ * | |
+ * Judging by this value, we can say, if it's legal to perform incremental | |
+ * ptrack backup, or we had lost ptrack mapping since previous backup and | |
+ * must do full backup now. | |
+ */ | |
+void | |
+SetPtrackClearLSN(bool set_invalid) | |
+{ | |
+ int fd; | |
+ XLogRecPtr ptrack_enabled_lsn; | |
+ char file_path[MAXPGPATH]; | |
+ | |
+ ptrack_enabled_lsn = (set_invalid)? | |
+ InvalidXLogRecPtr : GetXLogInsertRecPtr(); | |
+ | |
+ join_path_components(file_path, DataDir, "global/ptrack_control"); | |
+ canonicalize_path(file_path); | |
+ | |
+ fd = BasicOpenFile(file_path, | |
+ O_RDWR | O_CREAT | PG_BINARY, | |
+ S_IRUSR | S_IWUSR); | |
+ if (fd < 0) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not create ptrack control file \"%s\": %m", | |
+ "global/ptrack_control"))); | |
+ | |
+ errno = 0; | |
+ if (write(fd, &ptrack_enabled_lsn, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr)) | |
+ { | |
+ /* if write didn't set errno, assume problem is no disk space */ | |
+ if (errno == 0) | |
+ errno = ENOSPC; | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not write to ptrack control file: %m"))); | |
+ } | |
+ | |
+ if (pg_fsync(fd) != 0) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not fsync ptrack control file: %m"))); | |
+ | |
+ if (close(fd)) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not close ptrack control file: %m"))); | |
+} | |
+ | |
+/* | |
+ * If we disabled ptrack_enable, reset ptrack_enabled_lsn in ptrack_control | |
+ * file, to know, that it's illegal to perform incremental ptrack backup. | |
+ */ | |
+void | |
+assign_ptrack_enable(bool newval, void *extra) | |
+{ | |
+ if(DataDir != NULL && !IsBootstrapProcessingMode() && !newval) | |
+ SetPtrackClearLSN(true); | |
+} | |
+ | |
+/* Clear all ptrack files */ | |
+Datum | |
+pg_ptrack_clear(PG_FUNCTION_ARGS) | |
+{ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ ptrack_clear(); | |
+ | |
+ PG_RETURN_VOID(); | |
+} | |
+ | |
+/* Read all ptrack files and clear them afterwards */ | |
+Datum | |
+pg_ptrack_get_and_clear(PG_FUNCTION_ARGS) | |
+{ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ PG_RETURN_BYTEA_P(ptrack_get_and_clear(PG_GETARG_OID(0), PG_GETARG_OID(1))); | |
+} | |
+ | |
+/* | |
+ * Check if PTRACK_INIT_FILE exits in the given database | |
+ * and delete it. | |
+ * Args: dbOid and tblspcOid | |
+ * Return true if file existed. | |
+ */ | |
+Datum | |
+pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS) | |
+{ | |
+ char *db_path = GetDatabasePath(PG_GETARG_OID(0), PG_GETARG_OID(1)); | |
+ struct stat buf; | |
+ char ptrack_init_file_path[MAXPGPATH]; | |
+ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", db_path, PTRACK_INIT_FILE); | |
+ | |
+ if (stat(ptrack_init_file_path, &buf) == -1 && errno == ENOENT) | |
+ PG_RETURN_BOOL(false); | |
+ else if (!S_ISREG(buf.st_mode)) | |
+ PG_RETURN_BOOL(false); | |
+ else | |
+ { | |
+ drop_ptrack_init_file(db_path); | |
+ PG_RETURN_BOOL(true); | |
+ } | |
+} | |
+ | |
+/* | |
+ * Create PTRACK_INIT_FILE which allows to track changes | |
+ * on directory level made by operations which do not go | |
+ * through Shared Buffers. | |
+ */ | |
+void | |
+create_ptrack_init_file(char *dest_dir) | |
+{ | |
+ int dstfd; | |
+ | |
+ char ptrack_init_file_path[MAXPGPATH]; | |
+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", dest_dir, PTRACK_INIT_FILE); | |
+ | |
+ dstfd = OpenTransientFile(ptrack_init_file_path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, | |
+ S_IRUSR | S_IWUSR); | |
+ if (dstfd < 0) | |
+ { | |
+ if (errno != EEXIST) | |
+ ereport(ERROR, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not create file \"%s\": %m", ptrack_init_file_path))); | |
+ } | |
+ else if (CloseTransientFile(dstfd)) | |
+ ereport(ERROR, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not close file \"%s\": %m", ptrack_init_file_path))); | |
+} | |
+ | |
+/* Delete PTRACK_INIT_FILE */ | |
+void | |
+drop_ptrack_init_file(char *dest_dir) | |
+{ | |
+ char ptrack_init_file_path[MAXPGPATH]; | |
+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", dest_dir, PTRACK_INIT_FILE); | |
+ | |
+ if (unlink(ptrack_init_file_path) != 0) | |
+ { | |
+ if (errno != ENOENT) | |
+ ereport(WARNING, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not remove file \"%s\": %m", ptrack_init_file_path))); | |
+ } | |
+} | |
+ | |
+/* | |
+ * Returns ptrack version currently in use. | |
+ */ | |
+PG_FUNCTION_INFO_V1(ptrack_version); | |
+Datum | |
+ptrack_version(PG_FUNCTION_ARGS) | |
+{ | |
+ PG_RETURN_TEXT_P(cstring_to_text(PTRACK_VERSION)); | |
+} | |
+ | |
+/* Get lsn from ptrack_control file */ | |
+Datum | |
+pg_ptrack_control_lsn(PG_FUNCTION_ARGS) | |
+{ | |
+ int fd; | |
+ char file_path[MAXPGPATH]; | |
+ XLogRecPtr lsn = 0; | |
+ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role read ptrack files")))); | |
+ join_path_components(file_path, DataDir, "global/ptrack_control"); | |
+ canonicalize_path(file_path); | |
+ | |
+ fd = BasicOpenFile(file_path, O_RDONLY | PG_BINARY, 0); | |
+ if (fd < 0) | |
+ ereport(ERROR, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not open file \"%s\" for reading: %m", | |
+ file_path))); | |
+ | |
+ if (read(fd, &lsn, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr)) | |
+ ereport(ERROR, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not read content of the file \"%s\" %m", | |
+ file_path))); | |
+ | |
+ close(fd); | |
+ | |
+ PG_RETURN_LSN(lsn); | |
+} | |
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c | |
index 674b1bd..4759bcc 100644 | |
--- a/src/backend/access/heap/rewriteheap.c | |
+++ b/src/backend/access/heap/rewriteheap.c | |
@@ -114,6 +114,7 @@ | |
#include "access/tuptoaster.h" | |
#include "access/xact.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
@@ -330,11 +331,15 @@ end_heap_rewrite(RewriteState state) | |
if (state->rs_buffer_valid) | |
{ | |
if (state->rs_use_wal) | |
+ { | |
+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */ | |
+ ptrack_add_block(state->rs_new_rel, state->rs_blockno); | |
log_newpage(&state->rs_new_rel->rd_node, | |
MAIN_FORKNUM, | |
state->rs_blockno, | |
state->rs_buffer, | |
true); | |
+ } | |
RelationOpenSmgr(state->rs_new_rel); | |
PageSetChecksumInplace(state->rs_buffer, state->rs_blockno); | |
@@ -679,11 +684,15 @@ raw_heap_insert(RewriteState state, HeapTuple tup) | |
/* XLOG stuff */ | |
if (state->rs_use_wal) | |
+ { | |
+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */ | |
+ ptrack_add_block(state->rs_new_rel, state->rs_blockno); | |
log_newpage(&state->rs_new_rel->rd_node, | |
MAIN_FORKNUM, | |
state->rs_blockno, | |
page, | |
true); | |
+ } | |
/* | |
* Now write the page. We say isTemp = true even if it's not a | |
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c | |
index ef69290..9943666 100644 | |
--- a/src/backend/access/nbtree/nbtinsert.c | |
+++ b/src/backend/access/nbtree/nbtinsert.c | |
@@ -19,6 +19,7 @@ | |
#include "access/nbtree.h" | |
#include "access/transam.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/lmgr.h" | |
#include "storage/predicate.h" | |
@@ -837,6 +838,11 @@ _bt_insertonpg(Relation rel, | |
} | |
/* Do the update. No ereport(ERROR) until changes are logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
+ if (BufferIsValid(metabuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf)); | |
+ if (BufferIsValid(cbuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(cbuf)); | |
START_CRIT_SECTION(); | |
if (!_bt_pgaddtup(page, itemsz, itup, newitemoff)) | |
@@ -1223,6 +1229,12 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright, | |
* not starting the critical section till here because we haven't been | |
* scribbling on the original page yet; see comments above. | |
*/ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(rbuf)); | |
+ if (!P_RIGHTMOST(ropaque)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(sbuf)); | |
+ if (BufferIsValid(cbuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(cbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -1975,6 +1987,9 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf) | |
ItemPointerSet(&(right_item->t_tid), rbkno, P_HIKEY); | |
/* NO EREPORT(ERROR) from here till newroot op is logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(lbuf)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(rootbuf)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf)); | |
START_CRIT_SECTION(); | |
/* set btree special data */ | |
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c | |
index 2001dc1..61717bc 100644 | |
--- a/src/backend/access/nbtree/nbtpage.c | |
+++ b/src/backend/access/nbtree/nbtpage.c | |
@@ -26,6 +26,7 @@ | |
#include "access/transam.h" | |
#include "access/xlog.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/indexfsm.h" | |
#include "storage/lmgr.h" | |
@@ -221,6 +222,8 @@ _bt_getroot(Relation rel, int access) | |
rootopaque->btpo_cycleid = 0; | |
/* NO ELOG(ERROR) till meta is updated */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(rootbuf)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf)); | |
START_CRIT_SECTION(); | |
metad->btm_root = rootblkno; | |
@@ -793,6 +796,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf, | |
BTPageOpaque opaque; | |
/* No ereport(ERROR) until changes are logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
/* Fix the page */ | |
@@ -869,6 +873,7 @@ _bt_delitems_delete(Relation rel, Buffer buf, | |
Assert(nitems > 0); | |
/* No ereport(ERROR) until changes are logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
/* Fix the page */ | |
@@ -1409,6 +1414,8 @@ _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack) | |
PredicateLockPageCombine(rel, leafblkno, leafrightsib); | |
/* No ereport(ERROR) until changes are logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(topparent)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(leafbuf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -1731,6 +1738,14 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) | |
*/ | |
/* No ereport(ERROR) until changes are logged */ | |
+ ptrack_add_block(rel, BufferGetBlockNumber(rbuf)); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
+ if (BufferIsValid(lbuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(lbuf)); | |
+ if (BufferIsValid(metabuf)) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf)); | |
+ if (target != leafblkno) | |
+ ptrack_add_block(rel, BufferGetBlockNumber(leafbuf)); | |
START_CRIT_SECTION(); | |
/* | |
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c | |
index a264b92..8da35ef 100644 | |
--- a/src/backend/access/nbtree/nbtree.c | |
+++ b/src/backend/access/nbtree/nbtree.c | |
@@ -21,6 +21,7 @@ | |
#include "access/nbtree.h" | |
#include "access/relscan.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/index.h" | |
#include "commands/vacuum.h" | |
#include "storage/indexfsm.h" | |
@@ -252,6 +253,7 @@ btbuildempty(Relation index) | |
PageSetChecksumInplace(metapage, BTREE_METAPAGE); | |
smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE, | |
(char *) metapage, true); | |
+ | |
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, | |
BTREE_METAPAGE, metapage, false); | |
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c | |
index 99a014e..d4afb93 100644 | |
--- a/src/backend/access/nbtree/nbtsort.c | |
+++ b/src/backend/access/nbtree/nbtsort.c | |
@@ -69,6 +69,7 @@ | |
#include "access/nbtree.h" | |
#include "access/xlog.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/smgr.h" | |
#include "tcop/tcopprot.h" | |
@@ -276,8 +277,12 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) | |
/* XLOG stuff */ | |
if (wstate->btws_use_wal) | |
{ | |
+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */ | |
+ ptrack_add_block(wstate->index, blkno); | |
/* We use the heap NEWPAGE record type for this */ | |
log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page, true); | |
+ /* Ensure rd_smgr is open (could have been closed by relcache flush!) */ | |
+ RelationOpenSmgr(wstate->index); | |
} | |
/* | |
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c | |
index c536e22..d834878 100644 | |
--- a/src/backend/access/nbtree/nbtxlog.c | |
+++ b/src/backend/access/nbtree/nbtxlog.c | |
@@ -19,6 +19,7 @@ | |
#include "access/transam.h" | |
#include "access/xlog.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "storage/procarray.h" | |
#include "miscadmin.h" | |
@@ -82,6 +83,11 @@ _bt_restore_meta(XLogReaderState *record, uint8 block_id) | |
xl_btree_metadata *xlrec; | |
char *ptr; | |
Size len; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
metabuf = XLogInitBufferForRedo(record, block_id); | |
ptr = XLogRecGetBlockData(record, block_id, &len); | |
@@ -127,6 +133,11 @@ _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id) | |
{ | |
XLogRecPtr lsn = record->EndRecPtr; | |
Buffer buf; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO) | |
{ | |
@@ -150,6 +161,11 @@ btree_xlog_insert(bool isleaf, bool ismeta, XLogReaderState *record) | |
xl_btree_insert *xlrec = (xl_btree_insert *) XLogRecGetData(record); | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* Insertion to an internal page finishes an incomplete split at the child | |
@@ -207,11 +223,17 @@ btree_xlog_split(bool onleft, bool isroot, XLogReaderState *record) | |
BlockNumber leftsib; | |
BlockNumber rightsib; | |
BlockNumber rnext; | |
+ RelFileNode rnode; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &leftsib); | |
+ ptrack_add_block_redo(rnode, leftsib); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &rightsib); | |
+ ptrack_add_block_redo(rnode, rightsib); | |
- XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib); | |
- XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib); | |
if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext)) | |
rnext = P_NONE; | |
+ else | |
+ ptrack_add_block_redo(rnode, rnext); | |
/* | |
* Clear the incomplete split flag on the left sibling of the child page | |
@@ -388,6 +410,12 @@ btree_xlog_vacuum(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
BTPageOpaque opaque; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ | |
#ifdef UNUSED | |
xl_btree_vacuum *xlrec = (xl_btree_vacuum *) XLogRecGetData(record); | |
@@ -566,6 +594,7 @@ btree_xlog_delete_get_latestRemovedXid(XLogReaderState *record) | |
* overkill, but it's safe, and certainly better than panicking here. | |
*/ | |
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
ibuffer = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL); | |
if (!BufferIsValid(ibuffer)) | |
return InvalidTransactionId; | |
@@ -591,6 +620,7 @@ btree_xlog_delete_get_latestRemovedXid(XLogReaderState *record) | |
*/ | |
hblkno = ItemPointerGetBlockNumber(&(itup->t_tid)); | |
hbuffer = XLogReadBufferExtended(xlrec->hnode, MAIN_FORKNUM, hblkno, RBM_NORMAL); | |
+ ptrack_add_block_redo(rnode, hblkno); | |
if (!BufferIsValid(hbuffer)) | |
{ | |
UnlockReleaseBuffer(ibuffer); | |
@@ -665,6 +695,11 @@ btree_xlog_delete(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
BTPageOpaque opaque; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* If we have any conflict processing to do, it must happen before we | |
@@ -727,6 +762,13 @@ btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record) | |
Page page; | |
BTPageOpaque pageop; | |
IndexTupleData trunctuple; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* In normal operation, we would lock all the pages this WAL record | |
@@ -810,10 +852,27 @@ btree_xlog_unlink_page(uint8 info, XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
BTPageOpaque pageop; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
leftsib = xlrec->leftsib; | |
rightsib = xlrec->rightsib; | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if (leftsib != P_NONE) | |
+ { | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if (XLogRecHasBlockRef(record, 3)) | |
+ { | |
+ XLogRecGetBlockTag(record, 3, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
+ | |
/* | |
* In normal operation, we would lock all the pages this WAL record | |
* touches before changing any of them. In WAL replay, it should be okay | |
@@ -924,6 +983,11 @@ btree_xlog_newroot(XLogReaderState *record) | |
BTPageOpaque pageop; | |
char *ptr; | |
Size len; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
buffer = XLogInitBufferForRedo(record, 0); | |
page = (Page) BufferGetPage(buffer); | |
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c | |
index f090ca5..88dfb56 100644 | |
--- a/src/backend/access/spgist/spgdoinsert.c | |
+++ b/src/backend/access/spgist/spgdoinsert.c | |
@@ -18,6 +18,7 @@ | |
#include "access/genam.h" | |
#include "access/spgist_private.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "miscadmin.h" | |
#include "storage/bufmgr.h" | |
#include "utils/rel.h" | |
@@ -213,6 +214,9 @@ addLeafTuple(Relation index, SpGistState *state, SpGistLeafTuple leafTuple, | |
xlrec.offnumParent = InvalidOffsetNumber; | |
xlrec.nodeI = 0; | |
+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer)); | |
+ if (parent->buffer) | |
+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer)); | |
START_CRIT_SECTION(); | |
if (current->offnum == InvalidOffsetNumber || | |
@@ -457,6 +461,9 @@ moveLeafs(Relation index, SpGistState *state, | |
leafdata = leafptr = palloc(size); | |
+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(nbuf)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer)); | |
START_CRIT_SECTION(); | |
/* copy all the old tuples to new page, unless they're dead */ | |
@@ -1109,6 +1116,14 @@ doPickSplit(Relation index, SpGistState *state, | |
leafdata = leafptr = (char *) palloc(totalLeafSizes); | |
/* Here we begin making the changes to the target pages */ | |
+ if (current->buffer != InvalidBuffer) | |
+ ptrack_add_block(index, current->blkno); | |
+ if (parent->buffer != InvalidBuffer) | |
+ ptrack_add_block(index, parent->blkno); | |
+ if (newInnerBuffer != InvalidBuffer) | |
+ ptrack_add_block(index, BufferGetBlockNumber(newInnerBuffer)); | |
+ if (newLeafBuffer != InvalidBuffer) | |
+ ptrack_add_block(index, BufferGetBlockNumber(newLeafBuffer)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -1519,6 +1534,7 @@ spgAddNodeAction(Relation index, SpGistState *state, | |
/* | |
* We can replace the inner tuple by new version in-place | |
*/ | |
+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer)); | |
START_CRIT_SECTION(); | |
PageIndexTupleDelete(current->page, current->offnum); | |
@@ -1602,6 +1618,9 @@ spgAddNodeAction(Relation index, SpGistState *state, | |
else | |
xlrec.parentBlk = 2; | |
+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(saveCurrent.buffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer)); | |
START_CRIT_SECTION(); | |
/* insert new ... */ | |
@@ -1764,6 +1783,9 @@ spgSplitNodeAction(Relation index, SpGistState *state, | |
&xlrec.newPage); | |
} | |
+ if (newBuffer != InvalidBuffer) | |
+ ptrack_add_block(index, BufferGetBlockNumber(newBuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer)); | |
START_CRIT_SECTION(); | |
/* | |
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c | |
index 3eaa649..53f292c 100644 | |
--- a/src/backend/access/spgist/spginsert.c | |
+++ b/src/backend/access/spgist/spginsert.c | |
@@ -20,6 +20,7 @@ | |
#include "access/spgist_private.h" | |
#include "access/xlog.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/index.h" | |
#include "miscadmin.h" | |
#include "storage/bufmgr.h" | |
@@ -90,6 +91,9 @@ spgbuild(Relation heap, Relation index, IndexInfo *indexInfo) | |
Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO); | |
Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO); | |
+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(rootbuffer)); | |
+ ptrack_add_block(index, BufferGetBlockNumber(nullbuffer)); | |
START_CRIT_SECTION(); | |
SpGistInitMetapage(BufferGetPage(metabuffer)); | |
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c | |
index 15b867f..7afa7fb 100644 | |
--- a/src/backend/access/spgist/spgvacuum.c | |
+++ b/src/backend/access/spgist/spgvacuum.c | |
@@ -19,6 +19,7 @@ | |
#include "access/spgist_private.h" | |
#include "access/transam.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/storage_xlog.h" | |
#include "commands/vacuum.h" | |
#include "miscadmin.h" | |
@@ -323,6 +324,7 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer, | |
elog(ERROR, "inconsistent counts of deletable tuples"); | |
/* Do the updates */ | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
spgPageIndexMultiDelete(&bds->spgstate, page, | |
@@ -447,6 +449,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer) | |
return; /* nothing more to do */ | |
/* Do the update */ | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* The tuple numbers are in order, so we can use PageIndexMultiDelete */ | |
@@ -504,6 +507,7 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer) | |
xlrec.nToPlaceholder = 0; | |
xlrec.newestRedirectXid = InvalidTransactionId; | |
+ ptrack_add_block(index, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
/* | |
diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c | |
index e016cdb..6033bb8 100644 | |
--- a/src/backend/access/spgist/spgxlog.c | |
+++ b/src/backend/access/spgist/spgxlog.c | |
@@ -18,6 +18,7 @@ | |
#include "access/transam.h" | |
#include "access/xlog.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "storage/standby.h" | |
#include "utils/memutils.h" | |
@@ -76,6 +77,15 @@ spgRedoCreateIndex(XLogReaderState *record) | |
XLogRecPtr lsn = record->EndRecPtr; | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
buffer = XLogInitBufferForRedo(record, 0); | |
Assert(BufferGetBlockNumber(buffer) == SPGIST_METAPAGE_BLKNO); | |
@@ -113,6 +123,16 @@ spgRedoAddLeaf(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if (xldata->offnumParent != InvalidOffsetNumber) | |
+ { | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
ptr += sizeof(spgxlogAddLeaf); | |
leafTuple = ptr; | |
@@ -213,8 +233,15 @@ spgRedoMoveLeafs(XLogReaderState *record) | |
Page page; | |
XLogRedoAction action; | |
BlockNumber blknoDst; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
- XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoDst); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blknoDst); | |
+ ptrack_add_block_redo(rnode, blknoDst); | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
fillFakeState(&state, xldata->stateSrc); | |
@@ -324,6 +351,21 @@ spgRedoAddNode(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if(XLogRecHasBlockRef(record, 1)) | |
+ { | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
+ if (xldata->parentBlk == 2) | |
+ { | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
ptr += sizeof(spgxlogAddNode); | |
innerTuple = ptr; | |
@@ -492,6 +534,13 @@ spgRedoSplitTuple(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
XLogRedoAction action; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
ptr += sizeof(spgxlogSplitTuple); | |
prefixTuple = ptr; | |
@@ -578,8 +627,23 @@ spgRedoPickSplit(XLogReaderState *record) | |
int i; | |
BlockNumber blknoInner; | |
XLogRedoAction action; | |
- | |
- XLogRecGetBlockTag(record, 2, NULL, NULL, &blknoInner); | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blknoInner); | |
+ ptrack_add_block_redo(rnode, blknoInner); | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ if (XLogRecHasBlockRef(record, 1)) | |
+ { | |
+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
+ if (XLogRecHasBlockRef(record, 3)) | |
+ { | |
+ XLogRecGetBlockTag(record, 3, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
+ } | |
fillFakeState(&state, xldata->stateSrc); | |
@@ -795,6 +859,11 @@ spgRedoVacuumLeaf(XLogReaderState *record) | |
Buffer buffer; | |
Page page; | |
int i; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
fillFakeState(&state, xldata->stateSrc); | |
@@ -871,6 +940,11 @@ spgRedoVacuumRoot(XLogReaderState *record) | |
OffsetNumber *toDelete; | |
Buffer buffer; | |
Page page; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
toDelete = xldata->offsets; | |
@@ -896,6 +970,11 @@ spgRedoVacuumRedirect(XLogReaderState *record) | |
spgxlogVacuumRedirect *xldata = (spgxlogVacuumRedirect *) ptr; | |
OffsetNumber *itemToPlaceholder; | |
Buffer buffer; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
itemToPlaceholder = xldata->offsets; | |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c | |
index 68109e2..6cbd00f 100644 | |
--- a/src/backend/access/transam/xlog.c | |
+++ b/src/backend/access/transam/xlog.c | |
@@ -35,6 +35,7 @@ | |
#include "access/xloginsert.h" | |
#include "access/xlogreader.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catversion.h" | |
#include "catalog/pg_control.h" | |
#include "catalog/pg_database.h" | |
@@ -9484,6 +9485,11 @@ xlog_redo(XLogReaderState *record) | |
else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT) | |
{ | |
Buffer buffer; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
/* | |
* Full-page image (FPI) records contain nothing else but a backup | |
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c | |
index a92534e..3552135 100644 | |
--- a/src/backend/access/transam/xloginsert.c | |
+++ b/src/backend/access/transam/xloginsert.c | |
@@ -23,6 +23,7 @@ | |
#include "access/xlog.h" | |
#include "access/xlog_internal.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/pg_control.h" | |
#include "common/pg_lzcompress.h" | |
#include "miscadmin.h" | |
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c | |
index 51a8e8d..739b5c2 100644 | |
--- a/src/backend/access/transam/xlogutils.c | |
+++ b/src/backend/access/transam/xlogutils.c | |
@@ -550,8 +550,6 @@ CreateFakeRelcacheEntry(RelFileNode rnode) | |
FakeRelCacheEntry fakeentry; | |
Relation rel; | |
- Assert(InRecovery); | |
- | |
/* Allocate the Relation struct and all related space in one block. */ | |
fakeentry = palloc0(sizeof(FakeRelCacheEntryData)); | |
rel = (Relation) fakeentry; | |
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c | |
index 0d8311c..778dbfb 100644 | |
--- a/src/backend/catalog/storage.c | |
+++ b/src/backend/catalog/storage.c | |
@@ -238,6 +238,7 @@ RelationTruncate(Relation rel, BlockNumber nblocks) | |
rel->rd_smgr->smgr_targblock = InvalidBlockNumber; | |
rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber; | |
rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber; | |
+ rel->rd_smgr->smgr_ptrack_nblocks = InvalidBlockNumber; | |
/* Truncate the FSM first if it exists */ | |
fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM); | |
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c | |
index f47a13d..79fab18 100644 | |
--- a/src/backend/commands/dbcommands.c | |
+++ b/src/backend/commands/dbcommands.c | |
@@ -30,6 +30,7 @@ | |
#include "access/xact.h" | |
#include "access/xloginsert.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/dependency.h" | |
#include "catalog/indexing.h" | |
@@ -616,6 +617,7 @@ createdb(const CreatedbStmt *stmt) | |
* We don't need to copy subdirectories | |
*/ | |
copydir(srcpath, dstpath, false); | |
+ create_ptrack_init_file(dstpath); | |
/* Record the filesystem change in XLOG */ | |
{ | |
@@ -1222,6 +1224,7 @@ movedb(const char *dbname, const char *tblspcname) | |
* Copy files from the old tablespace to the new one | |
*/ | |
copydir(src_dbpath, dst_dbpath, false); | |
+ create_ptrack_init_file(dst_dbpath); | |
/* | |
* Record the filesystem change in XLOG | |
@@ -2085,6 +2088,7 @@ dbase_redo(XLogReaderState *record) | |
* We don't need to copy subdirectories | |
*/ | |
copydir(src_path, dst_path, false); | |
+ create_ptrack_init_file(dst_path); | |
} | |
else if (info == XLOG_DBASE_DROP) | |
{ | |
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c | |
index c98f981..1fc6114 100644 | |
--- a/src/backend/commands/sequence.c | |
+++ b/src/backend/commands/sequence.c | |
@@ -21,6 +21,7 @@ | |
#include "access/xlog.h" | |
#include "access/xloginsert.h" | |
#include "access/xlogutils.h" | |
+#include "access/ptrack.h" | |
#include "catalog/dependency.h" | |
#include "catalog/namespace.h" | |
#include "catalog/objectaccess.h" | |
@@ -365,6 +366,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple) | |
if (RelationNeedsWAL(rel)) | |
GetTopTransactionId(); | |
+ ptrack_add_block(rel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
MarkBufferDirty(buf); | |
@@ -451,6 +453,7 @@ AlterSequence(AlterSeqStmt *stmt) | |
GetTopTransactionId(); | |
/* Now okay to update the on-disk tuple */ | |
+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
memcpy(seq, &new, sizeof(FormData_pg_sequence)); | |
@@ -711,6 +714,7 @@ nextval_internal(Oid relid) | |
GetTopTransactionId(); | |
/* ready to change the on-disk (or really, in-buffer) tuple */ | |
+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
/* | |
@@ -910,6 +914,7 @@ do_setval(Oid relid, int64 next, bool iscalled) | |
GetTopTransactionId(); | |
/* ready to change the on-disk (or really, in-buffer) tuple */ | |
+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
seq->last_value = next; /* last fetched number */ | |
@@ -1586,6 +1591,11 @@ seq_redo(XLogReaderState *record) | |
Size itemsz; | |
xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record); | |
sequence_magic *sm; | |
+ RelFileNode rnode; | |
+ BlockNumber blkno; | |
+ | |
+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno); | |
+ ptrack_add_block_redo(rnode, blkno); | |
if (info != XLOG_SEQ_LOG) | |
elog(PANIC, "seq_redo: unknown op code %u", info); | |
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c | |
index 13d1102..dc1a2df 100644 | |
--- a/src/backend/commands/tablecmds.c | |
+++ b/src/backend/commands/tablecmds.c | |
@@ -23,6 +23,7 @@ | |
#include "access/tupconvert.h" | |
#include "access/xact.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/dependency.h" | |
#include "catalog/heap.h" | |
@@ -9773,6 +9774,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) | |
/* copy those extra forks that exist */ | |
for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++) | |
{ | |
+ /* | |
+ * Do not copy ptrack fork, because it will be created | |
+ * for new relation while copying data. | |
+ */ | |
+ if (forkNum == PAGESTRACK_FORKNUM) | |
+ continue; | |
+ | |
if (smgrexists(rel->rd_smgr, forkNum)) | |
{ | |
smgrcreate(dstrel, forkNum, false); | |
@@ -10056,7 +10064,18 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst, | |
* space. | |
*/ | |
if (use_wal) | |
+ { | |
+ /* | |
+ * Don't forget to set ptrack bit even if we're skipping bufmgr | |
+ * stage. The reason to use ptrack_add_block_redo() instead of the | |
+ * regular ptrack_add_block() function is that we don't have | |
+ * a Relation structure here. | |
+ */ | |
+ if (forkNum == MAIN_FORKNUM && | |
+ relpersistence == RELPERSISTENCE_PERMANENT) | |
+ ptrack_add_block_redo(dst->smgr_rnode.node, blkno); | |
log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false); | |
+ } | |
PageSetChecksumInplace(page, blkno); | |
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c | |
index 8f59732..943957d 100644 | |
--- a/src/backend/commands/vacuumlazy.c | |
+++ b/src/backend/commands/vacuumlazy.c | |
@@ -45,6 +45,7 @@ | |
#include "access/transam.h" | |
#include "access/visibilitymap.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/storage.h" | |
#include "commands/dbcommands.h" | |
@@ -855,6 +856,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, | |
empty_pages++; | |
} | |
freespace = PageGetHeapFreeSpace(page); | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buf)); | |
MarkBufferDirty(buf); | |
UnlockReleaseBuffer(buf); | |
@@ -870,6 +872,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, | |
/* empty pages are always all-visible and all-frozen */ | |
if (!PageIsAllVisible(page)) | |
{ | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
/* mark buffer dirty before writing a WAL record */ | |
@@ -1095,6 +1098,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, | |
*/ | |
if (nfrozen > 0) | |
{ | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buf)); | |
START_CRIT_SECTION(); | |
MarkBufferDirty(buf); | |
@@ -1168,6 +1172,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, | |
* rare cases after a crash, it is not worth optimizing. | |
*/ | |
PageSetAllVisible(page); | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buf)); | |
MarkBufferDirty(buf); | |
visibilitymap_set(onerel, blkno, buf, InvalidXLogRecPtr, | |
vmbuffer, visibility_cutoff_xid, flags); | |
@@ -1207,6 +1212,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, | |
elog(WARNING, "page containing dead tuples is marked as all-visible in relation \"%s\" page %u", | |
relname, blkno); | |
PageClearAllVisible(page); | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buf)); | |
MarkBufferDirty(buf); | |
visibilitymap_clear(onerel, blkno, vmbuffer, | |
VISIBILITYMAP_VALID_BITS); | |
@@ -1441,6 +1447,7 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, | |
pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno); | |
+ ptrack_add_block(onerel, BufferGetBlockNumber(buffer)); | |
START_CRIT_SECTION(); | |
for (; tupindex < vacrelstats->num_dead_tuples; tupindex++) | |
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c | |
index 94aa952..101c27d 100644 | |
--- a/src/backend/storage/smgr/smgr.c | |
+++ b/src/backend/storage/smgr/smgr.c | |
@@ -170,6 +170,7 @@ smgropen(RelFileNode rnode, BackendId backend) | |
reln->smgr_targblock = InvalidBlockNumber; | |
reln->smgr_fsm_nblocks = InvalidBlockNumber; | |
reln->smgr_vm_nblocks = InvalidBlockNumber; | |
+ reln->smgr_ptrack_nblocks = InvalidBlockNumber; | |
reln->smgr_which = 0; /* we only have md.c at present */ | |
/* mark it not open */ | |
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c | |
index 0a4295b..401714f 100644 | |
--- a/src/backend/utils/init/postinit.c | |
+++ b/src/backend/utils/init/postinit.c | |
@@ -24,6 +24,7 @@ | |
#include "access/sysattr.h" | |
#include "access/xact.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/indexing.h" | |
#include "catalog/namespace.h" | |
@@ -565,6 +566,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, | |
elog(DEBUG3, "InitPostgres"); | |
+ assign_ptrack_enable(ptrack_enable, NULL); | |
+ | |
/* | |
* Add my PGPROC struct to the ProcArray. | |
* | |
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c | |
index 7b8e108..74e5a70 100644 | |
--- a/src/backend/utils/misc/guc.c | |
+++ b/src/backend/utils/misc/guc.c | |
@@ -31,6 +31,7 @@ | |
#include "access/transam.h" | |
#include "access/twophase.h" | |
#include "access/xact.h" | |
+#include "access/ptrack.h" | |
#include "catalog/namespace.h" | |
#include "commands/async.h" | |
#include "commands/prepare.h" | |
@@ -1001,6 +1002,16 @@ static struct config_bool ConfigureNamesBool[] = | |
}, | |
{ | |
+ {"ptrack_enable", PGC_SIGHUP, WAL_SETTINGS, | |
+ gettext_noop("Enable page tracking."), | |
+ NULL | |
+ }, | |
+ &ptrack_enable, | |
+ false, | |
+ NULL, &assign_ptrack_enable, NULL | |
+ }, | |
+ | |
+ { | |
{"wal_compression", PGC_SUSET, WAL_SETTINGS, | |
gettext_noop("Compresses full-page writes written in WAL file."), | |
NULL | |
diff --git a/src/common/relpath.c b/src/common/relpath.c | |
index 1aacb81..324e9eb 100644 | |
--- a/src/common/relpath.c | |
+++ b/src/common/relpath.c | |
@@ -35,7 +35,8 @@ const char *const forkNames[] = { | |
"main", /* MAIN_FORKNUM */ | |
"fsm", /* FSM_FORKNUM */ | |
"vm", /* VISIBILITYMAP_FORKNUM */ | |
- "init" /* INIT_FORKNUM */ | |
+ "init", /* INIT_FORKNUM */ | |
+ "ptrack" /* PAGESTRACK_FORKNUM */ | |
}; | |
/* | |
diff --git a/src/include/access/ptrack.h b/src/include/access/ptrack.h | |
new file mode 100644 | |
index 0000000..389a33c | |
--- /dev/null | |
+++ b/src/include/access/ptrack.h | |
@@ -0,0 +1,30 @@ | |
+#ifndef PTRACK_H | |
+#define PTRACK_H | |
+ | |
+#include "access/xlogdefs.h" | |
+#include "storage/block.h" | |
+#include "storage/buf.h" | |
+#include "storage/relfilenode.h" | |
+#include "utils/relcache.h" | |
+ | |
+/* Ptrack version as a string */ | |
+#define PTRACK_VERSION "1.4" | |
+/* Ptrack version as a number */ | |
+#define PTRACK_VERSION_NUM 104 | |
+ | |
+/* Number of bits allocated for each heap block. */ | |
+#define PTRACK_BITS_PER_HEAPBLOCK 1 | |
+ | |
+#define PTRACK_INIT_FILE "ptrack_init" | |
+ | |
+extern PGDLLIMPORT bool ptrack_enable; | |
+ | |
+extern void ptrack_add_block(Relation rel, BlockNumber heapBlk); | |
+extern void ptrack_add_block_redo(RelFileNode rnode, BlockNumber heapBlk); | |
+extern void create_ptrack_init_file(char *dest_dir); | |
+ | |
+extern void ptrack_clear(void); | |
+extern bytea *ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid); | |
+extern void assign_ptrack_enable(bool newval, void *extra); | |
+ | |
+#endif /* PTRACK_H */ | |
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h | |
index af19c1a..97118c6 100644 | |
--- a/src/include/catalog/pg_proc.h | |
+++ b/src/include/catalog/pg_proc.h | |
@@ -5347,6 +5347,18 @@ DESCR("pg_controldata init state information as a function"); | |
#define PROPARALLEL_RESTRICTED 'r' /* can run in parallel master only */ | |
#define PROPARALLEL_UNSAFE 'u' /* banned while in parallel mode */ | |
+/* ptrack related functions*/ | |
+DATA(insert OID = 6016 ( pg_ptrack_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v u 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_clear _null_ _null_ _null_ )); | |
+DESCR("clear ptrack fork files"); | |
+DATA(insert OID = 6018 ( pg_ptrack_get_and_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 17 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear _null_ _null_ _null_ )); | |
+DESCR("get ptrack file as bytea and clear it"); | |
+DATA(insert OID = 6022 ( pg_ptrack_get_and_clear_db PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear_db _null_ _null_ _null_ )); | |
+DESCR("check if ptrack_init_file exists in the given database"); | |
+DATA(insert OID = 6021 ( ptrack_version PGNSP PGUID 12 1 0 0 0 f f f f t f s s 0 0 25 "" _null_ _null_ _null_ _null_ _null_ ptrack_version _null_ _null_ _null_ )); | |
+DESCR("Ptrack version string"); | |
+DATA(insert OID = 6023 ( pg_ptrack_control_lsn PGNSP PGUID 12 1 0 0 0 f f f f t f s s 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_control_lsn _null_ _null_ _null_ )); | |
+DESCR("read LSN from ptrack_control file"); | |
+ | |
/* | |
* Symbolic values for proargmodes column. Note that these must agree with | |
* the FunctionParameterMode enum in parsenodes.h; we declare them here to | |
diff --git a/src/include/common/relpath.h b/src/include/common/relpath.h | |
index 8754b2f..edf69bd 100644 | |
--- a/src/include/common/relpath.h | |
+++ b/src/include/common/relpath.h | |
@@ -27,7 +27,8 @@ typedef enum ForkNumber | |
MAIN_FORKNUM = 0, | |
FSM_FORKNUM, | |
VISIBILITYMAP_FORKNUM, | |
- INIT_FORKNUM | |
+ INIT_FORKNUM, | |
+ PAGESTRACK_FORKNUM | |
/* | |
* NOTE: if you add a new fork, change MAX_FORKNUM and possibly | |
@@ -36,9 +37,9 @@ typedef enum ForkNumber | |
*/ | |
} ForkNumber; | |
-#define MAX_FORKNUM INIT_FORKNUM | |
+#define MAX_FORKNUM PAGESTRACK_FORKNUM | |
-#define FORKNAMECHARS 4 /* max chars for a fork name */ | |
+#define FORKNAMECHARS 5 /* max chars for a fork name */ | |
extern const char *const forkNames[]; | |
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h | |
index a8e7877..3b4a4ad 100644 | |
--- a/src/include/storage/smgr.h | |
+++ b/src/include/storage/smgr.h | |
@@ -46,15 +46,16 @@ typedef struct SMgrRelationData | |
struct SMgrRelationData **smgr_owner; | |
/* | |
- * These next three fields are not actually used or manipulated by smgr, | |
+ * These next four fields are not actually used or manipulated by smgr, | |
* except that they are reset to InvalidBlockNumber upon a cache flush | |
* event (in particular, upon truncation of the relation). Higher levels | |
* store cached state here so that it will be reset when truncation | |
- * happens. In all three cases, InvalidBlockNumber means "unknown". | |
+ * happens. In all four cases, InvalidBlockNumber means "unknown". | |
*/ | |
BlockNumber smgr_targblock; /* current insertion target block */ | |
BlockNumber smgr_fsm_nblocks; /* last known size of fsm fork */ | |
- BlockNumber smgr_vm_nblocks; /* last known size of vm fork */ | |
+ BlockNumber smgr_vm_nblocks; /* last known size of vm fork */ | |
+ BlockNumber smgr_ptrack_nblocks; /* last known size of ptrack fork */ | |
/* additional public fields may someday exist here */ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment