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