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