· 8 years ago · Mar 27, 2018, 07:26 PM
1diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
2index 706b3ee..d51835b 100644
3--- a/src/backend/access/brin/brin.c
4+++ b/src/backend/access/brin/brin.c
5@@ -22,6 +22,7 @@
6 #include "access/reloptions.h"
7 #include "access/relscan.h"
8 #include "access/xloginsert.h"
9+#include "access/ptrack.h"
10 #include "catalog/index.h"
11 #include "catalog/pg_am.h"
12 #include "miscadmin.h"
13@@ -684,6 +685,7 @@ brinbuildempty(Relation index)
14 LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
15
16 /* Initialize and xlog metabuffer. */
17+ ptrack_add_block(index, BufferGetBlockNumber(metabuf));
18 START_CRIT_SECTION();
19 brin_metapage_init(BufferGetPage(metabuf), BrinGetPagesPerRange(index),
20 BRIN_CURRENT_VERSION);
21diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
22index bb30e41..6ca83f5 100644
23--- a/src/backend/access/brin/brin_pageops.c
24+++ b/src/backend/access/brin/brin_pageops.c
25@@ -15,6 +15,7 @@
26 #include "access/brin_revmap.h"
27 #include "access/brin_xlog.h"
28 #include "access/xloginsert.h"
29+#include "access/ptrack.h"
30 #include "miscadmin.h"
31 #include "storage/bufmgr.h"
32 #include "storage/freespace.h"
33@@ -181,6 +182,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
34 UnlockReleaseBuffer(newbuf);
35 }
36
37+ ptrack_add_block(idxrel, BufferGetBlockNumber(oldbuf));
38 START_CRIT_SECTION();
39 PageIndexDeleteNoCompact(oldpage, &oldoff, 1);
40 if (PageAddItemExtended(oldpage, (Item) newtup, newsz, oldoff,
41@@ -241,6 +243,9 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
42
43 revmapbuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
44
45+ ptrack_add_block(idxrel, BufferGetBlockNumber(newbuf));
46+ ptrack_add_block(idxrel, BufferGetBlockNumber(oldbuf));
47+ ptrack_add_block(idxrel, BufferGetBlockNumber(revmapbuf));
48 START_CRIT_SECTION();
49
50 /*
51@@ -410,6 +415,8 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange,
52 blk = BufferGetBlockNumber(*buffer);
53
54 /* Execute the actual insertion */
55+ ptrack_add_block(idxrel, BufferGetBlockNumber(*buffer));
56+ ptrack_add_block(idxrel, BufferGetBlockNumber(revmapbuf));
57 START_CRIT_SECTION();
58 if (extended)
59 brin_page_init(BufferGetPage(*buffer), BRIN_PAGETYPE_REGULAR);
60@@ -861,6 +868,7 @@ brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer)
61 "brin_initialize_empty_new_buffer: initializing blank page %u",
62 BufferGetBlockNumber(buffer)));
63
64+ ptrack_add_block(idxrel, BufferGetBlockNumber(buffer));
65 START_CRIT_SECTION();
66 page = BufferGetPage(buffer);
67 brin_page_init(page, BRIN_PAGETYPE_REGULAR);
68diff --git a/src/backend/access/brin/brin_revmap.c b/src/backend/access/brin/brin_revmap.c
69index 853181b..465b752 100644
70--- a/src/backend/access/brin/brin_revmap.c
71+++ b/src/backend/access/brin/brin_revmap.c
72@@ -27,6 +27,7 @@
73 #include "access/brin_xlog.h"
74 #include "access/rmgr.h"
75 #include "access/xloginsert.h"
76+#include "access/ptrack.h"
77 #include "miscadmin.h"
78 #include "storage/bufmgr.h"
79 #include "storage/lmgr.h"
80@@ -475,6 +476,8 @@ revmap_physical_extend(BrinRevmap *revmap)
81 * Ok, we have now locked the metapage and the target block. Re-initialize
82 * it as a revmap page.
83 */
84+ ptrack_add_block(irel, BufferGetBlockNumber(buf));
85+ ptrack_add_block(irel, BufferGetBlockNumber(revmap->rm_metaBuf));
86 START_CRIT_SECTION();
87
88 /* the rm_tids array is initialized to all invalid by PageInit */
89diff --git a/src/backend/access/brin/brin_xlog.c b/src/backend/access/brin/brin_xlog.c
90index 27ba0a9..1a61a0b 100644
91--- a/src/backend/access/brin/brin_xlog.c
92+++ b/src/backend/access/brin/brin_xlog.c
93@@ -14,6 +14,7 @@
94 #include "access/brin_pageops.h"
95 #include "access/brin_xlog.h"
96 #include "access/xlogutils.h"
97+#include "access/ptrack.h"
98
99
100 /*
101@@ -26,6 +27,11 @@ brin_xlog_createidx(XLogReaderState *record)
102 xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record);
103 Buffer buf;
104 Page page;
105+ RelFileNode rnode;
106+ BlockNumber blkno;
107+
108+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
109+ ptrack_add_block_redo(rnode, blkno);
110
111 /* create the index' metapage */
112 buf = XLogInitBufferForRedo(record, 0);
113@@ -50,6 +56,13 @@ brin_xlog_insert_update(XLogReaderState *record,
114 BlockNumber regpgno;
115 Page page;
116 XLogRedoAction action;
117+ RelFileNode rnode;
118+ BlockNumber blkno;
119+
120+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
121+ ptrack_add_block_redo(rnode, blkno);
122+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
123+ ptrack_add_block_redo(rnode, blkno);
124
125 /*
126 * If we inserted the first and only tuple on the page, re-initialize the
127@@ -137,9 +150,15 @@ brin_xlog_update(XLogReaderState *record)
128 xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record);
129 Buffer buffer;
130 XLogRedoAction action;
131+ RelFileNode rnode;
132+ BlockNumber blkno;
133+
134+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
135+ ptrack_add_block_redo(rnode, blkno);
136
137 /* First remove the old tuple */
138 action = XLogReadBufferForRedo(record, 2, &buffer);
139+
140 if (action == BLK_NEEDS_REDO)
141 {
142 Page page;
143@@ -174,9 +193,15 @@ brin_xlog_samepage_update(XLogReaderState *record)
144 xl_brin_samepage_update *xlrec;
145 Buffer buffer;
146 XLogRedoAction action;
147+ RelFileNode rnode;
148+ BlockNumber blkno;
149+
150+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
151+ ptrack_add_block_redo(rnode, blkno);
152
153 xlrec = (xl_brin_samepage_update *) XLogRecGetData(record);
154 action = XLogReadBufferForRedo(record, 0, &buffer);
155+
156 if (action == BLK_NEEDS_REDO)
157 {
158 Size tuplen;
159@@ -220,14 +245,21 @@ brin_xlog_revmap_extend(XLogReaderState *record)
160 Page page;
161 BlockNumber targetBlk;
162 XLogRedoAction action;
163+ RelFileNode rnode;
164+ BlockNumber blkno;
165
166 xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
167
168- XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
169+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
170+ ptrack_add_block_redo(rnode, blkno);
171+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &targetBlk);
172+ ptrack_add_block_redo(rnode, targetBlk);
173+
174 Assert(xlrec->targetBlk == targetBlk);
175
176 /* Update the metapage */
177 action = XLogReadBufferForRedo(record, 0, &metabuf);
178+
179 if (action == BLK_NEEDS_REDO)
180 {
181 Page metapg;
182diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
183index a0afec4..9513422 100644
184--- a/src/backend/access/gin/ginbtree.c
185+++ b/src/backend/access/gin/ginbtree.c
186@@ -16,6 +16,7 @@
187
188 #include "access/gin_private.h"
189 #include "access/xloginsert.h"
190+#include "access/ptrack.h"
191 #include "miscadmin.h"
192 #include "utils/memutils.h"
193 #include "utils/rel.h"
194@@ -385,6 +386,9 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
195 else if (rc == GPTP_INSERT)
196 {
197 /* It will fit, perform the insertion */
198+ ptrack_add_block(btree->index, BufferGetBlockNumber(stack->buffer));
199+ if (BufferIsValid(childbuf))
200+ ptrack_add_block(btree->index, BufferGetBlockNumber(childbuf));
201 START_CRIT_SECTION();
202
203 if (RelationNeedsWAL(btree->index))
204@@ -534,6 +538,12 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
205 * the new contents of the root.
206 */
207
208+ ptrack_add_block(btree->index, BufferGetBlockNumber(rbuffer));
209+ ptrack_add_block(btree->index, BufferGetBlockNumber(stack->buffer));
210+ if (stack->parent == NULL)
211+ ptrack_add_block(btree->index, BufferGetBlockNumber(lbuffer));
212+ if (BufferIsValid(childbuf))
213+ ptrack_add_block(btree->index, BufferGetBlockNumber(childbuf));
214 START_CRIT_SECTION();
215
216 MarkBufferDirty(rbuffer);
217diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
218index 276376a..a6cf119 100644
219--- a/src/backend/access/gin/gindatapage.c
220+++ b/src/backend/access/gin/gindatapage.c
221@@ -16,6 +16,7 @@
222
223 #include "access/gin_private.h"
224 #include "access/xloginsert.h"
225+#include "access/ptrack.h"
226 #include "lib/ilist.h"
227 #include "miscadmin.h"
228 #include "utils/rel.h"
229@@ -834,6 +835,7 @@ ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
230 computeLeafRecompressWALData(leaf);
231
232 /* Apply changes to page */
233+ ptrack_add_block(indexrel, BufferGetBlockNumber(buffer));
234 START_CRIT_SECTION();
235
236 dataPlaceToPageLeafRecompress(buffer, leaf);
237@@ -1809,6 +1811,7 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
238 page = BufferGetPage(buffer);
239 blkno = BufferGetBlockNumber(buffer);
240
241+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
242 START_CRIT_SECTION();
243
244 PageRestoreTempPage(tmppage, page);
245diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
246index 56a3ff5..d0af8da 100644
247--- a/src/backend/access/gin/ginfast.c
248+++ b/src/backend/access/gin/ginfast.c
249@@ -20,6 +20,7 @@
250
251 #include "access/gin_private.h"
252 #include "access/xloginsert.h"
253+#include "access/ptrack.h"
254 #include "access/xlog.h"
255 #include "commands/vacuum.h"
256 #include "catalog/pg_am.h"
257@@ -67,6 +68,7 @@ writeListPage(Relation index, Buffer buffer,
258 /* workspace could be a local array; we use palloc for alignment */
259 workspace = palloc(BLCKSZ);
260
261+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
262 START_CRIT_SECTION();
263
264 GinInitBuffer(buffer, GIN_LIST);
265@@ -293,6 +295,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
266 /*
267 * Main list is empty, so just insert sublist as main list
268 */
269+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
270 START_CRIT_SECTION();
271
272 metadata->head = sublist.head;
273@@ -316,6 +319,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
274
275 Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber);
276
277+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
278+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
279 START_CRIT_SECTION();
280
281 GinPageGetOpaque(page)->rightlink = sublist.head;
282@@ -358,6 +363,8 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
283 if (needWal)
284 XLogBeginInsert();
285
286+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
287+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
288 START_CRIT_SECTION();
289
290 /*
291@@ -557,6 +564,10 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
292 if (RelationNeedsWAL(index))
293 XLogEnsureRecordSpace(data.ndeleted, 0);
294
295+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
296+ for (i = 0; i < data.ndeleted; i++)
297+ ptrack_add_block(index, BufferGetBlockNumber(buffers[i]));
298+
299 START_CRIT_SECTION();
300
301 metadata->head = blknoToDelete;
302diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
303index 4e09f76..49171d4 100644
304--- a/src/backend/access/gin/gininsert.c
305+++ b/src/backend/access/gin/gininsert.c
306@@ -16,6 +16,7 @@
307
308 #include "access/gin_private.h"
309 #include "access/xloginsert.h"
310+#include "access/ptrack.h"
311 #include "catalog/index.h"
312 #include "miscadmin.h"
313 #include "storage/bufmgr.h"
314@@ -335,6 +336,8 @@ ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
315 /* initialize the root page */
316 RootBuffer = GinNewBuffer(index);
317
318+ ptrack_add_block(index, BufferGetBlockNumber(MetaBuffer));
319+ ptrack_add_block(index, BufferGetBlockNumber(RootBuffer));
320 START_CRIT_SECTION();
321 GinInitMetabuffer(MetaBuffer);
322 MarkBufferDirty(MetaBuffer);
323@@ -443,6 +446,8 @@ ginbuildempty(Relation index)
324 LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE);
325
326 /* Initialize and xlog metabuffer and root buffer. */
327+ ptrack_add_block(index, BufferGetBlockNumber(MetaBuffer));
328+ ptrack_add_block(index, BufferGetBlockNumber(RootBuffer));
329 START_CRIT_SECTION();
330 GinInitMetabuffer(MetaBuffer);
331 MarkBufferDirty(MetaBuffer);
332diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
333index d914648..f2f4fee 100644
334--- a/src/backend/access/gin/ginutil.c
335+++ b/src/backend/access/gin/ginutil.c
336@@ -17,6 +17,7 @@
337 #include "access/gin_private.h"
338 #include "access/reloptions.h"
339 #include "access/xloginsert.h"
340+#include "access/ptrack.h"
341 #include "catalog/pg_collation.h"
342 #include "catalog/pg_type.h"
343 #include "miscadmin.h"
344@@ -636,6 +637,7 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
345 metapage = BufferGetPage(metabuffer);
346 metadata = GinPageGetMeta(metapage);
347
348+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
349 START_CRIT_SECTION();
350
351 metadata->nTotalPages = stats->nTotalPages;
352diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
353index 3d23ceb..28e01fa 100644
354--- a/src/backend/access/gin/ginvacuum.c
355+++ b/src/backend/access/gin/ginvacuum.c
356@@ -16,6 +16,7 @@
357
358 #include "access/gin_private.h"
359 #include "access/xloginsert.h"
360+#include "access/ptrack.h"
361 #include "commands/vacuum.h"
362 #include "miscadmin.h"
363 #include "postmaster/autovacuum.h"
364@@ -209,6 +210,9 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
365 * LockBufferForCleanup() */
366 LockBuffer(pBuffer, GIN_EXCLUSIVE);
367
368+ ptrack_add_block(gvs->index, BufferGetBlockNumber(pBuffer));
369+ ptrack_add_block(gvs->index, BufferGetBlockNumber(lBuffer));
370+ ptrack_add_block(gvs->index, BufferGetBlockNumber(dBuffer));
371 START_CRIT_SECTION();
372
373 /* Unlink the page by changing left sibling's rightlink */
374@@ -603,6 +607,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
375
376 if (resPage)
377 {
378+ ptrack_add_block(gvs.index, BufferGetBlockNumber(buffer));
379 START_CRIT_SECTION();
380 PageRestoreTempPage(resPage, page);
381 MarkBufferDirty(buffer);
382diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
383index a40f168..11d63cd 100644
384--- a/src/backend/access/gin/ginxlog.c
385+++ b/src/backend/access/gin/ginxlog.c
386@@ -15,6 +15,7 @@
387
388 #include "access/gin_private.h"
389 #include "access/xlogutils.h"
390+#include "access/ptrack.h"
391 #include "utils/memutils.h"
392
393 static MemoryContext opCtx; /* working memory for operations */
394@@ -25,6 +26,11 @@ ginRedoClearIncompleteSplit(XLogReaderState *record, uint8 block_id)
395 XLogRecPtr lsn = record->EndRecPtr;
396 Buffer buffer;
397 Page page;
398+ RelFileNode rnode;
399+ BlockNumber blkno;
400+
401+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno);
402+ ptrack_add_block_redo(rnode, blkno);
403
404 if (XLogReadBufferForRedo(record, block_id, &buffer) == BLK_NEEDS_REDO)
405 {
406@@ -45,8 +51,12 @@ ginRedoCreateIndex(XLogReaderState *record)
407 Buffer RootBuffer,
408 MetaBuffer;
409 Page page;
410+ RelFileNode rnode;
411+
412+ XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
413
414 MetaBuffer = XLogInitBufferForRedo(record, 0);
415+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(MetaBuffer));
416 Assert(BufferGetBlockNumber(MetaBuffer) == GIN_METAPAGE_BLKNO);
417 page = (Page) BufferGetPage(MetaBuffer);
418
419@@ -56,6 +66,7 @@ ginRedoCreateIndex(XLogReaderState *record)
420 MarkBufferDirty(MetaBuffer);
421
422 RootBuffer = XLogInitBufferForRedo(record, 1);
423+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(RootBuffer));
424 Assert(BufferGetBlockNumber(RootBuffer) == GIN_ROOT_BLKNO);
425 page = (Page) BufferGetPage(RootBuffer);
426
427@@ -76,8 +87,13 @@ ginRedoCreatePTree(XLogReaderState *record)
428 char *ptr;
429 Buffer buffer;
430 Page page;
431+ RelFileNode rnode;
432+
433+ XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
434
435 buffer = XLogInitBufferForRedo(record, 0);
436+ ptrack_add_block_redo(rnode, BufferGetBlockNumber(buffer));
437+
438 page = (Page) BufferGetPage(buffer);
439
440 GinInitBuffer(buffer, GIN_DATA | GIN_LEAF | GIN_COMPRESSED);
441@@ -329,6 +345,11 @@ ginRedoInsert(XLogReaderState *record)
442 #endif
443 BlockNumber rightChildBlkno = InvalidBlockNumber;
444 bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
445+ RelFileNode rnode;
446+ BlockNumber blkno;
447+
448+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
449+ ptrack_add_block_redo(rnode, blkno);
450
451 /*
452 * First clear incomplete-split flag on child page if this finishes a
453@@ -382,6 +403,18 @@ ginRedoSplit(XLogReaderState *record)
454 rootbuf;
455 bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
456 bool isRoot = (data->flags & GIN_SPLIT_ROOT) != 0;
457+ RelFileNode rnode;
458+ BlockNumber blkno;
459+
460+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
461+ ptrack_add_block_redo(rnode, blkno);
462+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
463+ ptrack_add_block_redo(rnode, blkno);
464+ if (isRoot)
465+ {
466+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
467+ ptrack_add_block_redo(rnode, blkno);
468+ }
469
470 /*
471 * First clear incomplete-split flag on child page if this finishes a
472@@ -415,6 +448,11 @@ static void
473 ginRedoVacuumPage(XLogReaderState *record)
474 {
475 Buffer buffer;
476+ RelFileNode rnode;
477+ BlockNumber blkno;
478+
479+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
480+ ptrack_add_block_redo(rnode, blkno);
481
482 if (XLogReadBufferForRedo(record, 0, &buffer) != BLK_RESTORED)
483 {
484@@ -428,6 +466,11 @@ ginRedoVacuumDataLeafPage(XLogReaderState *record)
485 {
486 XLogRecPtr lsn = record->EndRecPtr;
487 Buffer buffer;
488+ RelFileNode rnode;
489+ BlockNumber blkno;
490+
491+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
492+ ptrack_add_block_redo(rnode, blkno);
493
494 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
495 {
496@@ -457,6 +500,15 @@ ginRedoDeletePage(XLogReaderState *record)
497 Buffer pbuffer;
498 Buffer lbuffer;
499 Page page;
500+ RelFileNode rnode;
501+ BlockNumber blkno;
502+
503+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
504+ ptrack_add_block_redo(rnode, blkno);
505+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
506+ ptrack_add_block_redo(rnode, blkno);
507+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
508+ ptrack_add_block_redo(rnode, blkno);
509
510 if (XLogReadBufferForRedo(record, 0, &dbuffer) == BLK_NEEDS_REDO)
511 {
512@@ -502,6 +554,13 @@ ginRedoUpdateMetapage(XLogReaderState *record)
513 Buffer metabuffer;
514 Page metapage;
515 Buffer buffer;
516+ RelFileNode rnode;
517+ BlockNumber blkno;
518+
519+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
520+ ptrack_add_block_redo(rnode, blkno);
521+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
522+ ptrack_add_block_redo(rnode, blkno);
523
524 /*
525 * Restore the metapage. This is essentially the same as a full-page
526@@ -600,6 +659,11 @@ ginRedoInsertListPage(XLogReaderState *record)
527 char *payload;
528 IndexTuple tuples;
529 Size totaltupsize;
530+ RelFileNode rnode;
531+ BlockNumber blkno;
532+
533+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
534+ ptrack_add_block_redo(rnode, blkno);
535
536 /* We always re-initialize the page. */
537 buffer = XLogInitBufferForRedo(record, 0);
538@@ -649,6 +713,11 @@ ginRedoDeleteListPages(XLogReaderState *record)
539 Buffer metabuffer;
540 Page metapage;
541 int i;
542+ RelFileNode rnode;
543+ BlockNumber blkno;
544+
545+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
546+ ptrack_add_block_redo(rnode, blkno);
547
548 metabuffer = XLogInitBufferForRedo(record, 0);
549 Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO);
550@@ -680,6 +749,9 @@ ginRedoDeleteListPages(XLogReaderState *record)
551 Buffer buffer;
552 Page page;
553
554+ XLogRecGetBlockTag(record, i+1, &rnode, NULL, &blkno);
555+ ptrack_add_block_redo(rnode, blkno);
556+
557 buffer = XLogInitBufferForRedo(record, i + 1);
558 page = BufferGetPage(buffer);
559 GinInitBuffer(buffer, GIN_DELETED);
560diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
561index 891ad38..b879360 100644
562--- a/src/backend/access/gist/gist.c
563+++ b/src/backend/access/gist/gist.c
564@@ -16,6 +16,7 @@
565
566 #include "access/gist_private.h"
567 #include "access/gistscan.h"
568+#include "access/ptrack.h"
569 #include "catalog/pg_collation.h"
570 #include "miscadmin.h"
571 #include "utils/index_selfuncs.h"
572@@ -121,6 +122,7 @@ gistbuildempty(Relation index)
573 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
574
575 /* Initialize and xlog buffer */
576+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
577 START_CRIT_SECTION();
578 GISTInitBuffer(buffer, F_LEAF);
579 MarkBufferDirty(buffer);
580@@ -446,6 +448,10 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
581 if (RelationNeedsWAL(rel))
582 XLogEnsureRecordSpace(npage, 1 + npage * 2);
583
584+ for (ptr = dist; ptr; ptr = ptr->next)
585+ ptrack_add_block(rel, BufferGetBlockNumber(ptr->buffer));
586+ if (BufferIsValid(leftchildbuf))
587+ ptrack_add_block(rel, BufferGetBlockNumber(leftchildbuf));
588 START_CRIT_SECTION();
589
590 /*
591@@ -495,6 +501,9 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
592 /*
593 * Enough space. We also get here if ntuples==0.
594 */
595+ ptrack_add_block(rel, BufferGetBlockNumber(buffer));
596+ if (BufferIsValid(leftchildbuf))
597+ ptrack_add_block(rel, BufferGetBlockNumber(leftchildbuf));
598 START_CRIT_SECTION();
599
600 /*
601@@ -1519,6 +1528,7 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
602
603 if (ndeletable > 0)
604 {
605+ ptrack_add_block(rel, BufferGetBlockNumber(buffer));
606 START_CRIT_SECTION();
607
608 PageIndexMultiDelete(page, deletable, ndeletable);
609diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
610index 4e43a69..66de58b 100644
611--- a/src/backend/access/gist/gistbuild.c
612+++ b/src/backend/access/gist/gistbuild.c
613@@ -19,6 +19,7 @@
614 #include "access/genam.h"
615 #include "access/gist_private.h"
616 #include "access/xloginsert.h"
617+#include "access/ptrack.h"
618 #include "catalog/index.h"
619 #include "miscadmin.h"
620 #include "optimizer/cost.h"
621@@ -171,6 +172,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
622 Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO);
623 page = BufferGetPage(buffer);
624
625+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
626 START_CRIT_SECTION();
627
628 GISTInitBuffer(buffer, F_LEAF);
629diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
630index a636988..b2904a9 100644
631--- a/src/backend/access/gist/gistvacuum.c
632+++ b/src/backend/access/gist/gistvacuum.c
633@@ -16,6 +16,7 @@
634
635 #include "access/genam.h"
636 #include "access/gist_private.h"
637+#include "access/ptrack.h"
638 #include "commands/vacuum.h"
639 #include "miscadmin.h"
640 #include "storage/indexfsm.h"
641@@ -212,6 +213,7 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
642
643 if (ntodelete)
644 {
645+ ptrack_add_block(rel, BufferGetBlockNumber(buffer));
646 START_CRIT_SECTION();
647
648 MarkBufferDirty(buffer);
649diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
650index 01c7ef7..1dd2ed7 100644
651--- a/src/backend/access/gist/gistxlog.c
652+++ b/src/backend/access/gist/gistxlog.c
653@@ -16,6 +16,7 @@
654 #include "access/gist_private.h"
655 #include "access/xloginsert.h"
656 #include "access/xlogutils.h"
657+#include "access/ptrack.h"
658 #include "utils/memutils.h"
659
660 static MemoryContext opCtx; /* working memory for operations */
661@@ -38,6 +39,11 @@ gistRedoClearFollowRight(XLogReaderState *record, uint8 block_id)
662 Buffer buffer;
663 Page page;
664 XLogRedoAction action;
665+ RelFileNode rnode;
666+ BlockNumber blkno;
667+
668+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno);
669+ ptrack_add_block_redo(rnode, blkno);
670
671 /*
672 * Note that we still update the page even if it was restored from a full
673@@ -68,6 +74,11 @@ gistRedoPageUpdateRecord(XLogReaderState *record)
674 gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record);
675 Buffer buffer;
676 Page page;
677+ RelFileNode rnode;
678+ BlockNumber blkno;
679+
680+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
681+ ptrack_add_block_redo(rnode, blkno);
682
683 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
684 {
685@@ -173,6 +184,7 @@ gistRedoPageSplitRecord(XLogReaderState *record)
686 int i;
687 bool isrootsplit = false;
688
689+
690 /*
691 * We must hold lock on the first-listed page throughout the action,
692 * including while updating the left child page (if any). We can unlock
693@@ -190,8 +202,11 @@ gistRedoPageSplitRecord(XLogReaderState *record)
694 int num;
695 BlockNumber blkno;
696 IndexTuple *tuples;
697+ RelFileNode rnode;
698+
699+ XLogRecGetBlockTag(record, i + 1, &rnode, NULL, &blkno);
700+ ptrack_add_block_redo(rnode, blkno);
701
702- XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno);
703 if (blkno == GIST_ROOT_BLKNO)
704 {
705 Assert(i == 0);
706@@ -262,6 +277,11 @@ gistRedoCreateIndex(XLogReaderState *record)
707 XLogRecPtr lsn = record->EndRecPtr;
708 Buffer buffer;
709 Page page;
710+ RelFileNode rnode;
711+ BlockNumber blkno;
712+
713+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
714+ ptrack_add_block_redo(rnode, blkno);
715
716 buffer = XLogInitBufferForRedo(record, 0);
717 Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO);
718diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
719index b83d496..788c55c 100644
720--- a/src/backend/access/heap/Makefile
721+++ b/src/backend/access/heap/Makefile
722@@ -12,6 +12,6 @@ subdir = src/backend/access/heap
723 top_builddir = ../../../..
724 include $(top_builddir)/src/Makefile.global
725
726-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
727+OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o ptrack.o
728
729 include $(top_srcdir)/src/backend/common.mk
730diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
731index 20dc810..e16f742 100644
732--- a/src/backend/access/heap/heapam.c
733+++ b/src/backend/access/heap/heapam.c
734@@ -53,6 +53,7 @@
735 #include "access/xlog.h"
736 #include "access/xloginsert.h"
737 #include "access/xlogutils.h"
738+#include "access/ptrack.h"
739 #include "catalog/catalog.h"
740 #include "catalog/namespace.h"
741 #include "miscadmin.h"
742@@ -2412,6 +2413,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
743 CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
744
745 /* NO EREPORT(ERROR) from here till changes are logged */
746+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
747 START_CRIT_SECTION();
748
749 RelationPutHeapTuple(relation, buffer, heaptup,
750@@ -2707,6 +2709,7 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
751 page = BufferGetPage(buffer);
752
753 /* NO EREPORT(ERROR) from here till changes are logged */
754+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
755 START_CRIT_SECTION();
756
757 /*
758@@ -3223,6 +3226,7 @@ l1:
759 xid, LockTupleExclusive, true,
760 &new_xmax, &new_infomask, &new_infomask2);
761
762+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
763 START_CRIT_SECTION();
764
765 /*
766@@ -3955,6 +3959,7 @@ l2:
767
768 Assert(HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple));
769
770+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
771 START_CRIT_SECTION();
772
773 /* Clear obsolete visibility flags ... */
774@@ -4126,6 +4131,9 @@ l2:
775 old_key_tuple = ExtractReplicaIdentity(relation, &oldtup, !satisfies_id, &old_key_copied);
776
777 /* NO EREPORT(ERROR) from here till changes are logged */
778+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
779+ if (newbuf != buffer)
780+ ptrack_add_block(relation, BufferGetBlockNumber(newbuf));
781 START_CRIT_SECTION();
782
783 /*
784@@ -5118,6 +5126,7 @@ failed:
785 GetCurrentTransactionId(), mode, false,
786 &xid, &new_infomask, &new_infomask2);
787
788+ ptrack_add_block(relation, BufferGetBlockNumber(*buffer));
789 START_CRIT_SECTION();
790
791 /*
792@@ -5891,6 +5900,7 @@ l4:
793 VISIBILITYMAP_ALL_FROZEN))
794 cleared_all_frozen = true;
795
796+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
797 START_CRIT_SECTION();
798
799 /* ... and set them */
800@@ -6046,6 +6056,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
801 "invalid speculative token constant");
802
803 /* NO EREPORT(ERROR) from here till changes are logged */
804+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
805 START_CRIT_SECTION();
806
807 Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
808@@ -6159,6 +6170,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
809 * do anything special with infomask bits.
810 */
811
812+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
813 START_CRIT_SECTION();
814
815 /*
816@@ -6292,6 +6304,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
817 elog(ERROR, "wrong tuple length");
818
819 /* NO EREPORT(ERROR) from here till changes are logged */
820+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
821 START_CRIT_SECTION();
822
823 memcpy((char *) htup + htup->t_hoff,
824@@ -8043,6 +8056,7 @@ heap_xlog_clean(XLogReaderState *record)
825 XLogRedoAction action;
826
827 XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
828+ ptrack_add_block_redo(rnode, blkno);
829
830 /*
831 * We're about to remove tuples. In Hot Standby mode, ensure that there's
832@@ -8134,6 +8148,7 @@ heap_xlog_visible(XLogReaderState *record)
833 XLogRedoAction action;
834
835 XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
836+ ptrack_add_block_redo(rnode, blkno);
837
838 /*
839 * If there are any Hot Standby transactions running that have an xmin
840@@ -8244,6 +8259,11 @@ heap_xlog_freeze_page(XLogReaderState *record)
841 TransactionId cutoff_xid = xlrec->cutoff_xid;
842 Buffer buffer;
843 int ntup;
844+ RelFileNode rnode;
845+ BlockNumber blkno;
846+
847+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
848+ ptrack_add_block_redo(rnode, blkno);
849
850 /*
851 * In Hot Standby mode, ensure that there's no queries running which still
852@@ -8329,6 +8349,7 @@ heap_xlog_delete(XLogReaderState *record)
853 ItemPointerData target_tid;
854
855 XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
856+ ptrack_add_block_redo(target_node, blkno);
857 ItemPointerSetBlockNumber(&target_tid, blkno);
858 ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
859
860@@ -8407,6 +8428,7 @@ heap_xlog_insert(XLogReaderState *record)
861 XLogRedoAction action;
862
863 XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
864+ ptrack_add_block_redo(target_node, blkno);
865 ItemPointerSetBlockNumber(&target_tid, blkno);
866 ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
867
868@@ -8529,6 +8551,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
869 xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
870
871 XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
872+ ptrack_add_block_redo(rnode, blkno);
873
874 /*
875 * The visibility map may need to be fixed even if the heap page is
876@@ -8675,8 +8698,10 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
877 oldtup.t_len = 0;
878
879 XLogRecGetBlockTag(record, 0, &rnode, NULL, &newblk);
880+ ptrack_add_block_redo(rnode, newblk);
881 if (XLogRecGetBlockTag(record, 1, NULL, NULL, &oldblk))
882 {
883+ ptrack_add_block_redo(rnode, oldblk);
884 /* HOT updates are never done across pages */
885 Assert(!hot_update);
886 }
887@@ -8922,6 +8947,11 @@ heap_xlog_confirm(XLogReaderState *record)
888 OffsetNumber offnum;
889 ItemId lp = NULL;
890 HeapTupleHeader htup;
891+ RelFileNode rnode;
892+ BlockNumber blkno;
893+
894+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
895+ ptrack_add_block_redo(rnode, blkno);
896
897 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
898 {
899@@ -8958,6 +8988,11 @@ heap_xlog_lock(XLogReaderState *record)
900 OffsetNumber offnum;
901 ItemId lp = NULL;
902 HeapTupleHeader htup;
903+ RelFileNode rnode;
904+ BlockNumber blkno;
905+
906+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
907+ ptrack_add_block_redo(rnode, blkno);
908
909 /*
910 * The visibility map may need to be fixed even if the heap page is
911@@ -9029,6 +9064,11 @@ heap_xlog_lock_updated(XLogReaderState *record)
912 OffsetNumber offnum;
913 ItemId lp = NULL;
914 HeapTupleHeader htup;
915+ RelFileNode rnode;
916+ BlockNumber blkno;
917+
918+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
919+ ptrack_add_block_redo(rnode, blkno);
920
921 xlrec = (xl_heap_lock_updated *) XLogRecGetData(record);
922
923@@ -9091,6 +9131,11 @@ heap_xlog_inplace(XLogReaderState *record)
924 HeapTupleHeader htup;
925 uint32 oldlen;
926 Size newlen;
927+ RelFileNode rnode;
928+ BlockNumber blkno;
929+
930+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
931+ ptrack_add_block_redo(rnode, blkno);
932
933 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
934 {
935diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
936index 6ff9251..0e30ef3 100644
937--- a/src/backend/access/heap/pruneheap.c
938+++ b/src/backend/access/heap/pruneheap.c
939@@ -19,6 +19,7 @@
940 #include "access/transam.h"
941 #include "access/htup_details.h"
942 #include "access/xlog.h"
943+#include "access/ptrack.h"
944 #include "catalog/catalog.h"
945 #include "miscadmin.h"
946 #include "pgstat.h"
947@@ -228,6 +229,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin,
948 }
949
950 /* Any error while applying the changes is critical */
951+ ptrack_add_block(relation, BufferGetBlockNumber(buffer));
952 START_CRIT_SECTION();
953
954 /* Have we found any prunable items? */
955diff --git a/src/backend/access/heap/ptrack.c b/src/backend/access/heap/ptrack.c
956new file mode 100644
957index 0000000..fad750b
958--- /dev/null
959+++ b/src/backend/access/heap/ptrack.c
960@@ -0,0 +1,785 @@
961+/*-------------------------------------------------------------------------
962+ *
963+ * ptrack.c
964+ * bitmap for tracking updates of relation's pages
965+ *
966+ * IDENTIFICATION
967+ * src/backend/access/heap/ptrack.c
968+ *
969+ * INTERFACE ROUTINES (PostgreSQL side)
970+ *
971+ * ptrack_add_block - set a bit to track dirtied page
972+ * ptrack_add_block_redo - set a bit to track recovered page
973+ * create_ptrack_init_file - create PTRACK_INIT_FILE
974+ * in the given database directory
975+ *
976+ * EXTERNAL INTERFACE ROUTINES (Backup utility side)
977+ * pg_ptrack_version() - Returns PTRACK version currently in use.
978+ * pg_ptrack_control_lsn() - Gets LSN from ptrack_control file.
979+ * pg_ptrack_clear() - Resets bits in all PTRACK files.
980+ * This function must be called for each database in the cluster.
981+ * pg_ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid) -
982+ * Reads a PTRACK file for the given relation and resets it.
983+ * Returns the PTRACK content as bytea. It is essential to receive and clear the map
984+ * atomically in order to avoid losing PTRACK bits because of race conditions.
985+ * (Imagine that your backup tool reads the map, then some blocks of the relation are
986+ * updated and the ptrack bits are set, after that the backup tool cleans up the map
987+ * and resets ptrack_clear_lsn. So, we may lose some of the updates).
988+ * This function must be called for each database in the cluster.
989+ * pg_ptrack_init_get_and_clear(Oid db_oid, Oid tablespace_oid) -
990+ * Checks whether PTRACK_INIT_FILE exists in the given database and deletes it.
991+ * Returns true if the file was found. This function is analogous to
992+ * pg_ptrack_get_and_clear(), but it handles directory-level changes
993+ * (i.e. CREATE DATABASE, ALTER DATABASE SET TABLESPACE).
994+ * This function must be called for each database in the cluster.
995+ *
996+ */
997+
998+#include "postgres.h"
999+
1000+#include "access/heapam_xlog.h"
1001+#include "access/heapam.h"
1002+#include "access/ptrack.h"
1003+#include "access/xlog.h"
1004+#include "access/xlogutils.h"
1005+#include "access/skey.h"
1006+#include "access/genam.h"
1007+#include "access/generic_xlog.h"
1008+#include "catalog/pg_depend.h"
1009+#include "catalog/pg_tablespace.h"
1010+#include "access/htup_details.h"
1011+#include "miscadmin.h"
1012+#include "storage/bufmgr.h"
1013+#include "storage/lmgr.h"
1014+#include "storage/smgr.h"
1015+#include "utils/inval.h"
1016+#include "utils/array.h"
1017+#include "utils/relfilenodemap.h"
1018+#include "utils/builtins.h"
1019+#include "utils/pg_lsn.h"
1020+#include "utils/lsyscache.h"
1021+#include "nodes/makefuncs.h"
1022+#include <unistd.h>
1023+#include <sys/stat.h>
1024+
1025+/* Effective data size */
1026+#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
1027+
1028+/* Number of heap blocks we can represent in one byte. */
1029+#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / PTRACK_BITS_PER_HEAPBLOCK)
1030+
1031+/* Number of heap blocks we can represent in one ptrack map page. */
1032+#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
1033+
1034+/* Mapping from heap block number to the right bit in the ptrack map */
1035+#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
1036+#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
1037+/* NOTE If you're going to increase PTRACK_BITS_PER_HEAPBLOCK, update macro below */
1038+#define HEAPBLK_TO_MAPBIT(x) ((x) % HEAPBLOCKS_PER_BYTE)
1039+
1040+bool ptrack_enable = false;
1041+
1042+static Buffer ptrack_readbuf(Relation rel, BlockNumber blkno, bool extend);
1043+static void ptrack_extend(Relation rel, BlockNumber nvmblocks);
1044+static void ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf);
1045+
1046+void SetPtrackClearLSN(bool set_invalid);
1047+
1048+Datum pg_ptrack_clear(PG_FUNCTION_ARGS);
1049+Datum pg_ptrack_get_and_clear(PG_FUNCTION_ARGS);
1050+Datum pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS);
1051+Datum pg_ptrack_control_lsn(PG_FUNCTION_ARGS);
1052+
1053+static void drop_ptrack_init_file(char *dest_dir);
1054+
1055+/*
1056+ * Mark tracked memory block during recovery.
1057+ * We should not miss any recovery actions, including
1058+ * recovery from full-page writes.
1059+ */
1060+void
1061+ptrack_add_block_redo(RelFileNode rnode, BlockNumber heapBlk)
1062+{
1063+ Relation reln;
1064+ reln = CreateFakeRelcacheEntry(rnode);
1065+ ptrack_add_block(reln, heapBlk);
1066+ FreeFakeRelcacheEntry(reln);
1067+}
1068+
1069+/* Save tracked memory block inside critical zone */
1070+void
1071+ptrack_add_block(Relation rel, BlockNumber heapBlk)
1072+{
1073+ Buffer ptrackbuf = InvalidBuffer;
1074+
1075+ /*
1076+ * Do not track changes for unlogged and temp relations,
1077+ * since we are not going to backup them anyway.
1078+ */
1079+ if (rel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
1080+ return;
1081+
1082+ if (ptrack_enable)
1083+ {
1084+ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
1085+ ptrackbuf = ptrack_readbuf(rel, mapBlock, true);
1086+ ptrack_set(heapBlk, ptrackbuf);
1087+ ReleaseBuffer(ptrackbuf);
1088+ }
1089+}
1090+
1091+/* Set one bit to buffer */
1092+static void
1093+ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf)
1094+{
1095+ uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
1096+ uint8 mapOffset = HEAPBLK_TO_MAPBIT(heapBlk);
1097+ Page page;
1098+ char *map;
1099+
1100+ page = BufferGetPage(ptrackBuf);
1101+ map = PageGetContents(page);
1102+ LockBuffer(ptrackBuf, BUFFER_LOCK_SHARE);
1103+
1104+ /* Check if the bit already set */
1105+ if (!(map[mapByte] & (1 << mapOffset)))
1106+ {
1107+ /* Bad luck. Take an exclusive lock now after unlock share.*/
1108+ LockBuffer(ptrackBuf, BUFFER_LOCK_UNLOCK);
1109+ LockBuffer(ptrackBuf, BUFFER_LOCK_EXCLUSIVE);
1110+
1111+ /* The bit could have been set concurrently */
1112+ if (!(map[mapByte] & (1 << mapOffset)))
1113+ {
1114+ START_CRIT_SECTION();
1115+
1116+ map[mapByte] |= (1 << mapOffset);
1117+ MarkBufferDirty(ptrackBuf);
1118+
1119+ /*
1120+ * We don't have Xlog entry for ptrack, update pages
1121+ * on recovery instead.
1122+ */
1123+ END_CRIT_SECTION();
1124+ }
1125+ }
1126+
1127+ LockBuffer(ptrackBuf, BUFFER_LOCK_UNLOCK);
1128+}
1129+
1130+/*
1131+ * Read a ptrack map page.
1132+ *
1133+ * If the page doesn't exist, InvalidBuffer is returned, or if 'extend' is
1134+ * true, the ptrack map file is extended.
1135+ */
1136+static Buffer
1137+ptrack_readbuf(Relation rel, BlockNumber blkno, bool extend)
1138+{
1139+ Buffer buf;
1140+
1141+ /*
1142+ * We might not have opened the relation at the smgr level yet, or we
1143+ * might have been forced to close it by a sinval message. The code below
1144+ * won't necessarily notice relation extension immediately when extend =
1145+ * false, so we rely on sinval messages to ensure that our ideas about the
1146+ * size of the map aren't too far out of date.
1147+ */
1148+ RelationOpenSmgr(rel);
1149+
1150+ /*
1151+ * If we haven't cached the size of the ptrack map fork yet, check it
1152+ * first.
1153+ */
1154+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber)
1155+ {
1156+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM))
1157+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr,
1158+ PAGESTRACK_FORKNUM);
1159+ else
1160+ rel->rd_smgr->smgr_ptrack_nblocks = 0;
1161+ }
1162+
1163+ /* Handle requests beyond EOF */
1164+ if (blkno >= rel->rd_smgr->smgr_ptrack_nblocks)
1165+ {
1166+ if (extend)
1167+ ptrack_extend(rel, blkno + 1);
1168+ else
1169+ return InvalidBuffer;
1170+ }
1171+
1172+ /* We should never miss updated pages, so error out if page is corrupted */
1173+ buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, blkno,
1174+ RBM_NORMAL, NULL);
1175+
1176+ if (PageIsNew(BufferGetPage(buf)))
1177+ PageInit(BufferGetPage(buf), BLCKSZ, 0);
1178+
1179+ return buf;
1180+}
1181+
1182+/*
1183+ * Ensure that the ptrack map fork is at least ptrack_nblocks long, extending
1184+ * it if necessary with zeroed pages.
1185+ */
1186+static void
1187+ptrack_extend(Relation rel, BlockNumber ptrack_nblocks)
1188+{
1189+ BlockNumber ptrack_nblocks_now;
1190+ Page pg;
1191+
1192+ pg = (Page) palloc(BLCKSZ);
1193+ PageInit(pg, BLCKSZ, 0);
1194+
1195+ /*
1196+ * We use the relation extension lock to lock out other backends trying to
1197+ * extend the ptrack map at the same time. It also locks out extension
1198+ * of the main fork, unnecessarily, but extending the ptrack map
1199+ * happens seldom enough that it doesn't seem worthwhile to have a
1200+ * separate lock tag type for it.
1201+ *
1202+ * Note that another backend might have extended or created the relation
1203+ * by the time we get the lock.
1204+ */
1205+ LockRelationForExtension(rel, ExclusiveLock);
1206+
1207+ /* Might have to re-open if a cache flush happened */
1208+ RelationOpenSmgr(rel);
1209+
1210+ /*
1211+ * Create the file first if it doesn't exist. If smgr_ptrack_nblocks is
1212+ * positive then it must exist, no need for an smgrexists call.
1213+ */
1214+ if ((rel->rd_smgr->smgr_ptrack_nblocks == 0 ||
1215+ rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) &&
1216+ !smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM))
1217+ smgrcreate(rel->rd_smgr, PAGESTRACK_FORKNUM, false);
1218+
1219+ ptrack_nblocks_now = smgrnblocks(rel->rd_smgr, PAGESTRACK_FORKNUM);
1220+
1221+ /* Now extend the file */
1222+ while (ptrack_nblocks_now < ptrack_nblocks)
1223+ {
1224+ PageSetChecksumInplace(pg, ptrack_nblocks_now);
1225+
1226+ smgrextend(rel->rd_smgr, PAGESTRACK_FORKNUM, ptrack_nblocks_now,
1227+ (char *) pg, false);
1228+ ptrack_nblocks_now++;
1229+ }
1230+
1231+ /*
1232+ * Send a shared-inval message to force other backends to close any smgr
1233+ * references they may have for this rel, which we are about to change.
1234+ * This is a useful optimization because it means that backends don't have
1235+ * to keep checking for creation or extension of the file, which happens
1236+ * infrequently.
1237+ */
1238+ CacheInvalidateSmgr(rel->rd_smgr->smgr_rnode);
1239+
1240+ /* Update local cache with the up-to-date size */
1241+ rel->rd_smgr->smgr_ptrack_nblocks = ptrack_nblocks_now;
1242+
1243+ UnlockRelationForExtension(rel, ExclusiveLock);
1244+
1245+ pfree(pg);
1246+}
1247+
1248+
1249+
1250+/* Clear all blocks of relation's ptrack map */
1251+static void
1252+ptrack_clear_one_rel(Oid relid)
1253+{
1254+ BlockNumber nblock;
1255+ Relation rel = relation_open(relid, AccessShareLock);
1256+
1257+ RelationOpenSmgr(rel);
1258+
1259+ if (rel->rd_smgr == NULL)
1260+ {
1261+ relation_close(rel, AccessShareLock);
1262+ return;
1263+ }
1264+
1265+ LockRelationForExtension(rel, ExclusiveLock);
1266+
1267+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber)
1268+ {
1269+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM))
1270+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr,
1271+ PAGESTRACK_FORKNUM);
1272+ else
1273+ rel->rd_smgr->smgr_ptrack_nblocks = 0;
1274+ }
1275+
1276+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++)
1277+ {
1278+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM,
1279+ nblock, RBM_ZERO_ON_ERROR, NULL);
1280+ Page page = BufferGetPage(buf);
1281+ char *map = PageGetContents(page);
1282+
1283+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
1284+
1285+ START_CRIT_SECTION();
1286+ MemSet(map, 0, MAPSIZE);
1287+ MarkBufferDirty(buf);
1288+ END_CRIT_SECTION();
1289+
1290+ UnlockReleaseBuffer(buf);
1291+ }
1292+
1293+ UnlockRelationForExtension(rel, ExclusiveLock);
1294+ relation_close(rel, AccessShareLock);
1295+ return;
1296+}
1297+
1298+/* Clear all ptrack files */
1299+void
1300+ptrack_clear(void)
1301+{
1302+ HeapTuple tuple;
1303+ Relation catalog = heap_open(RelationRelationId, AccessShareLock);
1304+ SysScanDesc scan = systable_beginscan(catalog, InvalidOid, false, NULL, 0, NULL);
1305+
1306+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1307+ {
1308+ ptrack_clear_one_rel(HeapTupleGetOid(tuple));
1309+ }
1310+
1311+ systable_endscan(scan);
1312+ heap_close(catalog, AccessShareLock);
1313+
1314+ /*
1315+ * Update ptrack_enabled_lsn to know
1316+ * that we track all changes since this LSN.
1317+ */
1318+ SetPtrackClearLSN(false);
1319+}
1320+
1321+/* TODO Rewiew and clean the code
1322+ * Get ptrack file as bytea and clear it */
1323+bytea *
1324+ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid)
1325+{
1326+ bytea *result = NULL;
1327+ BlockNumber nblock;
1328+ Relation rel = RelationIdGetRelation(RelidByRelfilenode(tablespace_oid, table_oid));
1329+
1330+ if (table_oid == InvalidOid)
1331+ {
1332+ elog(WARNING, "InvalidOid");
1333+ goto full_end;
1334+ }
1335+
1336+ if (rel == InvalidRelation)
1337+ {
1338+ elog(WARNING, "InvalidRelation");
1339+ goto full_end;
1340+ }
1341+
1342+ RelationOpenSmgr(rel);
1343+ if (rel->rd_smgr == NULL)
1344+ goto end_rel;
1345+
1346+ LockRelationForExtension(rel, ExclusiveLock);
1347+
1348+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber)
1349+ {
1350+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM))
1351+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr,
1352+ PAGESTRACK_FORKNUM);
1353+ else
1354+ rel->rd_smgr->smgr_ptrack_nblocks = 0;
1355+ }
1356+ if (rel->rd_smgr->smgr_ptrack_nblocks == 0)
1357+ {
1358+ UnlockRelationForExtension(rel, ExclusiveLock);
1359+ goto end_rel;
1360+ }
1361+ result = (bytea *) palloc(rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ);
1362+ SET_VARSIZE(result, rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ);
1363+
1364+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++)
1365+ {
1366+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM,
1367+ nblock, RBM_ZERO_ON_ERROR, NULL);
1368+ Page page = BufferGetPage(buf);
1369+ char *map = PageGetContents(page);
1370+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
1371+ START_CRIT_SECTION();
1372+ memcpy(VARDATA(result) + nblock*MAPSIZE, map, MAPSIZE);
1373+ MemSet(map, 0, MAPSIZE);
1374+ MarkBufferDirty(buf);
1375+ END_CRIT_SECTION();
1376+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
1377+ ReleaseBuffer(buf);
1378+ }
1379+
1380+ UnlockRelationForExtension(rel, ExclusiveLock);
1381+ end_rel:
1382+ RelationClose(rel);
1383+
1384+ /*
1385+ * Update ptrack_enabled_lsn to know
1386+ * that we track all changes since this LSN.
1387+ */
1388+ SetPtrackClearLSN(false);
1389+ full_end:
1390+ if (result == NULL)
1391+ {
1392+ result = palloc0(VARHDRSZ);
1393+ SET_VARSIZE(result, VARHDRSZ);
1394+ }
1395+
1396+ return result;
1397+}
1398+
1399+
1400+/*
1401+ * Reset LSN in ptrack_control file.
1402+ * If server started with ptrack_enable = off,
1403+ * set ptrack_enabled_lsn to InvalidXLogRecPtr,
1404+ * otherwise set it to current lsn.
1405+ *
1406+ * Also we update the value after ptrack_clear() call,
1407+ * to to know that we track all changes since this LSN.
1408+ *
1409+ * Judging by this value, we can say, if it's legal to perform incremental
1410+ * ptrack backup, or we had lost ptrack mapping since previous backup and
1411+ * must do full backup now.
1412+ */
1413+void
1414+SetPtrackClearLSN(bool set_invalid)
1415+{
1416+ int fd;
1417+ XLogRecPtr ptrack_enabled_lsn;
1418+ char file_path[MAXPGPATH];
1419+
1420+ ptrack_enabled_lsn = (set_invalid)?
1421+ InvalidXLogRecPtr : GetXLogInsertRecPtr();
1422+
1423+ join_path_components(file_path, DataDir, "global/ptrack_control");
1424+ canonicalize_path(file_path);
1425+
1426+ fd = BasicOpenFile(file_path,
1427+ O_RDWR | O_CREAT | PG_BINARY,
1428+ S_IRUSR | S_IWUSR);
1429+ if (fd < 0)
1430+ ereport(PANIC,
1431+ (errcode_for_file_access(),
1432+ errmsg("could not create ptrack control file \"%s\": %m",
1433+ "global/ptrack_control")));
1434+
1435+ errno = 0;
1436+ if (write(fd, &ptrack_enabled_lsn, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr))
1437+ {
1438+ /* if write didn't set errno, assume problem is no disk space */
1439+ if (errno == 0)
1440+ errno = ENOSPC;
1441+ ereport(PANIC,
1442+ (errcode_for_file_access(),
1443+ errmsg("could not write to ptrack control file: %m")));
1444+ }
1445+
1446+ if (pg_fsync(fd) != 0)
1447+ ereport(PANIC,
1448+ (errcode_for_file_access(),
1449+ errmsg("could not fsync ptrack control file: %m")));
1450+
1451+ if (close(fd))
1452+ ereport(PANIC,
1453+ (errcode_for_file_access(),
1454+ errmsg("could not close ptrack control file: %m")));
1455+}
1456+
1457+/*
1458+ * If we disabled ptrack_enable, reset ptrack_enabled_lsn in ptrack_control
1459+ * file, to know, that it's illegal to perform incremental ptrack backup.
1460+ */
1461+void
1462+assign_ptrack_enable(bool newval, void *extra)
1463+{
1464+ if(DataDir != NULL && !IsBootstrapProcessingMode() && !newval)
1465+ SetPtrackClearLSN(true);
1466+}
1467+
1468+/* Clear all ptrack files */
1469+Datum
1470+pg_ptrack_clear(PG_FUNCTION_ARGS)
1471+{
1472+ if (!superuser() && !has_rolreplication(GetUserId()))
1473+ ereport(ERROR,
1474+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1475+ (errmsg("must be superuser or replication role to clear ptrack files"))));
1476+
1477+ ptrack_clear();
1478+
1479+ PG_RETURN_VOID();
1480+}
1481+
1482+/* Read all ptrack files and clear them afterwards */
1483+Datum
1484+pg_ptrack_get_and_clear(PG_FUNCTION_ARGS)
1485+{
1486+ if (!superuser() && !has_rolreplication(GetUserId()))
1487+ ereport(ERROR,
1488+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1489+ (errmsg("must be superuser or replication role to clear ptrack files"))));
1490+
1491+ PG_RETURN_BYTEA_P(ptrack_get_and_clear(PG_GETARG_OID(0), PG_GETARG_OID(1)));
1492+}
1493+
1494+/*
1495+ * Check if PTRACK_INIT_FILE exits in the given database
1496+ * and delete it.
1497+ * Args: dbOid and tblspcOid
1498+ * Return true if file existed.
1499+ */
1500+Datum
1501+pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS)
1502+{
1503+ char *db_path = GetDatabasePath(PG_GETARG_OID(0), PG_GETARG_OID(1));
1504+ struct stat buf;
1505+ char ptrack_init_file_path[MAXPGPATH];
1506+
1507+ if (!superuser() && !has_rolreplication(GetUserId()))
1508+ ereport(ERROR,
1509+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1510+ (errmsg("must be superuser or replication role to clear ptrack files"))));
1511+
1512+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", db_path, PTRACK_INIT_FILE);
1513+
1514+ if (stat(ptrack_init_file_path, &buf) == -1 && errno == ENOENT)
1515+ PG_RETURN_BOOL(false);
1516+ else if (!S_ISREG(buf.st_mode))
1517+ PG_RETURN_BOOL(false);
1518+ else
1519+ {
1520+ drop_ptrack_init_file(db_path);
1521+ PG_RETURN_BOOL(true);
1522+ }
1523+}
1524+
1525+/*
1526+ * Create PTRACK_INIT_FILE which allows to track changes
1527+ * on directory level made by operations which do not go
1528+ * through Shared Buffers.
1529+ */
1530+void
1531+create_ptrack_init_file(char *dest_dir)
1532+{
1533+ int dstfd;
1534+
1535+ char ptrack_init_file_path[MAXPGPATH];
1536+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", dest_dir, PTRACK_INIT_FILE);
1537+
1538+ dstfd = OpenTransientFile(ptrack_init_file_path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
1539+ S_IRUSR | S_IWUSR);
1540+ if (dstfd < 0)
1541+ {
1542+ if (errno != EEXIST)
1543+ ereport(ERROR,
1544+ (errcode_for_file_access(),
1545+ errmsg("could not create file \"%s\": %m", ptrack_init_file_path)));
1546+ }
1547+ else if (CloseTransientFile(dstfd))
1548+ ereport(ERROR,
1549+ (errcode_for_file_access(),
1550+ errmsg("could not close file \"%s\": %m", ptrack_init_file_path)));
1551+}
1552+
1553+/* Delete PTRACK_INIT_FILE */
1554+void
1555+drop_ptrack_init_file(char *dest_dir)
1556+{
1557+ char ptrack_init_file_path[MAXPGPATH];
1558+ snprintf(ptrack_init_file_path, sizeof(ptrack_init_file_path), "%s/%s", dest_dir, PTRACK_INIT_FILE);
1559+
1560+ if (unlink(ptrack_init_file_path) != 0)
1561+ {
1562+ if (errno != ENOENT)
1563+ ereport(WARNING,
1564+ (errcode_for_file_access(),
1565+ errmsg("could not remove file \"%s\": %m", ptrack_init_file_path)));
1566+ }
1567+}
1568+
1569+/*
1570+ * Returns ptrack version currently in use.
1571+ */
1572+PG_FUNCTION_INFO_V1(ptrack_version);
1573+Datum
1574+ptrack_version(PG_FUNCTION_ARGS)
1575+{
1576+ PG_RETURN_TEXT_P(cstring_to_text(PTRACK_VERSION));
1577+}
1578+
1579+/* Get lsn from ptrack_control file */
1580+Datum
1581+pg_ptrack_control_lsn(PG_FUNCTION_ARGS)
1582+{
1583+ int fd;
1584+ char file_path[MAXPGPATH];
1585+ XLogRecPtr lsn = 0;
1586+
1587+ if (!superuser() && !has_rolreplication(GetUserId()))
1588+ ereport(ERROR,
1589+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1590+ (errmsg("must be superuser or replication role read ptrack files"))));
1591+ join_path_components(file_path, DataDir, "global/ptrack_control");
1592+ canonicalize_path(file_path);
1593+
1594+ fd = BasicOpenFile(file_path, O_RDONLY | PG_BINARY, 0);
1595+ if (fd < 0)
1596+ ereport(ERROR,
1597+ (errcode_for_file_access(),
1598+ errmsg("could not open file \"%s\" for reading: %m",
1599+ file_path)));
1600+
1601+ if (read(fd, &lsn, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr))
1602+ ereport(ERROR,
1603+ (errcode_for_file_access(),
1604+ errmsg("could not read content of the file \"%s\" %m",
1605+ file_path)));
1606+
1607+ close(fd);
1608+
1609+ PG_RETURN_LSN(lsn);
1610+}
1611+
1612+
1613+static RangeVar *
1614+makeRangeVarFromRelid(Oid relid)
1615+{
1616+ char *relname = get_rel_name(relid);
1617+ char *nspname = get_namespace_name(get_rel_namespace(relid));
1618+
1619+ if (relname == NULL)
1620+ elog(ERROR, "Invalid relation OID");
1621+
1622+ return makeRangeVar(nspname, relname, -1);
1623+}
1624+
1625+PG_FUNCTION_INFO_V1(pg_ptrack_get_block);
1626+
1627+Datum
1628+pg_ptrack_get_block(PG_FUNCTION_ARGS)
1629+{
1630+ Oid tablespace_oid = PG_GETARG_OID(0);
1631+ Oid relfilenode = PG_GETARG_OID(1);
1632+ uint32 blkno = PG_GETARG_UINT32(2);
1633+ bytea *raw_page;
1634+ RangeVar *relrv;
1635+ char *raw_page_data;
1636+ Buffer buf;
1637+ Oid relationOid = RelidByRelfilenode(tablespace_oid, relfilenode);
1638+ Relation rel;
1639+
1640+ if (relationOid == InvalidOid)
1641+ elog(ERROR, "InvalidOid");
1642+
1643+ elog(DEBUG1, "pg_ptrack_get_block(%i, %i, %u). relationOid %i",
1644+ tablespace_oid, relfilenode, blkno, relationOid);
1645+
1646+ rel = relation_open(relationOid, AccessShareLock);
1647+
1648+ if (rel == InvalidRelation)
1649+ elog(ERROR, "InvalidRelation");
1650+
1651+
1652+ /* Check that this relation has storage */
1653+ if (rel->rd_rel->relkind == RELKIND_VIEW)
1654+ ereport(ERROR,
1655+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1656+ errmsg("cannot get raw page from view \"%s\"",
1657+ RelationGetRelationName(rel))));
1658+ if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
1659+ ereport(ERROR,
1660+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1661+ errmsg("cannot get raw page from composite type \"%s\"",
1662+ RelationGetRelationName(rel))));
1663+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1664+ ereport(ERROR,
1665+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1666+ errmsg("cannot get raw page from foreign table \"%s\"",
1667+ RelationGetRelationName(rel))));
1668+
1669+ /*
1670+ * Reject attempts to read non-local temporary relations; we would be
1671+ * likely to get wrong data since we have no visibility into the owning
1672+ * session's local buffers.
1673+ */
1674+ if (RELATION_IS_OTHER_TEMP(rel))
1675+ ereport(ERROR,
1676+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1677+ errmsg("cannot access temporary tables of other sessions")));
1678+
1679+ /* Initialize buffer to copy to */
1680+ raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
1681+ SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
1682+ raw_page_data = VARDATA(raw_page);
1683+
1684+ /* Take a verbatim copy of the page */
1685+
1686+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, NULL);
1687+ LockBuffer(buf, BUFFER_LOCK_SHARE);
1688+
1689+ memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
1690+
1691+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
1692+ ReleaseBuffer(buf);
1693+
1694+ relation_close(rel, AccessShareLock);
1695+
1696+ PG_RETURN_BYTEA_P(raw_page);
1697+}
1698+
1699+PG_FUNCTION_INFO_V1(pg_ptrack_get_block_2);
1700+
1701+Datum
1702+pg_ptrack_get_block_2(PG_FUNCTION_ARGS)
1703+{
1704+ Oid tablespace_oid = PG_GETARG_OID(0);
1705+ Oid db_oid = PG_GETARG_OID(1);
1706+ Oid relfilenode = PG_GETARG_OID(2);
1707+ BlockNumber blkno = PG_GETARG_UINT32(3);
1708+ bytea *raw_page;
1709+ char *raw_page_data;
1710+ Buffer buf;
1711+ RelFileNode rnode;
1712+ SMgrRelation smgr;
1713+ BlockNumber nblocks;
1714+
1715+ rnode.dbNode = db_oid;
1716+ rnode.spcNode = tablespace_oid;
1717+ rnode.relNode = relfilenode;
1718+
1719+ elog(DEBUG1, "pg_ptrack_get_block_2(%i, %i, %i, %u)",
1720+ tablespace_oid, db_oid, relfilenode, blkno);
1721+ smgr = smgropen(rnode, InvalidBackendId);
1722+ nblocks = smgrnblocks(smgr, MAIN_FORKNUM);
1723+
1724+ if (blkno >= nblocks)
1725+ PG_RETURN_NULL();
1726+
1727+ /* Initialize buffer to copy to */
1728+ raw_page = (bytea *) palloc0(BLCKSZ + VARHDRSZ);
1729+ SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
1730+ raw_page_data = VARDATA(raw_page);
1731+
1732+ buf = ReadBufferWithoutRelcache(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL, NULL);
1733+
1734+ if (buf == InvalidBuffer)
1735+ elog(ERROR, "Block is not found in the buffer cache");
1736+
1737+ LockBuffer(buf, BUFFER_LOCK_SHARE);
1738+
1739+ memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
1740+
1741+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
1742+ ReleaseBuffer(buf);
1743+
1744+ PG_RETURN_BYTEA_P(raw_page);
1745+}
1746diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
1747index 170e77d..543495c 100644
1748--- a/src/backend/access/heap/rewriteheap.c
1749+++ b/src/backend/access/heap/rewriteheap.c
1750@@ -114,6 +114,7 @@
1751 #include "access/tuptoaster.h"
1752 #include "access/xact.h"
1753 #include "access/xloginsert.h"
1754+#include "access/ptrack.h"
1755
1756 #include "catalog/catalog.h"
1757
1758@@ -330,11 +331,15 @@ end_heap_rewrite(RewriteState state)
1759 if (state->rs_buffer_valid)
1760 {
1761 if (state->rs_use_wal)
1762+ {
1763+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */
1764+ ptrack_add_block(state->rs_new_rel, state->rs_blockno);
1765 log_newpage(&state->rs_new_rel->rd_node,
1766 MAIN_FORKNUM,
1767 state->rs_blockno,
1768 state->rs_buffer,
1769 true);
1770+ }
1771 RelationOpenSmgr(state->rs_new_rel);
1772
1773 PageSetChecksumInplace(state->rs_buffer, state->rs_blockno);
1774@@ -682,11 +687,15 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
1775
1776 /* XLOG stuff */
1777 if (state->rs_use_wal)
1778+ {
1779+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */
1780+ ptrack_add_block(state->rs_new_rel, state->rs_blockno);
1781 log_newpage(&state->rs_new_rel->rd_node,
1782 MAIN_FORKNUM,
1783 state->rs_blockno,
1784 page,
1785 true);
1786+ }
1787
1788 /*
1789 * Now write the page. We say isTemp = true even if it's not a
1790diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
1791index ef69290..9943666 100644
1792--- a/src/backend/access/nbtree/nbtinsert.c
1793+++ b/src/backend/access/nbtree/nbtinsert.c
1794@@ -19,6 +19,7 @@
1795 #include "access/nbtree.h"
1796 #include "access/transam.h"
1797 #include "access/xloginsert.h"
1798+#include "access/ptrack.h"
1799 #include "miscadmin.h"
1800 #include "storage/lmgr.h"
1801 #include "storage/predicate.h"
1802@@ -837,6 +838,11 @@ _bt_insertonpg(Relation rel,
1803 }
1804
1805 /* Do the update. No ereport(ERROR) until changes are logged */
1806+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
1807+ if (BufferIsValid(metabuf))
1808+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf));
1809+ if (BufferIsValid(cbuf))
1810+ ptrack_add_block(rel, BufferGetBlockNumber(cbuf));
1811 START_CRIT_SECTION();
1812
1813 if (!_bt_pgaddtup(page, itemsz, itup, newitemoff))
1814@@ -1223,6 +1229,12 @@ _bt_split(Relation rel, Buffer buf, Buffer cbuf, OffsetNumber firstright,
1815 * not starting the critical section till here because we haven't been
1816 * scribbling on the original page yet; see comments above.
1817 */
1818+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
1819+ ptrack_add_block(rel, BufferGetBlockNumber(rbuf));
1820+ if (!P_RIGHTMOST(ropaque))
1821+ ptrack_add_block(rel, BufferGetBlockNumber(sbuf));
1822+ if (BufferIsValid(cbuf))
1823+ ptrack_add_block(rel, BufferGetBlockNumber(cbuf));
1824 START_CRIT_SECTION();
1825
1826 /*
1827@@ -1975,6 +1987,9 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
1828 ItemPointerSet(&(right_item->t_tid), rbkno, P_HIKEY);
1829
1830 /* NO EREPORT(ERROR) from here till newroot op is logged */
1831+ ptrack_add_block(rel, BufferGetBlockNumber(lbuf));
1832+ ptrack_add_block(rel, BufferGetBlockNumber(rootbuf));
1833+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf));
1834 START_CRIT_SECTION();
1835
1836 /* set btree special data */
1837diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
1838index 2001dc1..61717bc 100644
1839--- a/src/backend/access/nbtree/nbtpage.c
1840+++ b/src/backend/access/nbtree/nbtpage.c
1841@@ -26,6 +26,7 @@
1842 #include "access/transam.h"
1843 #include "access/xlog.h"
1844 #include "access/xloginsert.h"
1845+#include "access/ptrack.h"
1846 #include "miscadmin.h"
1847 #include "storage/indexfsm.h"
1848 #include "storage/lmgr.h"
1849@@ -221,6 +222,8 @@ _bt_getroot(Relation rel, int access)
1850 rootopaque->btpo_cycleid = 0;
1851
1852 /* NO ELOG(ERROR) till meta is updated */
1853+ ptrack_add_block(rel, BufferGetBlockNumber(rootbuf));
1854+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf));
1855 START_CRIT_SECTION();
1856
1857 metad->btm_root = rootblkno;
1858@@ -793,6 +796,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf,
1859 BTPageOpaque opaque;
1860
1861 /* No ereport(ERROR) until changes are logged */
1862+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
1863 START_CRIT_SECTION();
1864
1865 /* Fix the page */
1866@@ -869,6 +873,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
1867 Assert(nitems > 0);
1868
1869 /* No ereport(ERROR) until changes are logged */
1870+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
1871 START_CRIT_SECTION();
1872
1873 /* Fix the page */
1874@@ -1409,6 +1414,8 @@ _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
1875 PredicateLockPageCombine(rel, leafblkno, leafrightsib);
1876
1877 /* No ereport(ERROR) until changes are logged */
1878+ ptrack_add_block(rel, BufferGetBlockNumber(topparent));
1879+ ptrack_add_block(rel, BufferGetBlockNumber(leafbuf));
1880 START_CRIT_SECTION();
1881
1882 /*
1883@@ -1731,6 +1738,14 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty)
1884 */
1885
1886 /* No ereport(ERROR) until changes are logged */
1887+ ptrack_add_block(rel, BufferGetBlockNumber(rbuf));
1888+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
1889+ if (BufferIsValid(lbuf))
1890+ ptrack_add_block(rel, BufferGetBlockNumber(lbuf));
1891+ if (BufferIsValid(metabuf))
1892+ ptrack_add_block(rel, BufferGetBlockNumber(metabuf));
1893+ if (target != leafblkno)
1894+ ptrack_add_block(rel, BufferGetBlockNumber(leafbuf));
1895 START_CRIT_SECTION();
1896
1897 /*
1898diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
1899index a264b92..8da35ef 100644
1900--- a/src/backend/access/nbtree/nbtree.c
1901+++ b/src/backend/access/nbtree/nbtree.c
1902@@ -21,6 +21,7 @@
1903 #include "access/nbtree.h"
1904 #include "access/relscan.h"
1905 #include "access/xlog.h"
1906+#include "access/ptrack.h"
1907 #include "catalog/index.h"
1908 #include "commands/vacuum.h"
1909 #include "storage/indexfsm.h"
1910@@ -252,6 +253,7 @@ btbuildempty(Relation index)
1911 PageSetChecksumInplace(metapage, BTREE_METAPAGE);
1912 smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE,
1913 (char *) metapage, true);
1914+
1915 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
1916 BTREE_METAPAGE, metapage, false);
1917
1918diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
1919index 99a014e..d4afb93 100644
1920--- a/src/backend/access/nbtree/nbtsort.c
1921+++ b/src/backend/access/nbtree/nbtsort.c
1922@@ -69,6 +69,7 @@
1923 #include "access/nbtree.h"
1924 #include "access/xlog.h"
1925 #include "access/xloginsert.h"
1926+#include "access/ptrack.h"
1927 #include "miscadmin.h"
1928 #include "storage/smgr.h"
1929 #include "tcop/tcopprot.h"
1930@@ -276,8 +277,12 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
1931 /* XLOG stuff */
1932 if (wstate->btws_use_wal)
1933 {
1934+ /* Don't forget to set ptrack bit even if we're skipping bufmgr stage */
1935+ ptrack_add_block(wstate->index, blkno);
1936 /* We use the heap NEWPAGE record type for this */
1937 log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page, true);
1938+ /* Ensure rd_smgr is open (could have been closed by relcache flush!) */
1939+ RelationOpenSmgr(wstate->index);
1940 }
1941
1942 /*
1943diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
1944index c536e22..d834878 100644
1945--- a/src/backend/access/nbtree/nbtxlog.c
1946+++ b/src/backend/access/nbtree/nbtxlog.c
1947@@ -19,6 +19,7 @@
1948 #include "access/transam.h"
1949 #include "access/xlog.h"
1950 #include "access/xlogutils.h"
1951+#include "access/ptrack.h"
1952 #include "storage/procarray.h"
1953 #include "miscadmin.h"
1954
1955@@ -82,6 +83,11 @@ _bt_restore_meta(XLogReaderState *record, uint8 block_id)
1956 xl_btree_metadata *xlrec;
1957 char *ptr;
1958 Size len;
1959+ RelFileNode rnode;
1960+ BlockNumber blkno;
1961+
1962+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno);
1963+ ptrack_add_block_redo(rnode, blkno);
1964
1965 metabuf = XLogInitBufferForRedo(record, block_id);
1966 ptr = XLogRecGetBlockData(record, block_id, &len);
1967@@ -127,6 +133,11 @@ _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id)
1968 {
1969 XLogRecPtr lsn = record->EndRecPtr;
1970 Buffer buf;
1971+ RelFileNode rnode;
1972+ BlockNumber blkno;
1973+
1974+ XLogRecGetBlockTag(record, block_id, &rnode, NULL, &blkno);
1975+ ptrack_add_block_redo(rnode, blkno);
1976
1977 if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO)
1978 {
1979@@ -150,6 +161,11 @@ btree_xlog_insert(bool isleaf, bool ismeta, XLogReaderState *record)
1980 xl_btree_insert *xlrec = (xl_btree_insert *) XLogRecGetData(record);
1981 Buffer buffer;
1982 Page page;
1983+ RelFileNode rnode;
1984+ BlockNumber blkno;
1985+
1986+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
1987+ ptrack_add_block_redo(rnode, blkno);
1988
1989 /*
1990 * Insertion to an internal page finishes an incomplete split at the child
1991@@ -207,11 +223,17 @@ btree_xlog_split(bool onleft, bool isroot, XLogReaderState *record)
1992 BlockNumber leftsib;
1993 BlockNumber rightsib;
1994 BlockNumber rnext;
1995+ RelFileNode rnode;
1996+
1997+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &leftsib);
1998+ ptrack_add_block_redo(rnode, leftsib);
1999+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &rightsib);
2000+ ptrack_add_block_redo(rnode, rightsib);
2001
2002- XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
2003- XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
2004 if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
2005 rnext = P_NONE;
2006+ else
2007+ ptrack_add_block_redo(rnode, rnext);
2008
2009 /*
2010 * Clear the incomplete split flag on the left sibling of the child page
2011@@ -388,6 +410,12 @@ btree_xlog_vacuum(XLogReaderState *record)
2012 Buffer buffer;
2013 Page page;
2014 BTPageOpaque opaque;
2015+ RelFileNode rnode;
2016+ BlockNumber blkno;
2017+
2018+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2019+ ptrack_add_block_redo(rnode, blkno);
2020+
2021 #ifdef UNUSED
2022 xl_btree_vacuum *xlrec = (xl_btree_vacuum *) XLogRecGetData(record);
2023
2024@@ -566,6 +594,7 @@ btree_xlog_delete_get_latestRemovedXid(XLogReaderState *record)
2025 * overkill, but it's safe, and certainly better than panicking here.
2026 */
2027 XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2028+ ptrack_add_block_redo(rnode, blkno);
2029 ibuffer = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL);
2030 if (!BufferIsValid(ibuffer))
2031 return InvalidTransactionId;
2032@@ -591,6 +620,7 @@ btree_xlog_delete_get_latestRemovedXid(XLogReaderState *record)
2033 */
2034 hblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
2035 hbuffer = XLogReadBufferExtended(xlrec->hnode, MAIN_FORKNUM, hblkno, RBM_NORMAL);
2036+ ptrack_add_block_redo(rnode, hblkno);
2037 if (!BufferIsValid(hbuffer))
2038 {
2039 UnlockReleaseBuffer(ibuffer);
2040@@ -665,6 +695,11 @@ btree_xlog_delete(XLogReaderState *record)
2041 Buffer buffer;
2042 Page page;
2043 BTPageOpaque opaque;
2044+ RelFileNode rnode;
2045+ BlockNumber blkno;
2046+
2047+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2048+ ptrack_add_block_redo(rnode, blkno);
2049
2050 /*
2051 * If we have any conflict processing to do, it must happen before we
2052@@ -727,6 +762,13 @@ btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record)
2053 Page page;
2054 BTPageOpaque pageop;
2055 IndexTupleData trunctuple;
2056+ RelFileNode rnode;
2057+ BlockNumber blkno;
2058+
2059+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2060+ ptrack_add_block_redo(rnode, blkno);
2061+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2062+ ptrack_add_block_redo(rnode, blkno);
2063
2064 /*
2065 * In normal operation, we would lock all the pages this WAL record
2066@@ -810,10 +852,27 @@ btree_xlog_unlink_page(uint8 info, XLogReaderState *record)
2067 Buffer buffer;
2068 Page page;
2069 BTPageOpaque pageop;
2070+ RelFileNode rnode;
2071+ BlockNumber blkno;
2072
2073 leftsib = xlrec->leftsib;
2074 rightsib = xlrec->rightsib;
2075
2076+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
2077+ ptrack_add_block_redo(rnode, blkno);
2078+ if (leftsib != P_NONE)
2079+ {
2080+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2081+ ptrack_add_block_redo(rnode, blkno);
2082+ }
2083+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2084+ ptrack_add_block_redo(rnode, blkno);
2085+ if (XLogRecHasBlockRef(record, 3))
2086+ {
2087+ XLogRecGetBlockTag(record, 3, &rnode, NULL, &blkno);
2088+ ptrack_add_block_redo(rnode, blkno);
2089+ }
2090+
2091 /*
2092 * In normal operation, we would lock all the pages this WAL record
2093 * touches before changing any of them. In WAL replay, it should be okay
2094@@ -924,6 +983,11 @@ btree_xlog_newroot(XLogReaderState *record)
2095 BTPageOpaque pageop;
2096 char *ptr;
2097 Size len;
2098+ RelFileNode rnode;
2099+ BlockNumber blkno;
2100+
2101+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2102+ ptrack_add_block_redo(rnode, blkno);
2103
2104 buffer = XLogInitBufferForRedo(record, 0);
2105 page = (Page) BufferGetPage(buffer);
2106diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
2107index f090ca5..88dfb56 100644
2108--- a/src/backend/access/spgist/spgdoinsert.c
2109+++ b/src/backend/access/spgist/spgdoinsert.c
2110@@ -18,6 +18,7 @@
2111 #include "access/genam.h"
2112 #include "access/spgist_private.h"
2113 #include "access/xloginsert.h"
2114+#include "access/ptrack.h"
2115 #include "miscadmin.h"
2116 #include "storage/bufmgr.h"
2117 #include "utils/rel.h"
2118@@ -213,6 +214,9 @@ addLeafTuple(Relation index, SpGistState *state, SpGistLeafTuple leafTuple,
2119 xlrec.offnumParent = InvalidOffsetNumber;
2120 xlrec.nodeI = 0;
2121
2122+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer));
2123+ if (parent->buffer)
2124+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer));
2125 START_CRIT_SECTION();
2126
2127 if (current->offnum == InvalidOffsetNumber ||
2128@@ -457,6 +461,9 @@ moveLeafs(Relation index, SpGistState *state,
2129
2130 leafdata = leafptr = palloc(size);
2131
2132+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer));
2133+ ptrack_add_block(index, BufferGetBlockNumber(nbuf));
2134+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer));
2135 START_CRIT_SECTION();
2136
2137 /* copy all the old tuples to new page, unless they're dead */
2138@@ -1109,6 +1116,14 @@ doPickSplit(Relation index, SpGistState *state,
2139 leafdata = leafptr = (char *) palloc(totalLeafSizes);
2140
2141 /* Here we begin making the changes to the target pages */
2142+ if (current->buffer != InvalidBuffer)
2143+ ptrack_add_block(index, current->blkno);
2144+ if (parent->buffer != InvalidBuffer)
2145+ ptrack_add_block(index, parent->blkno);
2146+ if (newInnerBuffer != InvalidBuffer)
2147+ ptrack_add_block(index, BufferGetBlockNumber(newInnerBuffer));
2148+ if (newLeafBuffer != InvalidBuffer)
2149+ ptrack_add_block(index, BufferGetBlockNumber(newLeafBuffer));
2150 START_CRIT_SECTION();
2151
2152 /*
2153@@ -1519,6 +1534,7 @@ spgAddNodeAction(Relation index, SpGistState *state,
2154 /*
2155 * We can replace the inner tuple by new version in-place
2156 */
2157+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer));
2158 START_CRIT_SECTION();
2159
2160 PageIndexTupleDelete(current->page, current->offnum);
2161@@ -1602,6 +1618,9 @@ spgAddNodeAction(Relation index, SpGistState *state,
2162 else
2163 xlrec.parentBlk = 2;
2164
2165+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer));
2166+ ptrack_add_block(index, BufferGetBlockNumber(saveCurrent.buffer));
2167+ ptrack_add_block(index, BufferGetBlockNumber(parent->buffer));
2168 START_CRIT_SECTION();
2169
2170 /* insert new ... */
2171@@ -1764,6 +1783,9 @@ spgSplitNodeAction(Relation index, SpGistState *state,
2172 &xlrec.newPage);
2173 }
2174
2175+ if (newBuffer != InvalidBuffer)
2176+ ptrack_add_block(index, BufferGetBlockNumber(newBuffer));
2177+ ptrack_add_block(index, BufferGetBlockNumber(current->buffer));
2178 START_CRIT_SECTION();
2179
2180 /*
2181diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
2182index 3eaa649..53f292c 100644
2183--- a/src/backend/access/spgist/spginsert.c
2184+++ b/src/backend/access/spgist/spginsert.c
2185@@ -20,6 +20,7 @@
2186 #include "access/spgist_private.h"
2187 #include "access/xlog.h"
2188 #include "access/xloginsert.h"
2189+#include "access/ptrack.h"
2190 #include "catalog/index.h"
2191 #include "miscadmin.h"
2192 #include "storage/bufmgr.h"
2193@@ -90,6 +91,9 @@ spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
2194 Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
2195 Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
2196
2197+ ptrack_add_block(index, BufferGetBlockNumber(metabuffer));
2198+ ptrack_add_block(index, BufferGetBlockNumber(rootbuffer));
2199+ ptrack_add_block(index, BufferGetBlockNumber(nullbuffer));
2200 START_CRIT_SECTION();
2201
2202 SpGistInitMetapage(BufferGetPage(metabuffer));
2203diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
2204index 15b867f..7afa7fb 100644
2205--- a/src/backend/access/spgist/spgvacuum.c
2206+++ b/src/backend/access/spgist/spgvacuum.c
2207@@ -19,6 +19,7 @@
2208 #include "access/spgist_private.h"
2209 #include "access/transam.h"
2210 #include "access/xloginsert.h"
2211+#include "access/ptrack.h"
2212 #include "catalog/storage_xlog.h"
2213 #include "commands/vacuum.h"
2214 #include "miscadmin.h"
2215@@ -323,6 +324,7 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
2216 elog(ERROR, "inconsistent counts of deletable tuples");
2217
2218 /* Do the updates */
2219+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
2220 START_CRIT_SECTION();
2221
2222 spgPageIndexMultiDelete(&bds->spgstate, page,
2223@@ -447,6 +449,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
2224 return; /* nothing more to do */
2225
2226 /* Do the update */
2227+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
2228 START_CRIT_SECTION();
2229
2230 /* The tuple numbers are in order, so we can use PageIndexMultiDelete */
2231@@ -504,6 +507,7 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
2232 xlrec.nToPlaceholder = 0;
2233 xlrec.newestRedirectXid = InvalidTransactionId;
2234
2235+ ptrack_add_block(index, BufferGetBlockNumber(buffer));
2236 START_CRIT_SECTION();
2237
2238 /*
2239diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c
2240index e016cdb..6033bb8 100644
2241--- a/src/backend/access/spgist/spgxlog.c
2242+++ b/src/backend/access/spgist/spgxlog.c
2243@@ -18,6 +18,7 @@
2244 #include "access/transam.h"
2245 #include "access/xlog.h"
2246 #include "access/xlogutils.h"
2247+#include "access/ptrack.h"
2248 #include "storage/standby.h"
2249 #include "utils/memutils.h"
2250
2251@@ -76,6 +77,15 @@ spgRedoCreateIndex(XLogReaderState *record)
2252 XLogRecPtr lsn = record->EndRecPtr;
2253 Buffer buffer;
2254 Page page;
2255+ RelFileNode rnode;
2256+ BlockNumber blkno;
2257+
2258+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2259+ ptrack_add_block_redo(rnode, blkno);
2260+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2261+ ptrack_add_block_redo(rnode, blkno);
2262+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
2263+ ptrack_add_block_redo(rnode, blkno);
2264
2265 buffer = XLogInitBufferForRedo(record, 0);
2266 Assert(BufferGetBlockNumber(buffer) == SPGIST_METAPAGE_BLKNO);
2267@@ -113,6 +123,16 @@ spgRedoAddLeaf(XLogReaderState *record)
2268 Buffer buffer;
2269 Page page;
2270 XLogRedoAction action;
2271+ RelFileNode rnode;
2272+ BlockNumber blkno;
2273+
2274+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2275+ ptrack_add_block_redo(rnode, blkno);
2276+ if (xldata->offnumParent != InvalidOffsetNumber)
2277+ {
2278+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2279+ ptrack_add_block_redo(rnode, blkno);
2280+ }
2281
2282 ptr += sizeof(spgxlogAddLeaf);
2283 leafTuple = ptr;
2284@@ -213,8 +233,15 @@ spgRedoMoveLeafs(XLogReaderState *record)
2285 Page page;
2286 XLogRedoAction action;
2287 BlockNumber blknoDst;
2288+ RelFileNode rnode;
2289+ BlockNumber blkno;
2290
2291- XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoDst);
2292+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blknoDst);
2293+ ptrack_add_block_redo(rnode, blknoDst);
2294+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2295+ ptrack_add_block_redo(rnode, blkno);
2296+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
2297+ ptrack_add_block_redo(rnode, blkno);
2298
2299 fillFakeState(&state, xldata->stateSrc);
2300
2301@@ -324,6 +351,21 @@ spgRedoAddNode(XLogReaderState *record)
2302 Buffer buffer;
2303 Page page;
2304 XLogRedoAction action;
2305+ RelFileNode rnode;
2306+ BlockNumber blkno;
2307+
2308+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2309+ ptrack_add_block_redo(rnode, blkno);
2310+ if(XLogRecHasBlockRef(record, 1))
2311+ {
2312+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2313+ ptrack_add_block_redo(rnode, blkno);
2314+ }
2315+ if (xldata->parentBlk == 2)
2316+ {
2317+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blkno);
2318+ ptrack_add_block_redo(rnode, blkno);
2319+ }
2320
2321 ptr += sizeof(spgxlogAddNode);
2322 innerTuple = ptr;
2323@@ -492,6 +534,13 @@ spgRedoSplitTuple(XLogReaderState *record)
2324 Buffer buffer;
2325 Page page;
2326 XLogRedoAction action;
2327+ RelFileNode rnode;
2328+ BlockNumber blkno;
2329+
2330+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2331+ ptrack_add_block_redo(rnode, blkno);
2332+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2333+ ptrack_add_block_redo(rnode, blkno);
2334
2335 ptr += sizeof(spgxlogSplitTuple);
2336 prefixTuple = ptr;
2337@@ -578,8 +627,23 @@ spgRedoPickSplit(XLogReaderState *record)
2338 int i;
2339 BlockNumber blknoInner;
2340 XLogRedoAction action;
2341-
2342- XLogRecGetBlockTag(record, 2, NULL, NULL, &blknoInner);
2343+ RelFileNode rnode;
2344+ BlockNumber blkno;
2345+
2346+ XLogRecGetBlockTag(record, 2, &rnode, NULL, &blknoInner);
2347+ ptrack_add_block_redo(rnode, blknoInner);
2348+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2349+ ptrack_add_block_redo(rnode, blkno);
2350+ if (XLogRecHasBlockRef(record, 1))
2351+ {
2352+ XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
2353+ ptrack_add_block_redo(rnode, blkno);
2354+ }
2355+ if (XLogRecHasBlockRef(record, 3))
2356+ {
2357+ XLogRecGetBlockTag(record, 3, &rnode, NULL, &blkno);
2358+ ptrack_add_block_redo(rnode, blkno);
2359+ }
2360
2361 fillFakeState(&state, xldata->stateSrc);
2362
2363@@ -795,6 +859,11 @@ spgRedoVacuumLeaf(XLogReaderState *record)
2364 Buffer buffer;
2365 Page page;
2366 int i;
2367+ RelFileNode rnode;
2368+ BlockNumber blkno;
2369+
2370+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2371+ ptrack_add_block_redo(rnode, blkno);
2372
2373 fillFakeState(&state, xldata->stateSrc);
2374
2375@@ -871,6 +940,11 @@ spgRedoVacuumRoot(XLogReaderState *record)
2376 OffsetNumber *toDelete;
2377 Buffer buffer;
2378 Page page;
2379+ RelFileNode rnode;
2380+ BlockNumber blkno;
2381+
2382+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2383+ ptrack_add_block_redo(rnode, blkno);
2384
2385 toDelete = xldata->offsets;
2386
2387@@ -896,6 +970,11 @@ spgRedoVacuumRedirect(XLogReaderState *record)
2388 spgxlogVacuumRedirect *xldata = (spgxlogVacuumRedirect *) ptr;
2389 OffsetNumber *itemToPlaceholder;
2390 Buffer buffer;
2391+ RelFileNode rnode;
2392+ BlockNumber blkno;
2393+
2394+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2395+ ptrack_add_block_redo(rnode, blkno);
2396
2397 itemToPlaceholder = xldata->offsets;
2398
2399diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
2400index 1b084a3..b21585b 100644
2401--- a/src/backend/access/transam/xlog.c
2402+++ b/src/backend/access/transam/xlog.c
2403@@ -35,6 +35,7 @@
2404 #include "access/xloginsert.h"
2405 #include "access/xlogreader.h"
2406 #include "access/xlogutils.h"
2407+#include "access/ptrack.h"
2408 #include "catalog/catversion.h"
2409 #include "catalog/pg_control.h"
2410 #include "catalog/pg_database.h"
2411@@ -9492,6 +9493,11 @@ xlog_redo(XLogReaderState *record)
2412 else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
2413 {
2414 Buffer buffer;
2415+ RelFileNode rnode;
2416+ BlockNumber blkno;
2417+
2418+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2419+ ptrack_add_block_redo(rnode, blkno);
2420
2421 /*
2422 * Full-page image (FPI) records contain nothing else but a backup
2423diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
2424index a92534e..3552135 100644
2425--- a/src/backend/access/transam/xloginsert.c
2426+++ b/src/backend/access/transam/xloginsert.c
2427@@ -23,6 +23,7 @@
2428 #include "access/xlog.h"
2429 #include "access/xlog_internal.h"
2430 #include "access/xloginsert.h"
2431+#include "access/ptrack.h"
2432 #include "catalog/pg_control.h"
2433 #include "common/pg_lzcompress.h"
2434 #include "miscadmin.h"
2435diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
2436index 51a8e8d..739b5c2 100644
2437--- a/src/backend/access/transam/xlogutils.c
2438+++ b/src/backend/access/transam/xlogutils.c
2439@@ -550,8 +550,6 @@ CreateFakeRelcacheEntry(RelFileNode rnode)
2440 FakeRelCacheEntry fakeentry;
2441 Relation rel;
2442
2443- Assert(InRecovery);
2444-
2445 /* Allocate the Relation struct and all related space in one block. */
2446 fakeentry = palloc0(sizeof(FakeRelCacheEntryData));
2447 rel = (Relation) fakeentry;
2448diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
2449index 0d8311c..778dbfb 100644
2450--- a/src/backend/catalog/storage.c
2451+++ b/src/backend/catalog/storage.c
2452@@ -238,6 +238,7 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
2453 rel->rd_smgr->smgr_targblock = InvalidBlockNumber;
2454 rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber;
2455 rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
2456+ rel->rd_smgr->smgr_ptrack_nblocks = InvalidBlockNumber;
2457
2458 /* Truncate the FSM first if it exists */
2459 fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
2460diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
2461index f47a13d..79fab18 100644
2462--- a/src/backend/commands/dbcommands.c
2463+++ b/src/backend/commands/dbcommands.c
2464@@ -30,6 +30,7 @@
2465 #include "access/xact.h"
2466 #include "access/xloginsert.h"
2467 #include "access/xlogutils.h"
2468+#include "access/ptrack.h"
2469 #include "catalog/catalog.h"
2470 #include "catalog/dependency.h"
2471 #include "catalog/indexing.h"
2472@@ -616,6 +617,7 @@ createdb(const CreatedbStmt *stmt)
2473 * We don't need to copy subdirectories
2474 */
2475 copydir(srcpath, dstpath, false);
2476+ create_ptrack_init_file(dstpath);
2477
2478 /* Record the filesystem change in XLOG */
2479 {
2480@@ -1222,6 +1224,7 @@ movedb(const char *dbname, const char *tblspcname)
2481 * Copy files from the old tablespace to the new one
2482 */
2483 copydir(src_dbpath, dst_dbpath, false);
2484+ create_ptrack_init_file(dst_dbpath);
2485
2486 /*
2487 * Record the filesystem change in XLOG
2488@@ -2085,6 +2088,7 @@ dbase_redo(XLogReaderState *record)
2489 * We don't need to copy subdirectories
2490 */
2491 copydir(src_path, dst_path, false);
2492+ create_ptrack_init_file(dst_path);
2493 }
2494 else if (info == XLOG_DBASE_DROP)
2495 {
2496diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
2497index c98f981..1fc6114 100644
2498--- a/src/backend/commands/sequence.c
2499+++ b/src/backend/commands/sequence.c
2500@@ -21,6 +21,7 @@
2501 #include "access/xlog.h"
2502 #include "access/xloginsert.h"
2503 #include "access/xlogutils.h"
2504+#include "access/ptrack.h"
2505 #include "catalog/dependency.h"
2506 #include "catalog/namespace.h"
2507 #include "catalog/objectaccess.h"
2508@@ -365,6 +366,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
2509 if (RelationNeedsWAL(rel))
2510 GetTopTransactionId();
2511
2512+ ptrack_add_block(rel, BufferGetBlockNumber(buf));
2513 START_CRIT_SECTION();
2514
2515 MarkBufferDirty(buf);
2516@@ -451,6 +453,7 @@ AlterSequence(AlterSeqStmt *stmt)
2517 GetTopTransactionId();
2518
2519 /* Now okay to update the on-disk tuple */
2520+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf));
2521 START_CRIT_SECTION();
2522
2523 memcpy(seq, &new, sizeof(FormData_pg_sequence));
2524@@ -711,6 +714,7 @@ nextval_internal(Oid relid)
2525 GetTopTransactionId();
2526
2527 /* ready to change the on-disk (or really, in-buffer) tuple */
2528+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf));
2529 START_CRIT_SECTION();
2530
2531 /*
2532@@ -910,6 +914,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
2533 GetTopTransactionId();
2534
2535 /* ready to change the on-disk (or really, in-buffer) tuple */
2536+ ptrack_add_block(seqrel, BufferGetBlockNumber(buf));
2537 START_CRIT_SECTION();
2538
2539 seq->last_value = next; /* last fetched number */
2540@@ -1586,6 +1591,11 @@ seq_redo(XLogReaderState *record)
2541 Size itemsz;
2542 xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
2543 sequence_magic *sm;
2544+ RelFileNode rnode;
2545+ BlockNumber blkno;
2546+
2547+ XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
2548+ ptrack_add_block_redo(rnode, blkno);
2549
2550 if (info != XLOG_SEQ_LOG)
2551 elog(PANIC, "seq_redo: unknown op code %u", info);
2552diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
2553index 1f7c732..39c4ac7 100644
2554--- a/src/backend/commands/tablecmds.c
2555+++ b/src/backend/commands/tablecmds.c
2556@@ -23,6 +23,7 @@
2557 #include "access/tupconvert.h"
2558 #include "access/xact.h"
2559 #include "access/xlog.h"
2560+#include "access/ptrack.h"
2561 #include "catalog/catalog.h"
2562 #include "catalog/dependency.h"
2563 #include "catalog/heap.h"
2564@@ -9773,6 +9774,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
2565 /* copy those extra forks that exist */
2566 for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
2567 {
2568+ /*
2569+ * Do not copy ptrack fork, because it will be created
2570+ * for new relation while copying data.
2571+ */
2572+ if (forkNum == PAGESTRACK_FORKNUM)
2573+ continue;
2574+
2575 if (smgrexists(rel->rd_smgr, forkNum))
2576 {
2577 smgrcreate(dstrel, forkNum, false);
2578@@ -10056,7 +10064,18 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
2579 * space.
2580 */
2581 if (use_wal)
2582+ {
2583+ /*
2584+ * Don't forget to set ptrack bit even if we're skipping bufmgr
2585+ * stage. The reason to use ptrack_add_block_redo() instead of the
2586+ * regular ptrack_add_block() function is that we don't have
2587+ * a Relation structure here.
2588+ */
2589+ if (forkNum == MAIN_FORKNUM &&
2590+ relpersistence == RELPERSISTENCE_PERMANENT)
2591+ ptrack_add_block_redo(dst->smgr_rnode.node, blkno);
2592 log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
2593+ }
2594
2595 PageSetChecksumInplace(page, blkno);
2596
2597diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
2598index ff8fe3f..e534cbb 100644
2599--- a/src/backend/commands/vacuumlazy.c
2600+++ b/src/backend/commands/vacuumlazy.c
2601@@ -45,6 +45,7 @@
2602 #include "access/transam.h"
2603 #include "access/visibilitymap.h"
2604 #include "access/xlog.h"
2605+#include "access/ptrack.h"
2606 #include "catalog/catalog.h"
2607 #include "catalog/storage.h"
2608 #include "commands/dbcommands.h"
2609@@ -857,6 +858,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
2610 empty_pages++;
2611 }
2612 freespace = PageGetHeapFreeSpace(page);
2613+ ptrack_add_block(onerel, BufferGetBlockNumber(buf));
2614 MarkBufferDirty(buf);
2615 UnlockReleaseBuffer(buf);
2616
2617@@ -872,6 +874,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
2618 /* empty pages are always all-visible and all-frozen */
2619 if (!PageIsAllVisible(page))
2620 {
2621+ ptrack_add_block(onerel, BufferGetBlockNumber(buf));
2622 START_CRIT_SECTION();
2623
2624 /* mark buffer dirty before writing a WAL record */
2625@@ -1106,6 +1109,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
2626 */
2627 if (nfrozen > 0)
2628 {
2629+ ptrack_add_block(onerel, BufferGetBlockNumber(buf));
2630 START_CRIT_SECTION();
2631
2632 MarkBufferDirty(buf);
2633@@ -1179,6 +1183,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
2634 * rare cases after a crash, it is not worth optimizing.
2635 */
2636 PageSetAllVisible(page);
2637+ ptrack_add_block(onerel, BufferGetBlockNumber(buf));
2638 MarkBufferDirty(buf);
2639 visibilitymap_set(onerel, blkno, buf, InvalidXLogRecPtr,
2640 vmbuffer, visibility_cutoff_xid, flags);
2641@@ -1218,6 +1223,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
2642 elog(WARNING, "page containing dead tuples is marked as all-visible in relation \"%s\" page %u",
2643 relname, blkno);
2644 PageClearAllVisible(page);
2645+ ptrack_add_block(onerel, BufferGetBlockNumber(buf));
2646 MarkBufferDirty(buf);
2647 visibilitymap_clear(onerel, blkno, vmbuffer,
2648 VISIBILITYMAP_VALID_BITS);
2649@@ -1452,6 +1458,7 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
2650
2651 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno);
2652
2653+ ptrack_add_block(onerel, BufferGetBlockNumber(buffer));
2654 START_CRIT_SECTION();
2655
2656 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++)
2657diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
2658index 94aa952..101c27d 100644
2659--- a/src/backend/storage/smgr/smgr.c
2660+++ b/src/backend/storage/smgr/smgr.c
2661@@ -170,6 +170,7 @@ smgropen(RelFileNode rnode, BackendId backend)
2662 reln->smgr_targblock = InvalidBlockNumber;
2663 reln->smgr_fsm_nblocks = InvalidBlockNumber;
2664 reln->smgr_vm_nblocks = InvalidBlockNumber;
2665+ reln->smgr_ptrack_nblocks = InvalidBlockNumber;
2666 reln->smgr_which = 0; /* we only have md.c at present */
2667
2668 /* mark it not open */
2669diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
2670index 0a4295b..401714f 100644
2671--- a/src/backend/utils/init/postinit.c
2672+++ b/src/backend/utils/init/postinit.c
2673@@ -24,6 +24,7 @@
2674 #include "access/sysattr.h"
2675 #include "access/xact.h"
2676 #include "access/xlog.h"
2677+#include "access/ptrack.h"
2678 #include "catalog/catalog.h"
2679 #include "catalog/indexing.h"
2680 #include "catalog/namespace.h"
2681@@ -565,6 +566,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
2682
2683 elog(DEBUG3, "InitPostgres");
2684
2685+ assign_ptrack_enable(ptrack_enable, NULL);
2686+
2687 /*
2688 * Add my PGPROC struct to the ProcArray.
2689 *
2690diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
2691index 9ecbe4a..db2acd1 100644
2692--- a/src/backend/utils/misc/guc.c
2693+++ b/src/backend/utils/misc/guc.c
2694@@ -31,6 +31,7 @@
2695 #include "access/transam.h"
2696 #include "access/twophase.h"
2697 #include "access/xact.h"
2698+#include "access/ptrack.h"
2699 #include "catalog/namespace.h"
2700 #include "commands/async.h"
2701 #include "commands/prepare.h"
2702@@ -1001,6 +1002,16 @@ static struct config_bool ConfigureNamesBool[] =
2703 },
2704
2705 {
2706+ {"ptrack_enable", PGC_SIGHUP, WAL_SETTINGS,
2707+ gettext_noop("Enable page tracking."),
2708+ NULL
2709+ },
2710+ &ptrack_enable,
2711+ false,
2712+ NULL, &assign_ptrack_enable, NULL
2713+ },
2714+
2715+ {
2716 {"wal_compression", PGC_SUSET, WAL_SETTINGS,
2717 gettext_noop("Compresses full-page writes written in WAL file."),
2718 NULL
2719diff --git a/src/common/relpath.c b/src/common/relpath.c
2720index 1aacb81..324e9eb 100644
2721--- a/src/common/relpath.c
2722+++ b/src/common/relpath.c
2723@@ -35,7 +35,8 @@ const char *const forkNames[] = {
2724 "main", /* MAIN_FORKNUM */
2725 "fsm", /* FSM_FORKNUM */
2726 "vm", /* VISIBILITYMAP_FORKNUM */
2727- "init" /* INIT_FORKNUM */
2728+ "init", /* INIT_FORKNUM */
2729+ "ptrack" /* PAGESTRACK_FORKNUM */
2730 };
2731
2732 /*
2733diff --git a/src/include/access/ptrack.h b/src/include/access/ptrack.h
2734new file mode 100644
2735index 0000000..57277be
2736--- /dev/null
2737+++ b/src/include/access/ptrack.h
2738@@ -0,0 +1,30 @@
2739+#ifndef PTRACK_H
2740+#define PTRACK_H
2741+
2742+#include "access/xlogdefs.h"
2743+#include "storage/block.h"
2744+#include "storage/buf.h"
2745+#include "storage/relfilenode.h"
2746+#include "utils/relcache.h"
2747+
2748+/* Ptrack version as a string */
2749+#define PTRACK_VERSION "1.5"
2750+/* Ptrack version as a number */
2751+#define PTRACK_VERSION_NUM 105
2752+
2753+/* Number of bits allocated for each heap block. */
2754+#define PTRACK_BITS_PER_HEAPBLOCK 1
2755+
2756+#define PTRACK_INIT_FILE "ptrack_init"
2757+
2758+extern PGDLLIMPORT bool ptrack_enable;
2759+
2760+extern void ptrack_add_block(Relation rel, BlockNumber heapBlk);
2761+extern void ptrack_add_block_redo(RelFileNode rnode, BlockNumber heapBlk);
2762+extern void create_ptrack_init_file(char *dest_dir);
2763+
2764+extern void ptrack_clear(void);
2765+extern bytea *ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid);
2766+extern void assign_ptrack_enable(bool newval, void *extra);
2767+
2768+#endif /* PTRACK_H */
2769diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
2770index af19c1a..0286142 100644
2771--- a/src/include/catalog/pg_proc.h
2772+++ b/src/include/catalog/pg_proc.h
2773@@ -5347,6 +5347,22 @@ DESCR("pg_controldata init state information as a function");
2774 #define PROPARALLEL_RESTRICTED 'r' /* can run in parallel master only */
2775 #define PROPARALLEL_UNSAFE 'u' /* banned while in parallel mode */
2776
2777+/* ptrack related functions*/
2778+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_ ));
2779+DESCR("clear ptrack fork files");
2780+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_ ));
2781+DESCR("get ptrack file as bytea and clear it");
2782+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_ ));
2783+DESCR("check if ptrack_init_file exists in the given database");
2784+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_ ));
2785+DESCR("Ptrack version string");
2786+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_ ));
2787+DESCR("read LSN from ptrack_control file");
2788+DATA(insert OID = 6024 ( pg_ptrack_get_block PGNSP PGUID 12 1 0 0 0 f f f f t f s s 3 0 17 "26 26 20" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_block _null_ _null_ _null_ ));
2789+DESCR("get one block of the relation");
2790+DATA(insert OID = 6025 ( pg_ptrack_get_block_2 PGNSP PGUID 12 1 0 0 0 f f f f t f s s 4 0 17 "26 26 26 20" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_block_2 _null_ _null_ _null_ ));
2791+DESCR("get one block of the relation");
2792+
2793 /*
2794 * Symbolic values for proargmodes column. Note that these must agree with
2795 * the FunctionParameterMode enum in parsenodes.h; we declare them here to
2796diff --git a/src/include/common/relpath.h b/src/include/common/relpath.h
2797index 8754b2f..edf69bd 100644
2798--- a/src/include/common/relpath.h
2799+++ b/src/include/common/relpath.h
2800@@ -27,7 +27,8 @@ typedef enum ForkNumber
2801 MAIN_FORKNUM = 0,
2802 FSM_FORKNUM,
2803 VISIBILITYMAP_FORKNUM,
2804- INIT_FORKNUM
2805+ INIT_FORKNUM,
2806+ PAGESTRACK_FORKNUM
2807
2808 /*
2809 * NOTE: if you add a new fork, change MAX_FORKNUM and possibly
2810@@ -36,9 +37,9 @@ typedef enum ForkNumber
2811 */
2812 } ForkNumber;
2813
2814-#define MAX_FORKNUM INIT_FORKNUM
2815+#define MAX_FORKNUM PAGESTRACK_FORKNUM
2816
2817-#define FORKNAMECHARS 4 /* max chars for a fork name */
2818+#define FORKNAMECHARS 5 /* max chars for a fork name */
2819
2820 extern const char *const forkNames[];
2821
2822diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
2823index a8e7877..3b4a4ad 100644
2824--- a/src/include/storage/smgr.h
2825+++ b/src/include/storage/smgr.h
2826@@ -46,15 +46,16 @@ typedef struct SMgrRelationData
2827 struct SMgrRelationData **smgr_owner;
2828
2829 /*
2830- * These next three fields are not actually used or manipulated by smgr,
2831+ * These next four fields are not actually used or manipulated by smgr,
2832 * except that they are reset to InvalidBlockNumber upon a cache flush
2833 * event (in particular, upon truncation of the relation). Higher levels
2834 * store cached state here so that it will be reset when truncation
2835- * happens. In all three cases, InvalidBlockNumber means "unknown".
2836+ * happens. In all four cases, InvalidBlockNumber means "unknown".
2837 */
2838 BlockNumber smgr_targblock; /* current insertion target block */
2839 BlockNumber smgr_fsm_nblocks; /* last known size of fsm fork */
2840- BlockNumber smgr_vm_nblocks; /* last known size of vm fork */
2841+ BlockNumber smgr_vm_nblocks; /* last known size of vm fork */
2842+ BlockNumber smgr_ptrack_nblocks; /* last known size of ptrack fork */
2843
2844 /* additional public fields may someday exist here */