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