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