/src/postgres/src/backend/access/heap/visibilitymap.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * visibilitymap.c |
4 | | * bitmap for tracking visibility of heap tuples |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/access/heap/visibilitymap.c |
12 | | * |
13 | | * INTERFACE ROUTINES |
14 | | * visibilitymap_clear - clear bits for one page in the visibility map |
15 | | * visibilitymap_pin - pin a map page for setting a bit |
16 | | * visibilitymap_pin_ok - check whether correct map page is already pinned |
17 | | * visibilitymap_set - set bit(s) in a previously pinned page |
18 | | * visibilitymap_get_status - get status of bits |
19 | | * visibilitymap_count - count number of bits set in visibility map |
20 | | * visibilitymap_prepare_truncate - |
21 | | * prepare for truncation of the visibility map |
22 | | * |
23 | | * NOTES |
24 | | * |
25 | | * The visibility map is a bitmap with two bits (all-visible and all-frozen) |
26 | | * per heap page. A set all-visible bit means that all tuples on the page are |
27 | | * known visible to all transactions, and therefore the page doesn't need to |
28 | | * be vacuumed. A set all-frozen bit means that all tuples on the page are |
29 | | * completely frozen, and therefore the page doesn't need to be vacuumed even |
30 | | * if whole table scanning vacuum is required (e.g. anti-wraparound vacuum). |
31 | | * The all-frozen bit must be set only when the page is already all-visible. |
32 | | * |
33 | | * The map is conservative in the sense that we make sure that whenever a bit |
34 | | * is set, we know the condition is true, but if a bit is not set, it might or |
35 | | * might not be true. |
36 | | * |
37 | | * Changes to the visibility map bits are not separately WAL-logged. Callers |
38 | | * must make sure that whenever a visibility map bit is cleared, the bit is |
39 | | * cleared on WAL replay of the updating operation. And whenever a visibility |
40 | | * map bit is set, the bit is set on WAL replay of the operation that rendered |
41 | | * the page all-visible/all-frozen. |
42 | | * |
43 | | * The visibility map bits operate as a hint in one direction: if they are |
44 | | * clear, it may still be the case that every tuple on the page is visible to |
45 | | * all transactions (we just don't know that for certain). However, if they |
46 | | * are set, we may skip vacuuming pages and advance relfrozenxid or skip |
47 | | * reading heap pages for an index-only scan. If they are incorrectly set, |
48 | | * this can lead to data corruption and wrong results. |
49 | | * |
50 | | * Additionally, it is critical that the heap-page level PD_ALL_VISIBLE bit be |
51 | | * correctly set and cleared along with the VM bits. |
52 | | * |
53 | | * When clearing the VM, if a crash occurs after the heap page makes it to |
54 | | * disk but before the VM page makes it to disk, replay must clear the VM or |
55 | | * the next index-only scan can return wrong results or vacuum may incorrectly |
56 | | * advance relfrozenxid. |
57 | | * |
58 | | * When setting the VM, if a crash occurs after the visibility map page makes |
59 | | * it to disk and before the updated heap page makes it to disk, redo must set |
60 | | * the bit on the heap page. Otherwise, the next insert, update, or delete on |
61 | | * the heap page will fail to realize that the visibility map bit must be |
62 | | * cleared, possibly causing index-only scans to return wrong answers. |
63 | | * |
64 | | * VACUUM will normally skip pages for which the visibility map bit is set; |
65 | | * such pages can't contain any dead tuples and therefore don't need vacuuming. |
66 | | * |
67 | | * LOCKING |
68 | | * |
69 | | * In heapam.c, whenever a page is modified so that not all tuples on the |
70 | | * page are visible to everyone anymore, the corresponding bit in the |
71 | | * visibility map is cleared. In order to be crash-safe, we need to do this |
72 | | * while still holding a lock on the heap page and in the same critical |
73 | | * section that logs the page modification. However, we don't want to hold |
74 | | * the buffer lock over any I/O that may be required to read in the visibility |
75 | | * map page. To avoid this, we examine the heap page before locking it; |
76 | | * if the page-level PD_ALL_VISIBLE bit is set, we pin the visibility map |
77 | | * bit. Then, we lock the buffer. But this creates a race condition: there |
78 | | * is a possibility that in the time it takes to lock the buffer, the |
79 | | * PD_ALL_VISIBLE bit gets set. If that happens, we have to unlock the |
80 | | * buffer, pin the visibility map page, and relock the buffer. This shouldn't |
81 | | * happen often, because only VACUUM currently sets visibility map bits, |
82 | | * and the race will only occur if VACUUM processes a given page at almost |
83 | | * exactly the same time that someone tries to further modify it. |
84 | | * |
85 | | * To set a bit, you need to hold a lock on the heap page. That prevents |
86 | | * the race condition where VACUUM sees that all tuples on the page are |
87 | | * visible to everyone, but another backend modifies the page before VACUUM |
88 | | * sets the bit in the visibility map. |
89 | | * |
90 | | * When a bit is set, the LSN of the visibility map page is updated to make |
91 | | * sure that the visibility map update doesn't get written to disk before the |
92 | | * WAL record of the changes that made it possible to set the bit is flushed. |
93 | | * But when a bit is cleared, we don't have to do that because it's always |
94 | | * safe to clear a bit in the map from correctness point of view. |
95 | | * |
96 | | *------------------------------------------------------------------------- |
97 | | */ |
98 | | #include "postgres.h" |
99 | | |
100 | | #include "access/heapam_xlog.h" |
101 | | #include "access/visibilitymap.h" |
102 | | #include "access/xloginsert.h" |
103 | | #include "access/xlogutils.h" |
104 | | #include "miscadmin.h" |
105 | | #include "port/pg_bitutils.h" |
106 | | #include "storage/bufmgr.h" |
107 | | #include "storage/smgr.h" |
108 | | #include "utils/inval.h" |
109 | | #include "utils/rel.h" |
110 | | |
111 | | |
112 | | /*#define TRACE_VISIBILITYMAP */ |
113 | | |
114 | | /* |
115 | | * Size of the bitmap on each visibility map page, in bytes. There's no |
116 | | * extra headers, so the whole page minus the standard page header is |
117 | | * used for the bitmap. |
118 | | */ |
119 | 0 | #define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData)) |
120 | | |
121 | | /* Number of heap blocks we can represent in one byte */ |
122 | 0 | #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK) |
123 | | |
124 | | /* Number of heap blocks we can represent in one visibility map page. */ |
125 | 0 | #define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE) |
126 | | |
127 | | /* Mapping from heap block number to the right bit in the visibility map */ |
128 | 0 | #define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE) |
129 | | #define HEAPBLK_TO_MAPBLOCK_LIMIT(x) \ |
130 | 0 | (((x) + HEAPBLOCKS_PER_PAGE - 1) / HEAPBLOCKS_PER_PAGE) |
131 | 0 | #define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE) |
132 | 0 | #define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK) |
133 | | |
134 | | /* Masks for counting subsets of bits in the visibility map. */ |
135 | 0 | #define VISIBLE_MASK8 (0x55) /* The lower bit of each bit pair */ |
136 | 0 | #define FROZEN_MASK8 (0xaa) /* The upper bit of each bit pair */ |
137 | | |
138 | | /* prototypes for internal routines */ |
139 | | static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend); |
140 | | static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks); |
141 | | |
142 | | /* |
143 | | * visibilitymap_clear - clear specified bits for one page in visibility map |
144 | | * |
145 | | * You must pass a buffer containing the correct map page to this function, |
146 | | * which already needs to be pinned and locked exclusively. |
147 | | * |
148 | | * This function doesn't do any I/O. Returns true if any bits have been |
149 | | * cleared and false otherwise. |
150 | | */ |
151 | | bool |
152 | | visibilitymap_clear(RelFileLocator rlocator, BlockNumber heapBlk, |
153 | | Buffer vmbuf, uint8 flags) |
154 | 0 | { |
155 | 0 | int mapByte = HEAPBLK_TO_MAPBYTE(heapBlk); |
156 | 0 | int mapOffset = HEAPBLK_TO_OFFSET(heapBlk); |
157 | 0 | BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); |
158 | 0 | uint8 mask = flags << mapOffset; |
159 | 0 | Page page; |
160 | 0 | char *map; |
161 | 0 | bool cleared = false; |
162 | | |
163 | | /* Must never clear all_visible bit while leaving all_frozen bit set */ |
164 | 0 | Assert(flags & VISIBILITYMAP_VALID_BITS); |
165 | 0 | Assert(flags != VISIBILITYMAP_ALL_VISIBLE); |
166 | |
|
167 | | #ifdef TRACE_VISIBILITYMAP |
168 | | elog(DEBUG1, "vm_clear %s %d", |
169 | | relpathbackend(rlocator, MyProcNumber, MAIN_FORKNUM).str, |
170 | | heapBlk); |
171 | | #endif |
172 | |
|
173 | 0 | if (!BufferIsValid(vmbuf) || BufferGetBlockNumber(vmbuf) != mapBlock) |
174 | 0 | elog(ERROR, "wrong buffer passed to visibilitymap_clear"); |
175 | | |
176 | 0 | Assert(BufferIsLockedByMeInMode(vmbuf, BUFFER_LOCK_EXCLUSIVE)); |
177 | |
|
178 | 0 | page = BufferGetPage(vmbuf); |
179 | 0 | map = PageGetContents(page); |
180 | |
|
181 | 0 | if (map[mapByte] & mask) |
182 | 0 | { |
183 | 0 | map[mapByte] &= ~mask; |
184 | |
|
185 | 0 | MarkBufferDirty(vmbuf); |
186 | 0 | cleared = true; |
187 | 0 | } |
188 | |
|
189 | 0 | return cleared; |
190 | 0 | } |
191 | | |
192 | | /* |
193 | | * visibilitymap_pin - pin a map page for setting a bit |
194 | | * |
195 | | * Setting a bit in the visibility map is a two-phase operation. First, call |
196 | | * visibilitymap_pin, to pin the visibility map page containing the bit for |
197 | | * the heap page. Because that can require I/O to read the map page, you |
198 | | * shouldn't hold a lock on the heap page while doing that. Then, call |
199 | | * visibilitymap_set to actually set the bit. |
200 | | * |
201 | | * On entry, *vmbuf should be InvalidBuffer or a valid buffer returned by |
202 | | * an earlier call to visibilitymap_pin or visibilitymap_get_status on the same |
203 | | * relation. On return, *vmbuf is a valid buffer with the map page containing |
204 | | * the bit for heapBlk. |
205 | | * |
206 | | * If the page doesn't exist in the map file yet, it is extended. |
207 | | */ |
208 | | void |
209 | | visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf) |
210 | 0 | { |
211 | 0 | BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); |
212 | | |
213 | | /* Reuse the old pinned buffer if possible */ |
214 | 0 | if (BufferIsValid(*vmbuf)) |
215 | 0 | { |
216 | 0 | if (BufferGetBlockNumber(*vmbuf) == mapBlock) |
217 | 0 | return; |
218 | | |
219 | 0 | ReleaseBuffer(*vmbuf); |
220 | 0 | } |
221 | 0 | *vmbuf = vm_readbuf(rel, mapBlock, true); |
222 | 0 | } |
223 | | |
224 | | /* |
225 | | * visibilitymap_pin_ok - do we already have the correct page pinned? |
226 | | * |
227 | | * On entry, vmbuf should be InvalidBuffer or a valid buffer returned by |
228 | | * an earlier call to visibilitymap_pin or visibilitymap_get_status on the same |
229 | | * relation. The return value indicates whether the buffer covers the |
230 | | * given heapBlk. |
231 | | */ |
232 | | bool |
233 | | visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf) |
234 | 0 | { |
235 | 0 | BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); |
236 | |
|
237 | 0 | return BufferIsValid(vmbuf) && BufferGetBlockNumber(vmbuf) == mapBlock; |
238 | 0 | } |
239 | | |
240 | | /* |
241 | | * Set VM (visibility map) flags in the VM block in vmBuf. |
242 | | * |
243 | | * This function is intended for callers that log VM changes together |
244 | | * with the heap page modifications that rendered the page all-visible. |
245 | | * |
246 | | * vmBuf must be pinned and exclusively locked, and it must cover the VM bits |
247 | | * corresponding to heapBlk. |
248 | | * |
249 | | * In normal operation (not recovery), this must be called inside a critical |
250 | | * section that also applies the necessary heap page changes and, if |
251 | | * applicable, emits WAL. |
252 | | * |
253 | | * The caller is responsible for ensuring consistency between the heap page |
254 | | * and the VM page by holding a pin and exclusive lock on the buffer |
255 | | * containing heapBlk. |
256 | | * |
257 | | * rlocator is used only for debugging messages. |
258 | | */ |
259 | | void |
260 | | visibilitymap_set(BlockNumber heapBlk, |
261 | | Buffer vmBuf, uint8 flags, |
262 | | RelFileLocator rlocator) |
263 | 0 | { |
264 | 0 | BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); |
265 | 0 | uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk); |
266 | 0 | uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk); |
267 | 0 | Page page; |
268 | 0 | uint8 *map; |
269 | 0 | uint8 status; |
270 | |
|
271 | | #ifdef TRACE_VISIBILITYMAP |
272 | | elog(DEBUG1, "vm_set flags 0x%02X for %s %d", |
273 | | flags, |
274 | | relpathbackend(rlocator, MyProcNumber, MAIN_FORKNUM).str, |
275 | | heapBlk); |
276 | | #endif |
277 | | |
278 | | /* Call in same critical section where WAL is emitted. */ |
279 | 0 | Assert(InRecovery || CritSectionCount > 0); |
280 | | |
281 | | /* Flags should be valid. Also never clear bits with this function */ |
282 | 0 | Assert((flags & VISIBILITYMAP_VALID_BITS) == flags); |
283 | | |
284 | | /* Must never set all_frozen bit without also setting all_visible bit */ |
285 | 0 | Assert(flags != VISIBILITYMAP_ALL_FROZEN); |
286 | | |
287 | | /* Check that we have the right VM page pinned */ |
288 | 0 | if (!BufferIsValid(vmBuf) || BufferGetBlockNumber(vmBuf) != mapBlock) |
289 | 0 | elog(ERROR, "wrong VM buffer passed to visibilitymap_set"); |
290 | | |
291 | 0 | Assert(BufferIsLockedByMeInMode(vmBuf, BUFFER_LOCK_EXCLUSIVE)); |
292 | |
|
293 | 0 | page = BufferGetPage(vmBuf); |
294 | 0 | map = (uint8 *) PageGetContents(page); |
295 | |
|
296 | 0 | status = (map[mapByte] >> mapOffset) & VISIBILITYMAP_VALID_BITS; |
297 | 0 | if (flags != status) |
298 | 0 | { |
299 | 0 | map[mapByte] |= (flags << mapOffset); |
300 | 0 | MarkBufferDirty(vmBuf); |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | | /* |
305 | | * visibilitymap_get_status - get status of bits |
306 | | * |
307 | | * Are all tuples on heapBlk visible to all or are marked frozen, according |
308 | | * to the visibility map? |
309 | | * |
310 | | * On entry, *vmbuf should be InvalidBuffer or a valid buffer returned by an |
311 | | * earlier call to visibilitymap_pin or visibilitymap_get_status on the same |
312 | | * relation. On return, *vmbuf is a valid buffer with the map page containing |
313 | | * the bit for heapBlk, or InvalidBuffer. The caller is responsible for |
314 | | * releasing *vmbuf after it's done testing and setting bits. |
315 | | * |
316 | | * NOTE: This function is typically called without a lock on the heap page, |
317 | | * so somebody else could change the bit just after we look at it. In fact, |
318 | | * since we don't lock the visibility map page either, it's even possible that |
319 | | * someone else could have changed the bit just before we look at it, but yet |
320 | | * we might see the old value. It is the caller's responsibility to deal with |
321 | | * all concurrency issues! |
322 | | */ |
323 | | uint8 |
324 | | visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf) |
325 | 0 | { |
326 | 0 | BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); |
327 | 0 | uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk); |
328 | 0 | uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk); |
329 | 0 | char *map; |
330 | 0 | uint8 result; |
331 | |
|
332 | | #ifdef TRACE_VISIBILITYMAP |
333 | | elog(DEBUG1, "vm_get_status %s %d", RelationGetRelationName(rel), heapBlk); |
334 | | #endif |
335 | | |
336 | | /* Reuse the old pinned buffer if possible */ |
337 | 0 | if (BufferIsValid(*vmbuf)) |
338 | 0 | { |
339 | 0 | if (BufferGetBlockNumber(*vmbuf) != mapBlock) |
340 | 0 | { |
341 | 0 | ReleaseBuffer(*vmbuf); |
342 | 0 | *vmbuf = InvalidBuffer; |
343 | 0 | } |
344 | 0 | } |
345 | |
|
346 | 0 | if (!BufferIsValid(*vmbuf)) |
347 | 0 | { |
348 | 0 | *vmbuf = vm_readbuf(rel, mapBlock, false); |
349 | 0 | if (!BufferIsValid(*vmbuf)) |
350 | 0 | return (uint8) 0; |
351 | 0 | } |
352 | | |
353 | 0 | map = PageGetContents(BufferGetPage(*vmbuf)); |
354 | | |
355 | | /* |
356 | | * A single byte read is atomic. There could be memory-ordering effects |
357 | | * here, but for performance reasons we make it the caller's job to worry |
358 | | * about that. |
359 | | */ |
360 | 0 | result = ((map[mapByte] >> mapOffset) & VISIBILITYMAP_VALID_BITS); |
361 | 0 | return result; |
362 | 0 | } |
363 | | |
364 | | /* |
365 | | * visibilitymap_count - count number of bits set in visibility map |
366 | | * |
367 | | * Note: we ignore the possibility of race conditions when the table is being |
368 | | * extended concurrently with the call. New pages added to the table aren't |
369 | | * going to be marked all-visible or all-frozen, so they won't affect the result. |
370 | | */ |
371 | | void |
372 | | visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen) |
373 | 0 | { |
374 | 0 | BlockNumber mapBlock; |
375 | 0 | BlockNumber nvisible = 0; |
376 | 0 | BlockNumber nfrozen = 0; |
377 | | |
378 | | /* all_visible must be specified */ |
379 | 0 | Assert(all_visible); |
380 | |
|
381 | 0 | for (mapBlock = 0;; mapBlock++) |
382 | 0 | { |
383 | 0 | Buffer mapBuffer; |
384 | 0 | uint64 *map; |
385 | | |
386 | | /* |
387 | | * Read till we fall off the end of the map. We assume that any extra |
388 | | * bytes in the last page are zeroed, so we don't bother excluding |
389 | | * them from the count. |
390 | | */ |
391 | 0 | mapBuffer = vm_readbuf(rel, mapBlock, false); |
392 | 0 | if (!BufferIsValid(mapBuffer)) |
393 | 0 | break; |
394 | | |
395 | | /* |
396 | | * We choose not to lock the page, since the result is going to be |
397 | | * immediately stale anyway if anyone is concurrently setting or |
398 | | * clearing bits, and we only really need an approximate value. |
399 | | */ |
400 | 0 | map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer)); |
401 | |
|
402 | 0 | nvisible += pg_popcount_masked((const char *) map, MAPSIZE, VISIBLE_MASK8); |
403 | 0 | if (all_frozen) |
404 | 0 | nfrozen += pg_popcount_masked((const char *) map, MAPSIZE, FROZEN_MASK8); |
405 | |
|
406 | 0 | ReleaseBuffer(mapBuffer); |
407 | 0 | } |
408 | |
|
409 | 0 | *all_visible = nvisible; |
410 | 0 | if (all_frozen) |
411 | 0 | *all_frozen = nfrozen; |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * visibilitymap_prepare_truncate - |
416 | | * prepare for truncation of the visibility map |
417 | | * |
418 | | * nheapblocks is the new size of the heap. |
419 | | * |
420 | | * Return the number of blocks of new visibility map. |
421 | | * If it's InvalidBlockNumber, there is nothing to truncate; |
422 | | * otherwise the caller is responsible for calling smgrtruncate() |
423 | | * to truncate the visibility map pages. |
424 | | */ |
425 | | BlockNumber |
426 | | visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks) |
427 | 0 | { |
428 | 0 | BlockNumber newnblocks; |
429 | | |
430 | | /* last remaining block, byte, and bit */ |
431 | 0 | BlockNumber truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks); |
432 | 0 | uint32 truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks); |
433 | 0 | uint8 truncOffset = HEAPBLK_TO_OFFSET(nheapblocks); |
434 | |
|
435 | | #ifdef TRACE_VISIBILITYMAP |
436 | | elog(DEBUG1, "vm_truncate %s %d", RelationGetRelationName(rel), nheapblocks); |
437 | | #endif |
438 | | |
439 | | /* |
440 | | * If no visibility map has been created yet for this relation, there's |
441 | | * nothing to truncate. |
442 | | */ |
443 | 0 | if (!smgrexists(RelationGetSmgr(rel), VISIBILITYMAP_FORKNUM)) |
444 | 0 | return InvalidBlockNumber; |
445 | | |
446 | | /* |
447 | | * Unless the new size is exactly at a visibility map page boundary, the |
448 | | * tail bits in the last remaining map page, representing truncated heap |
449 | | * blocks, need to be cleared. This is not only tidy, but also necessary |
450 | | * because we don't get a chance to clear the bits if the heap is extended |
451 | | * again. |
452 | | */ |
453 | 0 | if (truncByte != 0 || truncOffset != 0) |
454 | 0 | { |
455 | 0 | Buffer mapBuffer; |
456 | 0 | Page page; |
457 | 0 | char *map; |
458 | |
|
459 | 0 | newnblocks = truncBlock + 1; |
460 | |
|
461 | 0 | mapBuffer = vm_readbuf(rel, truncBlock, false); |
462 | 0 | if (!BufferIsValid(mapBuffer)) |
463 | 0 | { |
464 | | /* nothing to do, the file was already smaller */ |
465 | 0 | return InvalidBlockNumber; |
466 | 0 | } |
467 | | |
468 | 0 | page = BufferGetPage(mapBuffer); |
469 | 0 | map = PageGetContents(page); |
470 | |
|
471 | 0 | LockBuffer(mapBuffer, BUFFER_LOCK_EXCLUSIVE); |
472 | | |
473 | | /* NO EREPORT(ERROR) from here till changes are logged */ |
474 | 0 | START_CRIT_SECTION(); |
475 | | |
476 | | /* Clear out the unwanted bytes. */ |
477 | 0 | MemSet(&map[truncByte + 1], 0, MAPSIZE - (truncByte + 1)); |
478 | | |
479 | | /*---- |
480 | | * Mask out the unwanted bits of the last remaining byte. |
481 | | * |
482 | | * ((1 << 0) - 1) = 00000000 |
483 | | * ((1 << 1) - 1) = 00000001 |
484 | | * ... |
485 | | * ((1 << 6) - 1) = 00111111 |
486 | | * ((1 << 7) - 1) = 01111111 |
487 | | *---- |
488 | | */ |
489 | 0 | map[truncByte] &= (1 << truncOffset) - 1; |
490 | | |
491 | | /* |
492 | | * Truncation of a relation is WAL-logged at a higher-level, and we |
493 | | * will be called at WAL replay. But if checksums are enabled, we need |
494 | | * to still write a WAL record to protect against a torn page, if the |
495 | | * page is flushed to disk before the truncation WAL record. We cannot |
496 | | * use MarkBufferDirtyHint here, because that will not dirty the page |
497 | | * during recovery. |
498 | | */ |
499 | 0 | MarkBufferDirty(mapBuffer); |
500 | 0 | if (!InRecovery && RelationNeedsWAL(rel) && XLogHintBitIsNeeded()) |
501 | 0 | log_newpage_buffer(mapBuffer, false); |
502 | |
|
503 | 0 | END_CRIT_SECTION(); |
504 | |
|
505 | 0 | UnlockReleaseBuffer(mapBuffer); |
506 | 0 | } |
507 | 0 | else |
508 | 0 | newnblocks = truncBlock; |
509 | | |
510 | 0 | if (smgrnblocks(RelationGetSmgr(rel), VISIBILITYMAP_FORKNUM) <= newnblocks) |
511 | 0 | { |
512 | | /* nothing to do, the file was already smaller than requested size */ |
513 | 0 | return InvalidBlockNumber; |
514 | 0 | } |
515 | | |
516 | 0 | return newnblocks; |
517 | 0 | } |
518 | | |
519 | | /* |
520 | | * visibilitymap_truncation_length - |
521 | | * compute truncation length for visibility map |
522 | | * |
523 | | * Given a proposed truncation length for the main fork, compute the |
524 | | * correct truncation length for the visibility map. Should return the |
525 | | * same answer as visibilitymap_prepare_truncate(), but without modifying |
526 | | * anything. |
527 | | */ |
528 | | BlockNumber |
529 | | visibilitymap_truncation_length(BlockNumber nheapblocks) |
530 | 0 | { |
531 | 0 | return HEAPBLK_TO_MAPBLOCK_LIMIT(nheapblocks); |
532 | 0 | } |
533 | | |
534 | | /* |
535 | | * Read a visibility map page. |
536 | | * |
537 | | * If the page doesn't exist, InvalidBuffer is returned, or if 'extend' is |
538 | | * true, the visibility map file is extended. |
539 | | */ |
540 | | static Buffer |
541 | | vm_readbuf(Relation rel, BlockNumber blkno, bool extend) |
542 | 0 | { |
543 | 0 | Buffer buf; |
544 | 0 | SMgrRelation reln; |
545 | | |
546 | | /* |
547 | | * Caution: re-using this smgr pointer could fail if the relcache entry |
548 | | * gets closed. It's safe as long as we only do smgr-level operations |
549 | | * between here and the last use of the pointer. |
550 | | */ |
551 | 0 | reln = RelationGetSmgr(rel); |
552 | | |
553 | | /* |
554 | | * If we haven't cached the size of the visibility map fork yet, check it |
555 | | * first. |
556 | | */ |
557 | 0 | if (reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] == InvalidBlockNumber) |
558 | 0 | { |
559 | 0 | if (smgrexists(reln, VISIBILITYMAP_FORKNUM)) |
560 | 0 | smgrnblocks(reln, VISIBILITYMAP_FORKNUM); |
561 | 0 | else |
562 | 0 | reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] = 0; |
563 | 0 | } |
564 | | |
565 | | /* |
566 | | * For reading we use ZERO_ON_ERROR mode, and initialize the page if |
567 | | * necessary. It's always safe to clear bits, so it's better to clear |
568 | | * corrupt pages than error out. |
569 | | * |
570 | | * We use the same path below to initialize pages when extending the |
571 | | * relation, as a concurrent extension can end up with vm_extend() |
572 | | * returning an already-initialized page. |
573 | | */ |
574 | 0 | if (blkno >= reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM]) |
575 | 0 | { |
576 | 0 | if (extend) |
577 | 0 | buf = vm_extend(rel, blkno + 1); |
578 | 0 | else |
579 | 0 | return InvalidBuffer; |
580 | 0 | } |
581 | 0 | else |
582 | 0 | buf = ReadBufferExtended(rel, VISIBILITYMAP_FORKNUM, blkno, |
583 | 0 | RBM_ZERO_ON_ERROR, NULL); |
584 | | |
585 | | /* |
586 | | * Initializing the page when needed is trickier than it looks, because of |
587 | | * the possibility of multiple backends doing this concurrently, and our |
588 | | * desire to not uselessly take the buffer lock in the normal path where |
589 | | * the page is OK. We must take the lock to initialize the page, so |
590 | | * recheck page newness after we have the lock, in case someone else |
591 | | * already did it. Also, because we initially check PageIsNew with no |
592 | | * lock, it's possible to fall through and return the buffer while someone |
593 | | * else is still initializing the page (i.e., we might see pd_upper as set |
594 | | * but other page header fields are still zeroes). This is harmless for |
595 | | * callers that will take a buffer lock themselves, but some callers |
596 | | * inspect the page without any lock at all. The latter is OK only so |
597 | | * long as it doesn't depend on the page header having correct contents. |
598 | | * Current usage is safe because PageGetContents() does not require that. |
599 | | */ |
600 | 0 | if (PageIsNew(BufferGetPage(buf))) |
601 | 0 | { |
602 | 0 | LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); |
603 | 0 | if (PageIsNew(BufferGetPage(buf))) |
604 | 0 | PageInit(BufferGetPage(buf), BLCKSZ, 0); |
605 | 0 | LockBuffer(buf, BUFFER_LOCK_UNLOCK); |
606 | 0 | } |
607 | 0 | return buf; |
608 | 0 | } |
609 | | |
610 | | /* |
611 | | * Ensure that the visibility map fork is at least vm_nblocks long, extending |
612 | | * it if necessary with zeroed pages. |
613 | | */ |
614 | | static Buffer |
615 | | vm_extend(Relation rel, BlockNumber vm_nblocks) |
616 | 0 | { |
617 | 0 | Buffer buf; |
618 | |
|
619 | 0 | buf = ExtendBufferedRelTo(BMR_REL(rel), VISIBILITYMAP_FORKNUM, NULL, |
620 | 0 | EB_CREATE_FORK_IF_NEEDED | |
621 | 0 | EB_CLEAR_SIZE_CACHE, |
622 | 0 | vm_nblocks, |
623 | 0 | RBM_ZERO_ON_ERROR); |
624 | | |
625 | | /* |
626 | | * Send a shared-inval message to force other backends to close any smgr |
627 | | * references they may have for this rel, which we are about to change. |
628 | | * This is a useful optimization because it means that backends don't have |
629 | | * to keep checking for creation or extension of the file, which happens |
630 | | * infrequently. |
631 | | */ |
632 | 0 | CacheInvalidateSmgr(RelationGetSmgr(rel)->smgr_rlocator); |
633 | |
|
634 | 0 | return buf; |
635 | 0 | } |