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