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