Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * hash_xlog.c
4 : : * WAL replay logic for hash index.
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/hash/hash_xlog.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/bufmask.h"
18 : : #include "access/hash.h"
19 : : #include "access/hash_xlog.h"
20 : : #include "access/xlogutils.h"
21 : : #include "storage/standby.h"
22 : :
23 : : /*
24 : : * replay a hash index meta page
25 : : */
26 : : static void
27 : 30 : hash_xlog_init_meta_page(XLogReaderState *record)
28 : : {
29 : 30 : XLogRecPtr lsn = record->EndRecPtr;
30 : : Page page;
31 : : Buffer metabuf;
32 : :
33 : 30 : xl_hash_init_meta_page *xlrec = (xl_hash_init_meta_page *) XLogRecGetData(record);
34 : :
35 : : /* create the index' metapage */
36 : 30 : metabuf = XLogInitBufferForRedo(record, 0);
37 : : Assert(BufferIsValid(metabuf));
38 : 30 : _hash_init_metabuffer(metabuf, xlrec->num_tuples, xlrec->procid,
39 : 30 : xlrec->ffactor, true);
40 : 30 : page = BufferGetPage(metabuf);
41 : 30 : PageSetLSN(page, lsn);
42 : 30 : MarkBufferDirty(metabuf);
43 : 30 : XLogFlushBufferForRedoIfInit(record, 0, metabuf);
44 : :
45 : : /* all done */
46 : 30 : UnlockReleaseBuffer(metabuf);
47 : 30 : }
48 : :
49 : : /*
50 : : * replay a hash index bitmap page
51 : : */
52 : : static void
53 : 30 : hash_xlog_init_bitmap_page(XLogReaderState *record)
54 : : {
55 : 30 : XLogRecPtr lsn = record->EndRecPtr;
56 : : Buffer bitmapbuf;
57 : : Buffer metabuf;
58 : : Page page;
59 : : HashMetaPage metap;
60 : : uint32 num_buckets;
61 : :
62 : 30 : xl_hash_init_bitmap_page *xlrec = (xl_hash_init_bitmap_page *) XLogRecGetData(record);
63 : :
64 : : /*
65 : : * Initialize bitmap page
66 : : */
67 : 30 : bitmapbuf = XLogInitBufferForRedo(record, 0);
68 : 30 : _hash_initbitmapbuffer(bitmapbuf, xlrec->bmsize, true);
69 : 30 : PageSetLSN(BufferGetPage(bitmapbuf), lsn);
70 : 30 : MarkBufferDirty(bitmapbuf);
71 : 30 : XLogFlushBufferForRedoIfInit(record, 0, bitmapbuf);
72 : 30 : UnlockReleaseBuffer(bitmapbuf);
73 : :
74 : : /* add the new bitmap page to the metapage's list of bitmaps */
75 [ + - ]: 30 : if (XLogReadBufferForRedo(record, 1, &metabuf) == BLK_NEEDS_REDO)
76 : : {
77 : : /*
78 : : * Note: in normal operation, we'd update the metapage while still
79 : : * holding lock on the bitmap page. But during replay it's not
80 : : * necessary to hold that lock, since nobody can see it yet; the
81 : : * creating transaction hasn't yet committed.
82 : : */
83 : 30 : page = BufferGetPage(metabuf);
84 : 30 : metap = HashPageGetMeta(page);
85 : :
86 : 30 : num_buckets = metap->hashm_maxbucket + 1;
87 : 30 : metap->hashm_mapp[metap->hashm_nmaps] = num_buckets + 1;
88 : 30 : metap->hashm_nmaps++;
89 : :
90 : 30 : PageSetLSN(page, lsn);
91 : 30 : MarkBufferDirty(metabuf);
92 : 30 : XLogFlushBufferForRedoIfInit(record, 1, metabuf);
93 : : }
94 [ + - ]: 30 : if (BufferIsValid(metabuf))
95 : 30 : UnlockReleaseBuffer(metabuf);
96 : 30 : }
97 : :
98 : : /*
99 : : * replay a hash index insert without split
100 : : */
101 : : static void
102 : 121315 : hash_xlog_insert(XLogReaderState *record)
103 : : {
104 : : HashMetaPage metap;
105 : 121315 : XLogRecPtr lsn = record->EndRecPtr;
106 : 121315 : xl_hash_insert *xlrec = (xl_hash_insert *) XLogRecGetData(record);
107 : : Buffer buffer;
108 : : Page page;
109 : :
110 [ + + ]: 121315 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
111 : : {
112 : : Size datalen;
113 : 120049 : char *datapos = XLogRecGetBlockData(record, 0, &datalen);
114 : :
115 : 120049 : page = BufferGetPage(buffer);
116 : :
117 [ - + ]: 120049 : if (PageAddItem(page, datapos, datalen, xlrec->offnum, false, false) == InvalidOffsetNumber)
118 [ # # ]: 0 : elog(PANIC, "hash_xlog_insert: failed to add item");
119 : :
120 : 120049 : PageSetLSN(page, lsn);
121 : 120049 : MarkBufferDirty(buffer);
122 : : }
123 [ + - ]: 121315 : if (BufferIsValid(buffer))
124 : 121315 : UnlockReleaseBuffer(buffer);
125 : :
126 [ + + ]: 121315 : if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
127 : : {
128 : : /*
129 : : * Note: in normal operation, we'd update the metapage while still
130 : : * holding lock on the page we inserted into. But during replay it's
131 : : * not necessary to hold that lock, since no other index updates can
132 : : * be happening concurrently.
133 : : */
134 : 121290 : page = BufferGetPage(buffer);
135 : 121290 : metap = HashPageGetMeta(page);
136 : 121290 : metap->hashm_ntuples += 1;
137 : :
138 : 121290 : PageSetLSN(page, lsn);
139 : 121290 : MarkBufferDirty(buffer);
140 : : }
141 [ + - ]: 121315 : if (BufferIsValid(buffer))
142 : 121315 : UnlockReleaseBuffer(buffer);
143 : 121315 : }
144 : :
145 : : /*
146 : : * replay addition of overflow page for hash index
147 : : */
148 : : static void
149 : 70 : hash_xlog_add_ovfl_page(XLogReaderState *record)
150 : : {
151 : 70 : XLogRecPtr lsn = record->EndRecPtr;
152 : 70 : xl_hash_add_ovfl_page *xlrec = (xl_hash_add_ovfl_page *) XLogRecGetData(record);
153 : : Buffer leftbuf;
154 : : Buffer ovflbuf;
155 : : Buffer metabuf;
156 : : BlockNumber leftblk;
157 : : BlockNumber rightblk;
158 : 70 : BlockNumber newmapblk = InvalidBlockNumber;
159 : : Page ovflpage;
160 : : HashPageOpaque ovflopaque;
161 : : uint32 *num_bucket;
162 : : char *data;
163 : : Size datalen PG_USED_FOR_ASSERTS_ONLY;
164 : 70 : bool new_bmpage = false;
165 : :
166 : 70 : XLogRecGetBlockTag(record, 0, NULL, NULL, &rightblk);
167 : 70 : XLogRecGetBlockTag(record, 1, NULL, NULL, &leftblk);
168 : :
169 : 70 : ovflbuf = XLogInitBufferForRedo(record, 0);
170 : : Assert(BufferIsValid(ovflbuf));
171 : :
172 : 70 : data = XLogRecGetBlockData(record, 0, &datalen);
173 : 70 : num_bucket = (uint32 *) data;
174 : : Assert(datalen == sizeof(uint32));
175 : 70 : _hash_initbuf(ovflbuf, InvalidBlockNumber, *num_bucket, LH_OVERFLOW_PAGE,
176 : : true);
177 : : /* update backlink */
178 : 70 : ovflpage = BufferGetPage(ovflbuf);
179 : 70 : ovflopaque = HashPageGetOpaque(ovflpage);
180 : 70 : ovflopaque->hasho_prevblkno = leftblk;
181 : :
182 : 70 : PageSetLSN(ovflpage, lsn);
183 : 70 : MarkBufferDirty(ovflbuf);
184 : :
185 [ + - ]: 70 : if (XLogReadBufferForRedo(record, 1, &leftbuf) == BLK_NEEDS_REDO)
186 : : {
187 : : Page leftpage;
188 : : HashPageOpaque leftopaque;
189 : :
190 : 70 : leftpage = BufferGetPage(leftbuf);
191 : 70 : leftopaque = HashPageGetOpaque(leftpage);
192 : 70 : leftopaque->hasho_nextblkno = rightblk;
193 : :
194 : 70 : PageSetLSN(leftpage, lsn);
195 : 70 : MarkBufferDirty(leftbuf);
196 : : }
197 : :
198 [ + - ]: 70 : if (BufferIsValid(leftbuf))
199 : 70 : UnlockReleaseBuffer(leftbuf);
200 : 70 : UnlockReleaseBuffer(ovflbuf);
201 : :
202 : : /*
203 : : * Note: in normal operation, we'd update the bitmap and meta page while
204 : : * still holding lock on the overflow pages. But during replay it's not
205 : : * necessary to hold those locks, since no other index updates can be
206 : : * happening concurrently.
207 : : */
208 [ + - + + ]: 70 : if (XLogRecHasBlockRef(record, 2))
209 : : {
210 : : Buffer mapbuffer;
211 : :
212 [ + + ]: 12 : if (XLogReadBufferForRedo(record, 2, &mapbuffer) == BLK_NEEDS_REDO)
213 : : {
214 : 8 : Page mappage = BufferGetPage(mapbuffer);
215 : 8 : uint32 *freep = NULL;
216 : : uint32 *bitmap_page_bit;
217 : :
218 : 8 : freep = HashPageGetBitmap(mappage);
219 : :
220 : 8 : data = XLogRecGetBlockData(record, 2, &datalen);
221 : 8 : bitmap_page_bit = (uint32 *) data;
222 : :
223 : 8 : SETBIT(freep, *bitmap_page_bit);
224 : :
225 : 8 : PageSetLSN(mappage, lsn);
226 : 8 : MarkBufferDirty(mapbuffer);
227 : : }
228 [ + - ]: 12 : if (BufferIsValid(mapbuffer))
229 : 12 : UnlockReleaseBuffer(mapbuffer);
230 : : }
231 : :
232 [ + - - + ]: 70 : if (XLogRecHasBlockRef(record, 3))
233 : : {
234 : : Buffer newmapbuf;
235 : :
236 : 0 : newmapbuf = XLogInitBufferForRedo(record, 3);
237 : :
238 : 0 : _hash_initbitmapbuffer(newmapbuf, xlrec->bmsize, true);
239 : :
240 : 0 : new_bmpage = true;
241 : 0 : newmapblk = BufferGetBlockNumber(newmapbuf);
242 : :
243 : 0 : MarkBufferDirty(newmapbuf);
244 : 0 : PageSetLSN(BufferGetPage(newmapbuf), lsn);
245 : :
246 : 0 : UnlockReleaseBuffer(newmapbuf);
247 : : }
248 : :
249 [ + - ]: 70 : if (XLogReadBufferForRedo(record, 4, &metabuf) == BLK_NEEDS_REDO)
250 : : {
251 : : HashMetaPage metap;
252 : : Page page;
253 : : uint32 *firstfree_ovflpage;
254 : :
255 : 70 : data = XLogRecGetBlockData(record, 4, &datalen);
256 : 70 : firstfree_ovflpage = (uint32 *) data;
257 : :
258 : 70 : page = BufferGetPage(metabuf);
259 : 70 : metap = HashPageGetMeta(page);
260 : 70 : metap->hashm_firstfree = *firstfree_ovflpage;
261 : :
262 [ + + ]: 70 : if (!xlrec->bmpage_found)
263 : : {
264 : 58 : metap->hashm_spares[metap->hashm_ovflpoint]++;
265 : :
266 [ - + ]: 58 : if (new_bmpage)
267 : : {
268 : : Assert(BlockNumberIsValid(newmapblk));
269 : :
270 : 0 : metap->hashm_mapp[metap->hashm_nmaps] = newmapblk;
271 : 0 : metap->hashm_nmaps++;
272 : 0 : metap->hashm_spares[metap->hashm_ovflpoint]++;
273 : : }
274 : : }
275 : :
276 : 70 : PageSetLSN(page, lsn);
277 : 70 : MarkBufferDirty(metabuf);
278 : : }
279 [ + - ]: 70 : if (BufferIsValid(metabuf))
280 : 70 : UnlockReleaseBuffer(metabuf);
281 : 70 : }
282 : :
283 : : /*
284 : : * replay allocation of page for split operation
285 : : */
286 : : static void
287 : 221 : hash_xlog_split_allocate_page(XLogReaderState *record)
288 : : {
289 : 221 : XLogRecPtr lsn = record->EndRecPtr;
290 : 221 : xl_hash_split_allocate_page *xlrec = (xl_hash_split_allocate_page *) XLogRecGetData(record);
291 : : Buffer oldbuf;
292 : : Buffer newbuf;
293 : : Buffer metabuf;
294 : : XLogRedoAction action;
295 : :
296 : : /*
297 : : * To be consistent with normal operation, here we take cleanup locks on
298 : : * both the old and new buckets even though there can't be any concurrent
299 : : * inserts.
300 : : */
301 : :
302 : : /* replay the record for old bucket */
303 : 221 : action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &oldbuf);
304 : :
305 : : /*
306 : : * Note that we still update the page even if it was restored from a full
307 : : * page image, because the special space is not included in the image.
308 : : */
309 [ + + + - ]: 221 : if (action == BLK_NEEDS_REDO || action == BLK_RESTORED)
310 : : {
311 : : Page oldpage;
312 : : HashPageOpaque oldopaque;
313 : :
314 : 221 : oldpage = BufferGetPage(oldbuf);
315 : 221 : oldopaque = HashPageGetOpaque(oldpage);
316 : :
317 : 221 : oldopaque->hasho_flag = xlrec->old_bucket_flag;
318 : 221 : oldopaque->hasho_prevblkno = xlrec->new_bucket;
319 : :
320 : 221 : PageSetLSN(oldpage, lsn);
321 : 221 : MarkBufferDirty(oldbuf);
322 : : }
323 : :
324 : : /* replay the record for new bucket */
325 : 221 : XLogReadBufferForRedoExtended(record, 1, RBM_ZERO_AND_CLEANUP_LOCK, true,
326 : : &newbuf);
327 : 221 : _hash_initbuf(newbuf, xlrec->new_bucket, xlrec->new_bucket,
328 : 221 : xlrec->new_bucket_flag, true);
329 : 221 : MarkBufferDirty(newbuf);
330 : 221 : PageSetLSN(BufferGetPage(newbuf), lsn);
331 : :
332 : : /*
333 : : * We can release the lock on old bucket early as well but doing here to
334 : : * consistent with normal operation.
335 : : */
336 [ + - ]: 221 : if (BufferIsValid(oldbuf))
337 : 221 : UnlockReleaseBuffer(oldbuf);
338 [ + - ]: 221 : if (BufferIsValid(newbuf))
339 : 221 : UnlockReleaseBuffer(newbuf);
340 : :
341 : : /*
342 : : * Note: in normal operation, we'd update the meta page while still
343 : : * holding lock on the old and new bucket pages. But during replay it's
344 : : * not necessary to hold those locks, since no other bucket splits can be
345 : : * happening concurrently.
346 : : */
347 : :
348 : : /* replay the record for metapage changes */
349 [ + - ]: 221 : if (XLogReadBufferForRedo(record, 2, &metabuf) == BLK_NEEDS_REDO)
350 : : {
351 : : Page page;
352 : : HashMetaPage metap;
353 : : Size datalen;
354 : : char *data;
355 : : uint32 *uidata;
356 : : int uidatacount;
357 : :
358 : 221 : page = BufferGetPage(metabuf);
359 : 221 : metap = HashPageGetMeta(page);
360 : 221 : metap->hashm_maxbucket = xlrec->new_bucket;
361 : :
362 : 221 : data = XLogRecGetBlockData(record, 2, &datalen);
363 : :
364 : : /*
365 : : * This cast is ok because XLogRecGetBlockData() returns a MAXALIGNed
366 : : * buffer.
367 : : */
368 : 221 : uidata = (uint32 *) data;
369 : 221 : uidatacount = 0;
370 : :
371 [ + + ]: 221 : if (xlrec->flags & XLH_SPLIT_META_UPDATE_MASKS)
372 : : {
373 : 3 : uint32 lowmask = uidata[uidatacount++];
374 : 3 : uint32 highmask = uidata[uidatacount++];
375 : :
376 : : /* update metapage */
377 : 3 : metap->hashm_lowmask = lowmask;
378 : 3 : metap->hashm_highmask = highmask;
379 : : }
380 : :
381 [ + + ]: 221 : if (xlrec->flags & XLH_SPLIT_META_UPDATE_SPLITPOINT)
382 : : {
383 : 9 : uint32 ovflpoint = uidata[uidatacount++];
384 : 9 : uint32 ovflpages = uidata[uidatacount++];
385 : :
386 : : /* update metapage */
387 : 9 : metap->hashm_ovflpoint = ovflpoint;
388 : 9 : metap->hashm_spares[ovflpoint] = ovflpages;
389 : : }
390 : :
391 : 221 : MarkBufferDirty(metabuf);
392 : 221 : PageSetLSN(BufferGetPage(metabuf), lsn);
393 : : }
394 : :
395 [ + - ]: 221 : if (BufferIsValid(metabuf))
396 : 221 : UnlockReleaseBuffer(metabuf);
397 : 221 : }
398 : :
399 : : /*
400 : : * replay of split operation
401 : : */
402 : : static void
403 : 234 : hash_xlog_split_page(XLogReaderState *record)
404 : : {
405 : : Buffer buf;
406 : :
407 [ - + ]: 234 : if (XLogReadBufferForRedo(record, 0, &buf) != BLK_RESTORED)
408 [ # # ]: 0 : elog(ERROR, "Hash split record did not contain a full-page image");
409 : :
410 : 234 : UnlockReleaseBuffer(buf);
411 : 234 : }
412 : :
413 : : /*
414 : : * replay completion of split operation
415 : : */
416 : : static void
417 : 221 : hash_xlog_split_complete(XLogReaderState *record)
418 : : {
419 : 221 : XLogRecPtr lsn = record->EndRecPtr;
420 : 221 : xl_hash_split_complete *xlrec = (xl_hash_split_complete *) XLogRecGetData(record);
421 : : Buffer oldbuf;
422 : : Buffer newbuf;
423 : : XLogRedoAction action;
424 : :
425 : : /* replay the record for old bucket */
426 : 221 : action = XLogReadBufferForRedo(record, 0, &oldbuf);
427 : :
428 : : /*
429 : : * Note that we still update the page even if it was restored from a full
430 : : * page image, because the bucket flag is not included in the image.
431 : : */
432 [ - + - - ]: 221 : if (action == BLK_NEEDS_REDO || action == BLK_RESTORED)
433 : : {
434 : : Page oldpage;
435 : : HashPageOpaque oldopaque;
436 : :
437 : 221 : oldpage = BufferGetPage(oldbuf);
438 : 221 : oldopaque = HashPageGetOpaque(oldpage);
439 : :
440 : 221 : oldopaque->hasho_flag = xlrec->old_bucket_flag;
441 : :
442 : 221 : PageSetLSN(oldpage, lsn);
443 : 221 : MarkBufferDirty(oldbuf);
444 : : }
445 [ + - ]: 221 : if (BufferIsValid(oldbuf))
446 : 221 : UnlockReleaseBuffer(oldbuf);
447 : :
448 : : /* replay the record for new bucket */
449 : 221 : action = XLogReadBufferForRedo(record, 1, &newbuf);
450 : :
451 : : /*
452 : : * Note that we still update the page even if it was restored from a full
453 : : * page image, because the bucket flag is not included in the image.
454 : : */
455 [ - + - - ]: 221 : if (action == BLK_NEEDS_REDO || action == BLK_RESTORED)
456 : : {
457 : : Page newpage;
458 : : HashPageOpaque nopaque;
459 : :
460 : 221 : newpage = BufferGetPage(newbuf);
461 : 221 : nopaque = HashPageGetOpaque(newpage);
462 : :
463 : 221 : nopaque->hasho_flag = xlrec->new_bucket_flag;
464 : :
465 : 221 : PageSetLSN(newpage, lsn);
466 : 221 : MarkBufferDirty(newbuf);
467 : : }
468 [ + - ]: 221 : if (BufferIsValid(newbuf))
469 : 221 : UnlockReleaseBuffer(newbuf);
470 : 221 : }
471 : :
472 : : /*
473 : : * replay move of page contents for squeeze operation of hash index
474 : : */
475 : : static void
476 : 1 : hash_xlog_move_page_contents(XLogReaderState *record)
477 : : {
478 : 1 : XLogRecPtr lsn = record->EndRecPtr;
479 : 1 : xl_hash_move_page_contents *xldata = (xl_hash_move_page_contents *) XLogRecGetData(record);
480 : 1 : Buffer bucketbuf = InvalidBuffer;
481 : 1 : Buffer writebuf = InvalidBuffer;
482 : 1 : Buffer deletebuf = InvalidBuffer;
483 : : XLogRedoAction action;
484 : :
485 : : /*
486 : : * Ensure we have a cleanup lock on primary bucket page before we start
487 : : * with the actual replay operation. This is to ensure that neither a
488 : : * scan can start nor a scan can be already-in-progress during the replay
489 : : * of this operation. If we allow scans during this operation, then they
490 : : * can miss some records or show the same record multiple times.
491 : : */
492 [ + - ]: 1 : if (xldata->is_prim_bucket_same_wrt)
493 : 1 : action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &writebuf);
494 : : else
495 : : {
496 : : /*
497 : : * we don't care for return value as the purpose of reading bucketbuf
498 : : * is to ensure a cleanup lock on primary bucket page.
499 : : */
500 : 0 : (void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
501 : :
502 : 0 : action = XLogReadBufferForRedo(record, 1, &writebuf);
503 : : }
504 : :
505 : : /* replay the record for adding entries in overflow buffer */
506 [ + - ]: 1 : if (action == BLK_NEEDS_REDO)
507 : : {
508 : : Page writepage;
509 : : char *begin;
510 : : char *data;
511 : : Size datalen;
512 : 1 : uint16 ninserted = 0;
513 : :
514 : 1 : data = begin = XLogRecGetBlockData(record, 1, &datalen);
515 : :
516 : 1 : writepage = BufferGetPage(writebuf);
517 : :
518 [ + - ]: 1 : if (xldata->ntups > 0)
519 : : {
520 : 1 : OffsetNumber *towrite = (OffsetNumber *) data;
521 : :
522 : 1 : data += sizeof(OffsetNumber) * xldata->ntups;
523 : :
524 [ + + ]: 343 : while (data - begin < datalen)
525 : : {
526 : 342 : IndexTuple itup = (IndexTuple) data;
527 : : Size itemsz;
528 : : OffsetNumber l;
529 : :
530 : 342 : itemsz = IndexTupleSize(itup);
531 : 342 : itemsz = MAXALIGN(itemsz);
532 : :
533 : 342 : data += itemsz;
534 : :
535 : 342 : l = PageAddItem(writepage, itup, itemsz, towrite[ninserted], false, false);
536 [ - + ]: 342 : if (l == InvalidOffsetNumber)
537 [ # # ]: 0 : elog(ERROR, "hash_xlog_move_page_contents: failed to add item to hash index page, size %zu bytes", itemsz);
538 : :
539 : 342 : ninserted++;
540 : : }
541 : : }
542 : :
543 : : /*
544 : : * number of tuples inserted must be same as requested in REDO record.
545 : : */
546 : : Assert(ninserted == xldata->ntups);
547 : :
548 : 1 : PageSetLSN(writepage, lsn);
549 : 1 : MarkBufferDirty(writebuf);
550 : : }
551 : :
552 : : /* replay the record for deleting entries from overflow buffer */
553 [ + - ]: 1 : if (XLogReadBufferForRedo(record, 2, &deletebuf) == BLK_NEEDS_REDO)
554 : : {
555 : : Page page;
556 : : char *ptr;
557 : : Size len;
558 : :
559 : 1 : ptr = XLogRecGetBlockData(record, 2, &len);
560 : :
561 : 1 : page = BufferGetPage(deletebuf);
562 : :
563 [ + - ]: 1 : if (len > 0)
564 : : {
565 : : OffsetNumber *unused;
566 : : OffsetNumber *unend;
567 : :
568 : 1 : unused = (OffsetNumber *) ptr;
569 : 1 : unend = (OffsetNumber *) (ptr + len);
570 : :
571 [ + - ]: 1 : if ((unend - unused) > 0)
572 : 1 : PageIndexMultiDelete(page, unused, unend - unused);
573 : : }
574 : :
575 : 1 : PageSetLSN(page, lsn);
576 : 1 : MarkBufferDirty(deletebuf);
577 : : }
578 : :
579 : : /*
580 : : * Replay is complete, now we can release the buffers. We release locks at
581 : : * end of replay operation to ensure that we hold lock on primary bucket
582 : : * page till end of operation. We can optimize by releasing the lock on
583 : : * write buffer as soon as the operation for same is complete, if it is
584 : : * not same as primary bucket page, but that doesn't seem to be worth
585 : : * complicating the code.
586 : : */
587 [ + - ]: 1 : if (BufferIsValid(deletebuf))
588 : 1 : UnlockReleaseBuffer(deletebuf);
589 : :
590 [ + - ]: 1 : if (BufferIsValid(writebuf))
591 : 1 : UnlockReleaseBuffer(writebuf);
592 : :
593 [ - + ]: 1 : if (BufferIsValid(bucketbuf))
594 : 0 : UnlockReleaseBuffer(bucketbuf);
595 : 1 : }
596 : :
597 : : /*
598 : : * replay squeeze page operation of hash index
599 : : */
600 : : static void
601 : 32 : hash_xlog_squeeze_page(XLogReaderState *record)
602 : : {
603 : 32 : XLogRecPtr lsn = record->EndRecPtr;
604 : 32 : xl_hash_squeeze_page *xldata = (xl_hash_squeeze_page *) XLogRecGetData(record);
605 : 32 : Buffer bucketbuf = InvalidBuffer;
606 : 32 : Buffer writebuf = InvalidBuffer;
607 : : Buffer ovflbuf;
608 : 32 : Buffer prevbuf = InvalidBuffer;
609 : : Buffer mapbuf;
610 : : XLogRedoAction action;
611 : :
612 : : /*
613 : : * Ensure we have a cleanup lock on primary bucket page before we start
614 : : * with the actual replay operation. This is to ensure that neither a
615 : : * scan can start nor a scan can be already-in-progress during the replay
616 : : * of this operation. If we allow scans during this operation, then they
617 : : * can miss some records or show the same record multiple times.
618 : : */
619 [ + + ]: 32 : if (xldata->is_prim_bucket_same_wrt)
620 : 24 : action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &writebuf);
621 : : else
622 : : {
623 : : /*
624 : : * we don't care for return value as the purpose of reading bucketbuf
625 : : * is to ensure a cleanup lock on primary bucket page.
626 : : */
627 : 8 : (void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
628 : :
629 [ + + + + ]: 8 : if (xldata->ntups > 0 || xldata->is_prev_bucket_same_wrt)
630 : 7 : action = XLogReadBufferForRedo(record, 1, &writebuf);
631 : : else
632 : 1 : action = BLK_NOTFOUND;
633 : : }
634 : :
635 : : /* replay the record for adding entries in overflow buffer */
636 [ + + ]: 32 : if (action == BLK_NEEDS_REDO)
637 : : {
638 : : Page writepage;
639 : : char *begin;
640 : : char *data;
641 : : Size datalen;
642 : 29 : uint16 ninserted = 0;
643 : 29 : bool mod_wbuf = false;
644 : :
645 : 29 : data = begin = XLogRecGetBlockData(record, 1, &datalen);
646 : :
647 : 29 : writepage = BufferGetPage(writebuf);
648 : :
649 [ + + ]: 29 : if (xldata->ntups > 0)
650 : : {
651 : 15 : OffsetNumber *towrite = (OffsetNumber *) data;
652 : :
653 : 15 : data += sizeof(OffsetNumber) * xldata->ntups;
654 : :
655 [ + + ]: 627 : while (data - begin < datalen)
656 : : {
657 : 612 : IndexTuple itup = (IndexTuple) data;
658 : : Size itemsz;
659 : : OffsetNumber l;
660 : :
661 : 612 : itemsz = IndexTupleSize(itup);
662 : 612 : itemsz = MAXALIGN(itemsz);
663 : :
664 : 612 : data += itemsz;
665 : :
666 : 612 : l = PageAddItem(writepage, itup, itemsz, towrite[ninserted], false, false);
667 [ - + ]: 612 : if (l == InvalidOffsetNumber)
668 [ # # ]: 0 : elog(ERROR, "hash_xlog_squeeze_page: failed to add item to hash index page, size %zu bytes", itemsz);
669 : :
670 : 612 : ninserted++;
671 : : }
672 : :
673 : 15 : mod_wbuf = true;
674 : : }
675 : : else
676 : : {
677 : : /*
678 : : * Ensure that the required flags are set when there are no
679 : : * tuples. See _hash_freeovflpage().
680 : : */
681 : : Assert(xldata->is_prim_bucket_same_wrt ||
682 : : xldata->is_prev_bucket_same_wrt);
683 : : }
684 : :
685 : : /*
686 : : * number of tuples inserted must be same as requested in REDO record.
687 : : */
688 : : Assert(ninserted == xldata->ntups);
689 : :
690 : : /*
691 : : * if the page on which are adding tuples is a page previous to freed
692 : : * overflow page, then update its nextblkno.
693 : : */
694 [ + + ]: 29 : if (xldata->is_prev_bucket_same_wrt)
695 : : {
696 : 11 : HashPageOpaque writeopaque = HashPageGetOpaque(writepage);
697 : :
698 : 11 : writeopaque->hasho_nextblkno = xldata->nextblkno;
699 : 11 : mod_wbuf = true;
700 : : }
701 : :
702 : : /* Set LSN and mark writebuf dirty iff it is modified */
703 [ + + ]: 29 : if (mod_wbuf)
704 : : {
705 : 18 : PageSetLSN(writepage, lsn);
706 : 18 : MarkBufferDirty(writebuf);
707 : : }
708 : : }
709 : :
710 : : /* replay the record for initializing overflow buffer */
711 [ - + ]: 32 : if (XLogReadBufferForRedo(record, 2, &ovflbuf) == BLK_NEEDS_REDO)
712 : : {
713 : : Page ovflpage;
714 : : HashPageOpaque ovflopaque;
715 : :
716 : 0 : ovflpage = BufferGetPage(ovflbuf);
717 : :
718 : 0 : _hash_pageinit(ovflpage, BufferGetPageSize(ovflbuf));
719 : :
720 : 0 : ovflopaque = HashPageGetOpaque(ovflpage);
721 : :
722 : 0 : ovflopaque->hasho_prevblkno = InvalidBlockNumber;
723 : 0 : ovflopaque->hasho_nextblkno = InvalidBlockNumber;
724 : 0 : ovflopaque->hasho_bucket = InvalidBucket;
725 : 0 : ovflopaque->hasho_flag = LH_UNUSED_PAGE;
726 : 0 : ovflopaque->hasho_page_id = HASHO_PAGE_ID;
727 : :
728 : 0 : PageSetLSN(ovflpage, lsn);
729 : 0 : MarkBufferDirty(ovflbuf);
730 : : }
731 [ + - ]: 32 : if (BufferIsValid(ovflbuf))
732 : 32 : UnlockReleaseBuffer(ovflbuf);
733 : :
734 : : /* replay the record for page previous to the freed overflow page */
735 [ + + + + ]: 53 : if (!xldata->is_prev_bucket_same_wrt &&
736 : 21 : XLogReadBufferForRedo(record, 3, &prevbuf) == BLK_NEEDS_REDO)
737 : : {
738 : 20 : Page prevpage = BufferGetPage(prevbuf);
739 : 20 : HashPageOpaque prevopaque = HashPageGetOpaque(prevpage);
740 : :
741 : 20 : prevopaque->hasho_nextblkno = xldata->nextblkno;
742 : :
743 : 20 : PageSetLSN(prevpage, lsn);
744 : 20 : MarkBufferDirty(prevbuf);
745 : : }
746 [ + + ]: 32 : if (BufferIsValid(prevbuf))
747 : 21 : UnlockReleaseBuffer(prevbuf);
748 : :
749 : : /* replay the record for page next to the freed overflow page */
750 [ + - - + ]: 32 : if (XLogRecHasBlockRef(record, 4))
751 : : {
752 : : Buffer nextbuf;
753 : :
754 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 4, &nextbuf) == BLK_NEEDS_REDO)
755 : : {
756 : 0 : Page nextpage = BufferGetPage(nextbuf);
757 : 0 : HashPageOpaque nextopaque = HashPageGetOpaque(nextpage);
758 : :
759 : 0 : nextopaque->hasho_prevblkno = xldata->prevblkno;
760 : :
761 : 0 : PageSetLSN(nextpage, lsn);
762 : 0 : MarkBufferDirty(nextbuf);
763 : : }
764 [ # # ]: 0 : if (BufferIsValid(nextbuf))
765 : 0 : UnlockReleaseBuffer(nextbuf);
766 : : }
767 : :
768 [ + + ]: 32 : if (BufferIsValid(writebuf))
769 : 31 : UnlockReleaseBuffer(writebuf);
770 : :
771 [ + + ]: 32 : if (BufferIsValid(bucketbuf))
772 : 8 : UnlockReleaseBuffer(bucketbuf);
773 : :
774 : : /*
775 : : * Note: in normal operation, we'd update the bitmap and meta page while
776 : : * still holding lock on the primary bucket page and overflow pages. But
777 : : * during replay it's not necessary to hold those locks, since no other
778 : : * index updates can be happening concurrently.
779 : : */
780 : : /* replay the record for bitmap page */
781 [ + + ]: 32 : if (XLogReadBufferForRedo(record, 5, &mapbuf) == BLK_NEEDS_REDO)
782 : : {
783 : 27 : Page mappage = BufferGetPage(mapbuf);
784 : 27 : uint32 *freep = NULL;
785 : : char *data;
786 : : uint32 *bitmap_page_bit;
787 : : Size datalen;
788 : :
789 : 27 : freep = HashPageGetBitmap(mappage);
790 : :
791 : 27 : data = XLogRecGetBlockData(record, 5, &datalen);
792 : 27 : bitmap_page_bit = (uint32 *) data;
793 : :
794 : 27 : CLRBIT(freep, *bitmap_page_bit);
795 : :
796 : 27 : PageSetLSN(mappage, lsn);
797 : 27 : MarkBufferDirty(mapbuf);
798 : : }
799 [ + - ]: 32 : if (BufferIsValid(mapbuf))
800 : 32 : UnlockReleaseBuffer(mapbuf);
801 : :
802 : : /* replay the record for meta page */
803 [ + + + - ]: 32 : if (XLogRecHasBlockRef(record, 6))
804 : : {
805 : : Buffer metabuf;
806 : :
807 [ + + ]: 31 : if (XLogReadBufferForRedo(record, 6, &metabuf) == BLK_NEEDS_REDO)
808 : : {
809 : : HashMetaPage metap;
810 : : Page page;
811 : : char *data;
812 : : uint32 *firstfree_ovflpage;
813 : : Size datalen;
814 : :
815 : 28 : data = XLogRecGetBlockData(record, 6, &datalen);
816 : 28 : firstfree_ovflpage = (uint32 *) data;
817 : :
818 : 28 : page = BufferGetPage(metabuf);
819 : 28 : metap = HashPageGetMeta(page);
820 : 28 : metap->hashm_firstfree = *firstfree_ovflpage;
821 : :
822 : 28 : PageSetLSN(page, lsn);
823 : 28 : MarkBufferDirty(metabuf);
824 : : }
825 [ + - ]: 31 : if (BufferIsValid(metabuf))
826 : 31 : UnlockReleaseBuffer(metabuf);
827 : : }
828 : 32 : }
829 : :
830 : : /*
831 : : * replay delete operation of hash index
832 : : */
833 : : static void
834 : 261 : hash_xlog_delete(XLogReaderState *record)
835 : : {
836 : 261 : XLogRecPtr lsn = record->EndRecPtr;
837 : 261 : xl_hash_delete *xldata = (xl_hash_delete *) XLogRecGetData(record);
838 : 261 : Buffer bucketbuf = InvalidBuffer;
839 : : Buffer deletebuf;
840 : : Page page;
841 : : XLogRedoAction action;
842 : :
843 : : /*
844 : : * Ensure we have a cleanup lock on primary bucket page before we start
845 : : * with the actual replay operation. This is to ensure that neither a
846 : : * scan can start nor a scan can be already-in-progress during the replay
847 : : * of this operation. If we allow scans during this operation, then they
848 : : * can miss some records or show the same record multiple times.
849 : : */
850 [ + + ]: 261 : if (xldata->is_primary_bucket_page)
851 : 229 : action = XLogReadBufferForRedoExtended(record, 1, RBM_NORMAL, true, &deletebuf);
852 : : else
853 : : {
854 : : /*
855 : : * we don't care for return value as the purpose of reading bucketbuf
856 : : * is to ensure a cleanup lock on primary bucket page.
857 : : */
858 : 32 : (void) XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &bucketbuf);
859 : :
860 : 32 : action = XLogReadBufferForRedo(record, 1, &deletebuf);
861 : : }
862 : :
863 : : /* replay the record for deleting entries in bucket page */
864 [ + + ]: 261 : if (action == BLK_NEEDS_REDO)
865 : : {
866 : : char *ptr;
867 : : Size len;
868 : :
869 : 230 : ptr = XLogRecGetBlockData(record, 1, &len);
870 : :
871 : 230 : page = BufferGetPage(deletebuf);
872 : :
873 [ + - ]: 230 : if (len > 0)
874 : : {
875 : : OffsetNumber *unused;
876 : : OffsetNumber *unend;
877 : :
878 : 230 : unused = (OffsetNumber *) ptr;
879 : 230 : unend = (OffsetNumber *) (ptr + len);
880 : :
881 [ + - ]: 230 : if ((unend - unused) > 0)
882 : 230 : PageIndexMultiDelete(page, unused, unend - unused);
883 : : }
884 : :
885 : : /*
886 : : * Mark the page as not containing any LP_DEAD items only if
887 : : * clear_dead_marking flag is set to true. See comments in
888 : : * hashbucketcleanup() for details.
889 : : */
890 [ - + ]: 230 : if (xldata->clear_dead_marking)
891 : : {
892 : : HashPageOpaque pageopaque;
893 : :
894 : 0 : pageopaque = HashPageGetOpaque(page);
895 : 0 : pageopaque->hasho_flag &= ~LH_PAGE_HAS_DEAD_TUPLES;
896 : : }
897 : :
898 : 230 : PageSetLSN(page, lsn);
899 : 230 : MarkBufferDirty(deletebuf);
900 : : }
901 [ + - ]: 261 : if (BufferIsValid(deletebuf))
902 : 261 : UnlockReleaseBuffer(deletebuf);
903 : :
904 [ + + ]: 261 : if (BufferIsValid(bucketbuf))
905 : 32 : UnlockReleaseBuffer(bucketbuf);
906 : 261 : }
907 : :
908 : : /*
909 : : * replay split cleanup flag operation for primary bucket page.
910 : : */
911 : : static void
912 : 221 : hash_xlog_split_cleanup(XLogReaderState *record)
913 : : {
914 : 221 : XLogRecPtr lsn = record->EndRecPtr;
915 : : Buffer buffer;
916 : : Page page;
917 : :
918 [ + - ]: 221 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
919 : : {
920 : : HashPageOpaque bucket_opaque;
921 : :
922 : 221 : page = BufferGetPage(buffer);
923 : :
924 : 221 : bucket_opaque = HashPageGetOpaque(page);
925 : 221 : bucket_opaque->hasho_flag &= ~LH_BUCKET_NEEDS_SPLIT_CLEANUP;
926 : 221 : PageSetLSN(page, lsn);
927 : 221 : MarkBufferDirty(buffer);
928 : : }
929 [ + - ]: 221 : if (BufferIsValid(buffer))
930 : 221 : UnlockReleaseBuffer(buffer);
931 : 221 : }
932 : :
933 : : /*
934 : : * replay for update meta page
935 : : */
936 : : static void
937 : 9 : hash_xlog_update_meta_page(XLogReaderState *record)
938 : : {
939 : : HashMetaPage metap;
940 : 9 : XLogRecPtr lsn = record->EndRecPtr;
941 : 9 : xl_hash_update_meta_page *xldata = (xl_hash_update_meta_page *) XLogRecGetData(record);
942 : : Buffer metabuf;
943 : : Page page;
944 : :
945 [ + + ]: 9 : if (XLogReadBufferForRedo(record, 0, &metabuf) == BLK_NEEDS_REDO)
946 : : {
947 : 5 : page = BufferGetPage(metabuf);
948 : 5 : metap = HashPageGetMeta(page);
949 : :
950 : 5 : metap->hashm_ntuples = xldata->ntuples;
951 : :
952 : 5 : PageSetLSN(page, lsn);
953 : 5 : MarkBufferDirty(metabuf);
954 : : }
955 [ + - ]: 9 : if (BufferIsValid(metabuf))
956 : 9 : UnlockReleaseBuffer(metabuf);
957 : 9 : }
958 : :
959 : : /*
960 : : * replay delete operation in hash index to remove
961 : : * tuples marked as DEAD during index tuple insertion.
962 : : */
963 : : static void
964 : 1 : hash_xlog_vacuum_one_page(XLogReaderState *record)
965 : : {
966 : 1 : XLogRecPtr lsn = record->EndRecPtr;
967 : : xl_hash_vacuum_one_page *xldata;
968 : : Buffer buffer;
969 : : Buffer metabuf;
970 : : Page page;
971 : : XLogRedoAction action;
972 : : HashPageOpaque pageopaque;
973 : : OffsetNumber *toDelete;
974 : :
975 : 1 : xldata = (xl_hash_vacuum_one_page *) XLogRecGetData(record);
976 : 1 : toDelete = xldata->offsets;
977 : :
978 : : /*
979 : : * If we have any conflict processing to do, it must happen before we
980 : : * update the page.
981 : : *
982 : : * Hash index records that are marked as LP_DEAD and being removed during
983 : : * hash index tuple insertion can conflict with standby queries. You might
984 : : * think that vacuum records would conflict as well, but we've handled
985 : : * that already. XLOG_HEAP2_PRUNE_VACUUM_SCAN records provide the highest
986 : : * xid cleaned by the vacuum of the heap and so we can resolve any
987 : : * conflicts just once when that arrives. After that we know that no
988 : : * conflicts exist from individual hash index vacuum records on that
989 : : * index.
990 : : */
991 [ + - ]: 1 : if (InHotStandby)
992 : : {
993 : : RelFileLocator rlocator;
994 : :
995 : 1 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, NULL);
996 : 1 : ResolveRecoveryConflictWithSnapshot(xldata->snapshotConflictHorizon,
997 : 1 : xldata->isCatalogRel,
998 : : rlocator);
999 : : }
1000 : :
1001 : 1 : action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer);
1002 : :
1003 [ + - ]: 1 : if (action == BLK_NEEDS_REDO)
1004 : : {
1005 : 1 : page = BufferGetPage(buffer);
1006 : :
1007 : 1 : PageIndexMultiDelete(page, toDelete, xldata->ntuples);
1008 : :
1009 : : /*
1010 : : * Mark the page as not containing any LP_DEAD items. See comments in
1011 : : * _hash_vacuum_one_page() for details.
1012 : : */
1013 : 1 : pageopaque = HashPageGetOpaque(page);
1014 : 1 : pageopaque->hasho_flag &= ~LH_PAGE_HAS_DEAD_TUPLES;
1015 : :
1016 : 1 : PageSetLSN(page, lsn);
1017 : 1 : MarkBufferDirty(buffer);
1018 : : }
1019 [ + - ]: 1 : if (BufferIsValid(buffer))
1020 : 1 : UnlockReleaseBuffer(buffer);
1021 : :
1022 [ + - ]: 1 : if (XLogReadBufferForRedo(record, 1, &metabuf) == BLK_NEEDS_REDO)
1023 : : {
1024 : : Page metapage;
1025 : : HashMetaPage metap;
1026 : :
1027 : 1 : metapage = BufferGetPage(metabuf);
1028 : 1 : metap = HashPageGetMeta(metapage);
1029 : :
1030 : 1 : metap->hashm_ntuples -= xldata->ntuples;
1031 : :
1032 : 1 : PageSetLSN(metapage, lsn);
1033 : 1 : MarkBufferDirty(metabuf);
1034 : : }
1035 [ + - ]: 1 : if (BufferIsValid(metabuf))
1036 : 1 : UnlockReleaseBuffer(metabuf);
1037 : 1 : }
1038 : :
1039 : : void
1040 : 122646 : hash_redo(XLogReaderState *record)
1041 : : {
1042 : 122646 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1043 : :
1044 [ + + + + : 122646 : switch (info)
+ + + + +
+ + + +
- ]
1045 : : {
1046 : 30 : case XLOG_HASH_INIT_META_PAGE:
1047 : 30 : hash_xlog_init_meta_page(record);
1048 : 30 : break;
1049 : 30 : case XLOG_HASH_INIT_BITMAP_PAGE:
1050 : 30 : hash_xlog_init_bitmap_page(record);
1051 : 30 : break;
1052 : 121315 : case XLOG_HASH_INSERT:
1053 : 121315 : hash_xlog_insert(record);
1054 : 121315 : break;
1055 : 70 : case XLOG_HASH_ADD_OVFL_PAGE:
1056 : 70 : hash_xlog_add_ovfl_page(record);
1057 : 70 : break;
1058 : 221 : case XLOG_HASH_SPLIT_ALLOCATE_PAGE:
1059 : 221 : hash_xlog_split_allocate_page(record);
1060 : 221 : break;
1061 : 234 : case XLOG_HASH_SPLIT_PAGE:
1062 : 234 : hash_xlog_split_page(record);
1063 : 234 : break;
1064 : 221 : case XLOG_HASH_SPLIT_COMPLETE:
1065 : 221 : hash_xlog_split_complete(record);
1066 : 221 : break;
1067 : 1 : case XLOG_HASH_MOVE_PAGE_CONTENTS:
1068 : 1 : hash_xlog_move_page_contents(record);
1069 : 1 : break;
1070 : 32 : case XLOG_HASH_SQUEEZE_PAGE:
1071 : 32 : hash_xlog_squeeze_page(record);
1072 : 32 : break;
1073 : 261 : case XLOG_HASH_DELETE:
1074 : 261 : hash_xlog_delete(record);
1075 : 261 : break;
1076 : 221 : case XLOG_HASH_SPLIT_CLEANUP:
1077 : 221 : hash_xlog_split_cleanup(record);
1078 : 221 : break;
1079 : 9 : case XLOG_HASH_UPDATE_META_PAGE:
1080 : 9 : hash_xlog_update_meta_page(record);
1081 : 9 : break;
1082 : 1 : case XLOG_HASH_VACUUM_ONE_PAGE:
1083 : 1 : hash_xlog_vacuum_one_page(record);
1084 : 1 : break;
1085 : 0 : default:
1086 [ # # ]: 0 : elog(PANIC, "hash_redo: unknown op code %u", info);
1087 : : }
1088 : 122646 : }
1089 : :
1090 : : /*
1091 : : * Mask a hash page before performing consistency checks on it.
1092 : : */
1093 : : void
1094 : 486438 : hash_mask(char *pagedata, BlockNumber blkno)
1095 : : {
1096 : 486438 : Page page = (Page) pagedata;
1097 : : HashPageOpaque opaque;
1098 : : int pagetype;
1099 : :
1100 : 486438 : mask_page_lsn_and_checksum(page);
1101 : :
1102 : 486438 : mask_page_hint_bits(page);
1103 : 486438 : mask_unused_space(page);
1104 : :
1105 : 486438 : opaque = HashPageGetOpaque(page);
1106 : :
1107 : 486438 : pagetype = opaque->hasho_flag & LH_PAGE_TYPE;
1108 [ - + ]: 486438 : if (pagetype == LH_UNUSED_PAGE)
1109 : : {
1110 : : /*
1111 : : * Mask everything on a UNUSED page.
1112 : : */
1113 : 0 : mask_page_content(page);
1114 : : }
1115 [ + + + + ]: 486438 : else if (pagetype == LH_BUCKET_PAGE ||
1116 : : pagetype == LH_OVERFLOW_PAGE)
1117 : : {
1118 : : /*
1119 : : * In hash bucket and overflow pages, it is possible to modify the
1120 : : * LP_FLAGS without emitting any WAL record. Hence, mask the line
1121 : : * pointer flags. See hashgettuple(), _hash_kill_items() for details.
1122 : : */
1123 : 242958 : mask_lp_flags(page);
1124 : : }
1125 : :
1126 : : /*
1127 : : * It is possible that the hint bit LH_PAGE_HAS_DEAD_TUPLES may remain
1128 : : * unlogged. So, mask it. See _hash_kill_items() for details.
1129 : : */
1130 : 486438 : opaque->hasho_flag &= ~LH_PAGE_HAS_DEAD_TUPLES;
1131 : 486438 : }
|