Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * hashovfl.c
4 : : * Overflow page management code for the Postgres hash access method
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/hash/hashovfl.c
12 : : *
13 : : * NOTES
14 : : * Overflow pages look like ordinary relation pages.
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : : #include "postgres.h"
19 : :
20 : : #include "access/hash.h"
21 : : #include "access/hash_xlog.h"
22 : : #include "access/xloginsert.h"
23 : : #include "miscadmin.h"
24 : : #include "utils/rel.h"
25 : :
26 : :
27 : : static uint32 _hash_firstfreebit(uint32 map);
28 : :
29 : :
30 : : /*
31 : : * Convert overflow page bit number (its index in the free-page bitmaps)
32 : : * to block number within the index.
33 : : */
34 : : static BlockNumber
35 : 266 : bitno_to_blkno(HashMetaPage metap, uint32 ovflbitnum)
36 : : {
37 : 266 : uint32 splitnum = metap->hashm_ovflpoint;
38 : : uint32 i;
39 : :
40 : : /* Convert zero-based bitnumber to 1-based page number */
41 : 266 : ovflbitnum += 1;
42 : :
43 : : /* Determine the split number for this page (must be >= 1) */
44 : 266 : for (i = 1;
45 [ + + + + ]: 1362 : i < splitnum && ovflbitnum > metap->hashm_spares[i];
46 : 1096 : i++)
47 : : /* loop */ ;
48 : :
49 : : /*
50 : : * Convert to absolute page number by adding the number of bucket pages
51 : : * that exist before this split point.
52 : : */
53 : 266 : return (BlockNumber) (_hash_get_totalbuckets(i) + ovflbitnum);
54 : : }
55 : :
56 : : /*
57 : : * _hash_ovflblkno_to_bitno
58 : : *
59 : : * Convert overflow page block number to bit number for free-page bitmap.
60 : : */
61 : : uint32
62 : 142 : _hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno)
63 : : {
64 : 142 : uint32 splitnum = metap->hashm_ovflpoint;
65 : : uint32 i;
66 : : uint32 bitnum;
67 : :
68 : : /* Determine the split number containing this page */
69 [ + - ]: 562 : for (i = 1; i <= splitnum; i++)
70 : : {
71 [ + + ]: 562 : if (ovflblkno <= (BlockNumber) _hash_get_totalbuckets(i))
72 : 4 : break; /* oops */
73 : 558 : bitnum = ovflblkno - _hash_get_totalbuckets(i);
74 : :
75 : : /*
76 : : * bitnum has to be greater than number of overflow page added in
77 : : * previous split point. The overflow page at this splitnum (i) if any
78 : : * should start from (_hash_get_totalbuckets(i) +
79 : : * metap->hashm_spares[i - 1] + 1).
80 : : */
81 [ + - ]: 558 : if (bitnum > metap->hashm_spares[i - 1] &&
82 [ + + ]: 558 : bitnum <= metap->hashm_spares[i])
83 : 138 : return bitnum - 1; /* -1 to convert 1-based to 0-based */
84 : : }
85 : :
86 [ + - ]: 4 : ereport(ERROR,
87 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
88 : : errmsg("invalid overflow block number %u", ovflblkno)));
89 : : return 0; /* keep compiler quiet */
90 : : }
91 : :
92 : : /*
93 : : * _hash_addovflpage
94 : : *
95 : : * Add an overflow page to the bucket whose last page is pointed to by 'buf'.
96 : : *
97 : : * On entry, the caller must hold a pin but no lock on 'buf'. The pin is
98 : : * dropped before exiting (we assume the caller is not interested in 'buf'
99 : : * anymore) if not asked to retain. The pin will be retained only for the
100 : : * primary bucket. The returned overflow page will be pinned and
101 : : * write-locked; it is guaranteed to be empty.
102 : : *
103 : : * The caller must hold a pin, but no lock, on the metapage buffer.
104 : : * That buffer is returned in the same state.
105 : : *
106 : : * NB: since this could be executed concurrently by multiple processes,
107 : : * one should not assume that the returned overflow page will be the
108 : : * immediate successor of the originally passed 'buf'. Additional overflow
109 : : * pages might have been added to the bucket chain in between.
110 : : */
111 : : Buffer
112 : 266 : _hash_addovflpage(Relation rel, Buffer metabuf, Buffer buf, bool retain_pin)
113 : : {
114 : : Buffer ovflbuf;
115 : : Page page;
116 : : Page ovflpage;
117 : : HashPageOpaque pageopaque;
118 : : HashPageOpaque ovflopaque;
119 : : HashMetaPage metap;
120 : 266 : Buffer mapbuf = InvalidBuffer;
121 : 266 : Buffer newmapbuf = InvalidBuffer;
122 : : BlockNumber blkno;
123 : : uint32 orig_firstfree;
124 : : uint32 splitnum;
125 : 266 : uint32 *freep = NULL;
126 : : uint32 max_ovflpg;
127 : : uint32 bit;
128 : : uint32 bitmap_page_bit;
129 : : uint32 first_page;
130 : : uint32 last_bit;
131 : : uint32 last_page;
132 : : uint32 i,
133 : : j;
134 : 266 : bool page_found = false;
135 : : XLogRecPtr recptr;
136 : :
137 : : /*
138 : : * Write-lock the tail page. Here, we need to maintain locking order such
139 : : * that, first acquire the lock on tail page of bucket, then on meta page
140 : : * to find and lock the bitmap page and if it is found, then lock on meta
141 : : * page is released, then finally acquire the lock on new overflow buffer.
142 : : * We need this locking order to avoid deadlock with backends that are
143 : : * doing inserts.
144 : : *
145 : : * Note: We could have avoided locking many buffers here if we made two
146 : : * WAL records for acquiring an overflow page (one to allocate an overflow
147 : : * page and another to add it to overflow bucket chain). However, doing
148 : : * so can leak an overflow page, if the system crashes after allocation.
149 : : * Needless to say, it is better to have a single record from a
150 : : * performance point of view as well.
151 : : */
152 : 266 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
153 : :
154 : : /* probably redundant... */
155 : 266 : _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
156 : :
157 : : /* loop to find current tail page, in case someone else inserted too */
158 : : for (;;)
159 : 0 : {
160 : : BlockNumber nextblkno;
161 : :
162 : 266 : page = BufferGetPage(buf);
163 : 266 : pageopaque = HashPageGetOpaque(page);
164 : 266 : nextblkno = pageopaque->hasho_nextblkno;
165 : :
166 [ + - ]: 266 : if (!BlockNumberIsValid(nextblkno))
167 : 266 : break;
168 : :
169 : : /* we assume we do not need to write the unmodified page */
170 [ # # ]: 0 : if (retain_pin)
171 : : {
172 : : /* pin will be retained only for the primary bucket page */
173 : : Assert((pageopaque->hasho_flag & LH_PAGE_TYPE) == LH_BUCKET_PAGE);
174 : 0 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
175 : : }
176 : : else
177 : 0 : _hash_relbuf(rel, buf);
178 : :
179 : 0 : retain_pin = false;
180 : :
181 : 0 : buf = _hash_getbuf(rel, nextblkno, HASH_WRITE, LH_OVERFLOW_PAGE);
182 : : }
183 : :
184 : : /* Get exclusive lock on the meta page */
185 : 266 : LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
186 : :
187 : 266 : _hash_checkpage(rel, metabuf, LH_META_PAGE);
188 : 266 : metap = HashPageGetMeta(BufferGetPage(metabuf));
189 : :
190 : : /* start search at hashm_firstfree */
191 : 266 : orig_firstfree = metap->hashm_firstfree;
192 : 266 : first_page = orig_firstfree >> BMPG_SHIFT(metap);
193 : 266 : bit = orig_firstfree & BMPG_MASK(metap);
194 : 266 : i = first_page;
195 : 266 : j = bit / BITS_PER_MAP;
196 : 266 : bit &= ~(BITS_PER_MAP - 1);
197 : :
198 : : /* outer loop iterates once per bitmap page */
199 : : for (;;)
200 : 220 : {
201 : : BlockNumber mapblkno;
202 : : Page mappage;
203 : : uint32 last_inpage;
204 : :
205 : : /* want to end search with the last existing overflow page */
206 : 486 : splitnum = metap->hashm_ovflpoint;
207 : 486 : max_ovflpg = metap->hashm_spares[splitnum] - 1;
208 : 486 : last_page = max_ovflpg >> BMPG_SHIFT(metap);
209 : 486 : last_bit = max_ovflpg & BMPG_MASK(metap);
210 : :
211 [ + + ]: 486 : if (i > last_page)
212 : 220 : break;
213 : :
214 : : Assert(i < metap->hashm_nmaps);
215 : 266 : mapblkno = metap->hashm_mapp[i];
216 : :
217 [ + - ]: 266 : if (i == last_page)
218 : 266 : last_inpage = last_bit;
219 : : else
220 : 0 : last_inpage = BMPGSZ_BIT(metap) - 1;
221 : :
222 : : /* Release exclusive lock on metapage while reading bitmap page */
223 : 266 : LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
224 : :
225 : 266 : mapbuf = _hash_getbuf(rel, mapblkno, HASH_WRITE, LH_BITMAP_PAGE);
226 : 266 : mappage = BufferGetPage(mapbuf);
227 : 266 : freep = HashPageGetBitmap(mappage);
228 : :
229 [ + + ]: 486 : for (; bit <= last_inpage; j++, bit += BITS_PER_MAP)
230 : : {
231 [ + + ]: 266 : if (freep[j] != ALL_SET)
232 : : {
233 : 46 : page_found = true;
234 : :
235 : : /* Reacquire exclusive lock on the meta page */
236 : 46 : LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
237 : :
238 : : /* convert bit to bit number within page */
239 : 46 : bit += _hash_firstfreebit(freep[j]);
240 : 46 : bitmap_page_bit = bit;
241 : :
242 : : /* convert bit to absolute bit number */
243 : 46 : bit += (i << BMPG_SHIFT(metap));
244 : : /* Calculate address of the recycled overflow page */
245 : 46 : blkno = bitno_to_blkno(metap, bit);
246 : :
247 : : /* Fetch and init the recycled page */
248 : 46 : ovflbuf = _hash_getinitbuf(rel, blkno);
249 : :
250 : 46 : goto found;
251 : : }
252 : : }
253 : :
254 : : /* No free space here, try to advance to next map page */
255 : 220 : _hash_relbuf(rel, mapbuf);
256 : 220 : mapbuf = InvalidBuffer;
257 : 220 : i++;
258 : 220 : j = 0; /* scan from start of next map page */
259 : 220 : bit = 0;
260 : :
261 : : /* Reacquire exclusive lock on the meta page */
262 : 220 : LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
263 : : }
264 : :
265 : : /*
266 : : * No free pages --- have to extend the relation to add an overflow page.
267 : : * First, check to see if we have to add a new bitmap page too.
268 : : */
269 [ - + ]: 220 : if (last_bit == (uint32) (BMPGSZ_BIT(metap) - 1))
270 : : {
271 : : /*
272 : : * We create the new bitmap page with all pages marked "in use".
273 : : * Actually two pages in the new bitmap's range will exist
274 : : * immediately: the bitmap page itself, and the following page which
275 : : * is the one we return to the caller. Both of these are correctly
276 : : * marked "in use". Subsequent pages do not exist yet, but it is
277 : : * convenient to pre-mark them as "in use" too.
278 : : */
279 : 0 : bit = metap->hashm_spares[splitnum];
280 : :
281 : : /* metapage already has a write lock */
282 [ # # ]: 0 : if (metap->hashm_nmaps >= HASH_MAX_BITMAPS)
283 [ # # ]: 0 : ereport(ERROR,
284 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
285 : : errmsg("out of overflow pages in hash index \"%s\"",
286 : : RelationGetRelationName(rel))));
287 : :
288 : 0 : newmapbuf = _hash_getnewbuf(rel, bitno_to_blkno(metap, bit), MAIN_FORKNUM);
289 : : }
290 : : else
291 : : {
292 : : /*
293 : : * Nothing to do here; since the page will be past the last used page,
294 : : * we know its bitmap bit was preinitialized to "in use".
295 : : */
296 : : }
297 : :
298 : : /* Calculate address of the new overflow page */
299 : 220 : bit = BufferIsValid(newmapbuf) ?
300 [ - + ]: 220 : metap->hashm_spares[splitnum] + 1 : metap->hashm_spares[splitnum];
301 : 220 : blkno = bitno_to_blkno(metap, bit);
302 : :
303 : : /*
304 : : * Fetch the page with _hash_getnewbuf to ensure smgr's idea of the
305 : : * relation length stays in sync with ours. XXX It's annoying to do this
306 : : * with metapage write lock held; would be better to use a lock that
307 : : * doesn't block incoming searches.
308 : : *
309 : : * It is okay to hold two buffer locks here (one on tail page of bucket
310 : : * and other on new overflow page) since there cannot be anyone else
311 : : * contending for access to ovflbuf.
312 : : */
313 : 220 : ovflbuf = _hash_getnewbuf(rel, blkno, MAIN_FORKNUM);
314 : :
315 : 266 : found:
316 : :
317 : : /*
318 : : * Do the update. No ereport(ERROR) until changes are logged. We want to
319 : : * log the changes for bitmap page and overflow page together to avoid
320 : : * loss of pages in case the new page is added.
321 : : */
322 : 266 : START_CRIT_SECTION();
323 : :
324 [ + + ]: 266 : if (page_found)
325 : : {
326 : : Assert(BufferIsValid(mapbuf));
327 : :
328 : : /* mark page "in use" in the bitmap */
329 : 46 : SETBIT(freep, bitmap_page_bit);
330 : 46 : MarkBufferDirty(mapbuf);
331 : : }
332 : : else
333 : : {
334 : : /* update the count to indicate new overflow page is added */
335 : 220 : metap->hashm_spares[splitnum]++;
336 : :
337 [ - + ]: 220 : if (BufferIsValid(newmapbuf))
338 : : {
339 : 0 : _hash_initbitmapbuffer(newmapbuf, metap->hashm_bmsize, false);
340 : 0 : MarkBufferDirty(newmapbuf);
341 : :
342 : : /* add the new bitmap page to the metapage's list of bitmaps */
343 : 0 : metap->hashm_mapp[metap->hashm_nmaps] = BufferGetBlockNumber(newmapbuf);
344 : 0 : metap->hashm_nmaps++;
345 : 0 : metap->hashm_spares[splitnum]++;
346 : : }
347 : :
348 : 220 : MarkBufferDirty(metabuf);
349 : :
350 : : /*
351 : : * for new overflow page, we don't need to explicitly set the bit in
352 : : * bitmap page, as by default that will be set to "in use".
353 : : */
354 : : }
355 : :
356 : : /*
357 : : * Adjust hashm_firstfree to avoid redundant searches. But don't risk
358 : : * changing it if someone moved it while we were searching bitmap pages.
359 : : */
360 [ + - ]: 266 : if (metap->hashm_firstfree == orig_firstfree)
361 : : {
362 : 266 : metap->hashm_firstfree = bit + 1;
363 : 266 : MarkBufferDirty(metabuf);
364 : : }
365 : :
366 : : /* initialize new overflow page */
367 : 266 : ovflpage = BufferGetPage(ovflbuf);
368 : 266 : ovflopaque = HashPageGetOpaque(ovflpage);
369 : 266 : ovflopaque->hasho_prevblkno = BufferGetBlockNumber(buf);
370 : 266 : ovflopaque->hasho_nextblkno = InvalidBlockNumber;
371 : 266 : ovflopaque->hasho_bucket = pageopaque->hasho_bucket;
372 : 266 : ovflopaque->hasho_flag = LH_OVERFLOW_PAGE;
373 : 266 : ovflopaque->hasho_page_id = HASHO_PAGE_ID;
374 : :
375 : 266 : MarkBufferDirty(ovflbuf);
376 : :
377 : : /* logically chain overflow page to previous page */
378 : 266 : pageopaque->hasho_nextblkno = BufferGetBlockNumber(ovflbuf);
379 : :
380 : 266 : MarkBufferDirty(buf);
381 : :
382 : : /* XLOG stuff */
383 [ + + - + : 266 : if (RelationNeedsWAL(rel))
- - - - ]
384 : 265 : {
385 : : xl_hash_add_ovfl_page xlrec;
386 : :
387 : 265 : xlrec.bmpage_found = page_found;
388 : 265 : xlrec.bmsize = metap->hashm_bmsize;
389 : :
390 : 265 : XLogBeginInsert();
391 : 265 : XLogRegisterData(&xlrec, SizeOfHashAddOvflPage);
392 : :
393 : 265 : XLogRegisterBuffer(0, ovflbuf, REGBUF_WILL_INIT);
394 : 265 : XLogRegisterBufData(0, &pageopaque->hasho_bucket, sizeof(Bucket));
395 : :
396 : 265 : XLogRegisterBuffer(1, buf, REGBUF_STANDARD);
397 : :
398 [ + + ]: 265 : if (BufferIsValid(mapbuf))
399 : : {
400 : 46 : XLogRegisterBuffer(2, mapbuf, REGBUF_STANDARD);
401 : 46 : XLogRegisterBufData(2, &bitmap_page_bit, sizeof(uint32));
402 : : }
403 : :
404 [ - + ]: 265 : if (BufferIsValid(newmapbuf))
405 : 0 : XLogRegisterBuffer(3, newmapbuf, REGBUF_WILL_INIT);
406 : :
407 : 265 : XLogRegisterBuffer(4, metabuf, REGBUF_STANDARD);
408 : 265 : XLogRegisterBufData(4, &metap->hashm_firstfree, sizeof(uint32));
409 : :
410 : 265 : recptr = XLogInsert(RM_HASH_ID, XLOG_HASH_ADD_OVFL_PAGE);
411 : : }
412 : : else
413 : 1 : recptr = XLogGetFakeLSN(rel);
414 : :
415 : 266 : PageSetLSN(BufferGetPage(ovflbuf), recptr);
416 : 266 : PageSetLSN(BufferGetPage(buf), recptr);
417 : :
418 [ + + ]: 266 : if (BufferIsValid(mapbuf))
419 : 46 : PageSetLSN(BufferGetPage(mapbuf), recptr);
420 : :
421 [ - + ]: 266 : if (BufferIsValid(newmapbuf))
422 : 0 : PageSetLSN(BufferGetPage(newmapbuf), recptr);
423 : :
424 : 266 : PageSetLSN(BufferGetPage(metabuf), recptr);
425 : :
426 : 266 : END_CRIT_SECTION();
427 : :
428 [ + + ]: 266 : if (retain_pin)
429 : 78 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
430 : : else
431 : 188 : _hash_relbuf(rel, buf);
432 : :
433 [ + + ]: 266 : if (BufferIsValid(mapbuf))
434 : 46 : _hash_relbuf(rel, mapbuf);
435 : :
436 : 266 : LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
437 : :
438 [ - + ]: 266 : if (BufferIsValid(newmapbuf))
439 : 0 : _hash_relbuf(rel, newmapbuf);
440 : :
441 : 266 : return ovflbuf;
442 : : }
443 : :
444 : : /*
445 : : * _hash_firstfreebit()
446 : : *
447 : : * Return the number of the first bit that is not set in the word 'map'.
448 : : */
449 : : static uint32
450 : 46 : _hash_firstfreebit(uint32 map)
451 : : {
452 : : uint32 i,
453 : : mask;
454 : :
455 : 46 : mask = 0x1;
456 [ + - ]: 384 : for (i = 0; i < BITS_PER_MAP; i++)
457 : : {
458 [ + + ]: 384 : if (!(mask & map))
459 : 46 : return i;
460 : 338 : mask <<= 1;
461 : : }
462 : :
463 [ # # ]: 0 : elog(ERROR, "firstfreebit found no free bit");
464 : :
465 : : return 0; /* keep compiler quiet */
466 : : }
467 : :
468 : : /*
469 : : * _hash_freeovflpage() -
470 : : *
471 : : * Remove this overflow page from its bucket's chain, and mark the page as
472 : : * free. On entry, ovflbuf is write-locked; it is released before exiting.
473 : : *
474 : : * Add the tuples (itups) to wbuf in this function. We could do that in the
475 : : * caller as well, but the advantage of doing it here is we can easily write
476 : : * the WAL for XLOG_HASH_SQUEEZE_PAGE operation. Addition of tuples and
477 : : * removal of overflow page has to done as an atomic operation, otherwise
478 : : * during replay on standby users might find duplicate records.
479 : : *
480 : : * Since this function is invoked in VACUUM, we provide an access strategy
481 : : * parameter that controls fetches of the bucket pages.
482 : : *
483 : : * Returns the block number of the page that followed the given page
484 : : * in the bucket, or InvalidBlockNumber if no following page.
485 : : *
486 : : * NB: caller must not hold lock on metapage, nor on page, that's next to
487 : : * ovflbuf in the bucket chain. We don't acquire the lock on page that's
488 : : * prior to ovflbuf in chain if it is same as wbuf because the caller already
489 : : * has a lock on same.
490 : : */
491 : : BlockNumber
492 : 138 : _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
493 : : Buffer wbuf, IndexTuple *itups, OffsetNumber *itup_offsets,
494 : : Size *tups_size, uint16 nitups,
495 : : BufferAccessStrategy bstrategy)
496 : : {
497 : : HashMetaPage metap;
498 : : Buffer metabuf;
499 : : Buffer mapbuf;
500 : : BlockNumber ovflblkno;
501 : : BlockNumber prevblkno;
502 : : BlockNumber blkno;
503 : : BlockNumber nextblkno;
504 : : BlockNumber writeblkno;
505 : : HashPageOpaque ovflopaque;
506 : : Page ovflpage;
507 : : Page mappage;
508 : : uint32 *freep;
509 : : uint32 ovflbitno;
510 : : int32 bitmappage,
511 : : bitmapbit;
512 : : Bucket bucket PG_USED_FOR_ASSERTS_ONLY;
513 : 138 : Buffer prevbuf = InvalidBuffer;
514 : 138 : Buffer nextbuf = InvalidBuffer;
515 : 138 : bool update_metap = false,
516 : : mod_wbuf,
517 : : is_prim_bucket_same_wrt,
518 : : is_prev_bucket_same_wrt;
519 : : XLogRecPtr recptr;
520 : :
521 : : /* Get information from the doomed page */
522 : 138 : _hash_checkpage(rel, ovflbuf, LH_OVERFLOW_PAGE);
523 : 138 : ovflblkno = BufferGetBlockNumber(ovflbuf);
524 : 138 : ovflpage = BufferGetPage(ovflbuf);
525 : 138 : ovflopaque = HashPageGetOpaque(ovflpage);
526 : 138 : nextblkno = ovflopaque->hasho_nextblkno;
527 : 138 : prevblkno = ovflopaque->hasho_prevblkno;
528 : 138 : writeblkno = BufferGetBlockNumber(wbuf);
529 : 138 : bucket = ovflopaque->hasho_bucket;
530 : :
531 : : /*
532 : : * Fix up the bucket chain. this is a doubly-linked list, so we must fix
533 : : * up the bucket chain members behind and ahead of the overflow page being
534 : : * deleted. Concurrency issues are avoided by using lock chaining as
535 : : * described atop hashbucketcleanup.
536 : : */
537 [ + - ]: 138 : if (BlockNumberIsValid(prevblkno))
538 : : {
539 [ + + ]: 138 : if (prevblkno == writeblkno)
540 : 42 : prevbuf = wbuf;
541 : : else
542 : 96 : prevbuf = _hash_getbuf_with_strategy(rel,
543 : : prevblkno,
544 : : HASH_WRITE,
545 : : LH_BUCKET_PAGE | LH_OVERFLOW_PAGE,
546 : : bstrategy);
547 : : }
548 [ - + ]: 138 : if (BlockNumberIsValid(nextblkno))
549 : 0 : nextbuf = _hash_getbuf_with_strategy(rel,
550 : : nextblkno,
551 : : HASH_WRITE,
552 : : LH_OVERFLOW_PAGE,
553 : : bstrategy);
554 : :
555 : : /* Note: bstrategy is intentionally not used for metapage and bitmap */
556 : :
557 : : /* Read the metapage so we can determine which bitmap page to use */
558 : 138 : metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
559 : 138 : metap = HashPageGetMeta(BufferGetPage(metabuf));
560 : :
561 : : /* Identify which bit to set */
562 : 138 : ovflbitno = _hash_ovflblkno_to_bitno(metap, ovflblkno);
563 : :
564 : 138 : bitmappage = ovflbitno >> BMPG_SHIFT(metap);
565 : 138 : bitmapbit = ovflbitno & BMPG_MASK(metap);
566 : :
567 [ - + ]: 138 : if (bitmappage >= metap->hashm_nmaps)
568 [ # # ]: 0 : elog(ERROR, "invalid overflow bit number %u", ovflbitno);
569 : 138 : blkno = metap->hashm_mapp[bitmappage];
570 : :
571 : : /* Release metapage lock while we access the bitmap page */
572 : 138 : LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
573 : :
574 : : /* read the bitmap page to clear the bitmap bit */
575 : 138 : mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE, LH_BITMAP_PAGE);
576 : 138 : mappage = BufferGetPage(mapbuf);
577 : 138 : freep = HashPageGetBitmap(mappage);
578 : : Assert(ISSET(freep, bitmapbit));
579 : :
580 : : /* Get write-lock on metapage to update firstfree */
581 : 138 : LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
582 : :
583 : : /* This operation needs to log multiple tuples, prepare WAL for that */
584 [ + - - + : 138 : if (RelationNeedsWAL(rel))
- - - - ]
585 : 138 : XLogEnsureRecordSpace(HASH_XLOG_FREE_OVFL_BUFS, 4 + nitups);
586 : :
587 : 138 : START_CRIT_SECTION();
588 : :
589 : : /*
590 : : * we have to insert tuples on the "write" page, being careful to preserve
591 : : * hashkey ordering. (If we insert many tuples into the same "write" page
592 : : * it would be worth qsort'ing them).
593 : : */
594 [ + + ]: 138 : if (nitups > 0)
595 : : {
596 : 58 : _hash_pgaddmultitup(rel, wbuf, itups, itup_offsets, nitups);
597 : 58 : MarkBufferDirty(wbuf);
598 : : }
599 : :
600 : : /*
601 : : * Reinitialize the freed overflow page. Just zeroing the page won't
602 : : * work, because WAL replay routines expect pages to be initialized. See
603 : : * explanation of RBM_NORMAL mode atop XLogReadBufferExtended. We are
604 : : * careful to make the special space valid here so that tools like
605 : : * pageinspect won't get confused.
606 : : */
607 : 138 : _hash_pageinit(ovflpage, BufferGetPageSize(ovflbuf));
608 : :
609 : 138 : ovflopaque = HashPageGetOpaque(ovflpage);
610 : :
611 : 138 : ovflopaque->hasho_prevblkno = InvalidBlockNumber;
612 : 138 : ovflopaque->hasho_nextblkno = InvalidBlockNumber;
613 : 138 : ovflopaque->hasho_bucket = InvalidBucket;
614 : 138 : ovflopaque->hasho_flag = LH_UNUSED_PAGE;
615 : 138 : ovflopaque->hasho_page_id = HASHO_PAGE_ID;
616 : :
617 : 138 : MarkBufferDirty(ovflbuf);
618 : :
619 [ + - ]: 138 : if (BufferIsValid(prevbuf))
620 : : {
621 : 138 : Page prevpage = BufferGetPage(prevbuf);
622 : 138 : HashPageOpaque prevopaque = HashPageGetOpaque(prevpage);
623 : :
624 : : Assert(prevopaque->hasho_bucket == bucket);
625 : 138 : prevopaque->hasho_nextblkno = nextblkno;
626 : 138 : MarkBufferDirty(prevbuf);
627 : : }
628 [ - + ]: 138 : if (BufferIsValid(nextbuf))
629 : : {
630 : 0 : Page nextpage = BufferGetPage(nextbuf);
631 : 0 : HashPageOpaque nextopaque = HashPageGetOpaque(nextpage);
632 : :
633 : : Assert(nextopaque->hasho_bucket == bucket);
634 : 0 : nextopaque->hasho_prevblkno = prevblkno;
635 : 0 : MarkBufferDirty(nextbuf);
636 : : }
637 : :
638 : : /* Clear the bitmap bit to indicate that this overflow page is free */
639 : 138 : CLRBIT(freep, bitmapbit);
640 : 138 : MarkBufferDirty(mapbuf);
641 : :
642 : : /* if this is now the first free page, update hashm_firstfree */
643 [ + + ]: 138 : if (ovflbitno < metap->hashm_firstfree)
644 : : {
645 : 121 : metap->hashm_firstfree = ovflbitno;
646 : 121 : update_metap = true;
647 : 121 : MarkBufferDirty(metabuf);
648 : : }
649 : :
650 : : /* Determine which pages are modified */
651 : 138 : is_prim_bucket_same_wrt = (wbuf == bucketbuf);
652 : 138 : is_prev_bucket_same_wrt = (wbuf == prevbuf);
653 [ + + + + ]: 138 : mod_wbuf = (nitups > 0 || is_prev_bucket_same_wrt);
654 : :
655 : : /* XLOG stuff */
656 [ + - - + : 138 : if (RelationNeedsWAL(rel))
- - - - ]
657 : 138 : {
658 : : xl_hash_squeeze_page xlrec;
659 : :
660 : 138 : xlrec.prevblkno = prevblkno;
661 : 138 : xlrec.nextblkno = nextblkno;
662 : 138 : xlrec.ntups = nitups;
663 : 138 : xlrec.is_prim_bucket_same_wrt = is_prim_bucket_same_wrt;
664 : 138 : xlrec.is_prev_bucket_same_wrt = is_prev_bucket_same_wrt;
665 : :
666 : 138 : XLogBeginInsert();
667 : 138 : XLogRegisterData(&xlrec, SizeOfHashSqueezePage);
668 : :
669 : : /*
670 : : * bucket buffer was not changed, but still needs to be registered to
671 : : * ensure that we can acquire a cleanup lock on it during replay.
672 : : */
673 [ + + ]: 138 : if (!is_prim_bucket_same_wrt)
674 : : {
675 : 32 : uint8 flags = REGBUF_STANDARD | REGBUF_NO_IMAGE | REGBUF_NO_CHANGE;
676 : :
677 : 32 : XLogRegisterBuffer(0, bucketbuf, flags);
678 : : }
679 : :
680 [ + + ]: 138 : if (nitups > 0)
681 : : {
682 : 58 : XLogRegisterBuffer(1, wbuf, REGBUF_STANDARD);
683 : 58 : XLogRegisterBufData(1, itup_offsets,
684 : : nitups * sizeof(OffsetNumber));
685 [ + + ]: 2320 : for (int i = 0; i < nitups; i++)
686 : 2262 : XLogRegisterBufData(1, itups[i], tups_size[i]);
687 : : }
688 [ + + + + ]: 80 : else if (is_prim_bucket_same_wrt || is_prev_bucket_same_wrt)
689 : : {
690 : : uint8 wbuf_flags;
691 : :
692 : : /*
693 : : * A write buffer needs to be registered even if no tuples are
694 : : * added to it to ensure that we can acquire a cleanup lock on it
695 : : * if it is the same as primary bucket buffer or update the
696 : : * nextblkno if it is same as the previous bucket buffer.
697 : : */
698 : : Assert(nitups == 0);
699 : :
700 : 76 : wbuf_flags = REGBUF_STANDARD;
701 [ + + ]: 76 : if (!is_prev_bucket_same_wrt)
702 : 63 : wbuf_flags |= REGBUF_NO_CHANGE;
703 : : else
704 : : Assert(mod_wbuf);
705 : 76 : XLogRegisterBuffer(1, wbuf, wbuf_flags);
706 : : }
707 : :
708 : 138 : XLogRegisterBuffer(2, ovflbuf, REGBUF_STANDARD);
709 : :
710 : : /*
711 : : * If prevpage and the writepage (block in which we are moving tuples
712 : : * from overflow) are same, then no need to separately register
713 : : * prevpage. During replay, we can directly update the nextblock in
714 : : * writepage.
715 : : */
716 [ + - + + ]: 138 : if (BufferIsValid(prevbuf) && !is_prev_bucket_same_wrt)
717 : 96 : XLogRegisterBuffer(3, prevbuf, REGBUF_STANDARD);
718 : :
719 [ - + ]: 138 : if (BufferIsValid(nextbuf))
720 : 0 : XLogRegisterBuffer(4, nextbuf, REGBUF_STANDARD);
721 : :
722 : 138 : XLogRegisterBuffer(5, mapbuf, REGBUF_STANDARD);
723 : 138 : XLogRegisterBufData(5, &bitmapbit, sizeof(uint32));
724 : :
725 [ + + ]: 138 : if (update_metap)
726 : : {
727 : 121 : XLogRegisterBuffer(6, metabuf, REGBUF_STANDARD);
728 : 121 : XLogRegisterBufData(6, &metap->hashm_firstfree, sizeof(uint32));
729 : : }
730 : :
731 : 138 : recptr = XLogInsert(RM_HASH_ID, XLOG_HASH_SQUEEZE_PAGE);
732 : : }
733 : : else /* !RelationNeedsWAL(rel) */
734 : 0 : recptr = XLogGetFakeLSN(rel);
735 : :
736 : : /* Set LSN iff wbuf is modified. */
737 [ + + ]: 138 : if (mod_wbuf)
738 : 71 : PageSetLSN(BufferGetPage(wbuf), recptr);
739 : :
740 : 138 : PageSetLSN(BufferGetPage(ovflbuf), recptr);
741 : :
742 [ + - + + ]: 138 : if (BufferIsValid(prevbuf) && !is_prev_bucket_same_wrt)
743 : 96 : PageSetLSN(BufferGetPage(prevbuf), recptr);
744 [ - + ]: 138 : if (BufferIsValid(nextbuf))
745 : 0 : PageSetLSN(BufferGetPage(nextbuf), recptr);
746 : :
747 : 138 : PageSetLSN(BufferGetPage(mapbuf), recptr);
748 : :
749 [ + + ]: 138 : if (update_metap)
750 : 121 : PageSetLSN(BufferGetPage(metabuf), recptr);
751 : :
752 : 138 : END_CRIT_SECTION();
753 : :
754 : : /* release previous bucket if it is not same as write bucket */
755 [ + - + + ]: 138 : if (BufferIsValid(prevbuf) && prevblkno != writeblkno)
756 : 96 : _hash_relbuf(rel, prevbuf);
757 : :
758 [ + - ]: 138 : if (BufferIsValid(ovflbuf))
759 : 138 : _hash_relbuf(rel, ovflbuf);
760 : :
761 [ - + ]: 138 : if (BufferIsValid(nextbuf))
762 : 0 : _hash_relbuf(rel, nextbuf);
763 : :
764 : 138 : _hash_relbuf(rel, mapbuf);
765 : 138 : _hash_relbuf(rel, metabuf);
766 : :
767 : 138 : return nextblkno;
768 : : }
769 : :
770 : :
771 : : /*
772 : : * _hash_initbitmapbuffer()
773 : : *
774 : : * Initialize a new bitmap page. All bits in the new bitmap page are set to
775 : : * "1", indicating "in use".
776 : : */
777 : : void
778 : 245 : _hash_initbitmapbuffer(Buffer buf, uint16 bmsize, bool initpage)
779 : : {
780 : : Page pg;
781 : : HashPageOpaque op;
782 : : uint32 *freep;
783 : :
784 : 245 : pg = BufferGetPage(buf);
785 : :
786 : : /* initialize the page */
787 [ + + ]: 245 : if (initpage)
788 : 30 : _hash_pageinit(pg, BufferGetPageSize(buf));
789 : :
790 : : /* initialize the page's special space */
791 : 245 : op = HashPageGetOpaque(pg);
792 : 245 : op->hasho_prevblkno = InvalidBlockNumber;
793 : 245 : op->hasho_nextblkno = InvalidBlockNumber;
794 : 245 : op->hasho_bucket = InvalidBucket;
795 : 245 : op->hasho_flag = LH_BITMAP_PAGE;
796 : 245 : op->hasho_page_id = HASHO_PAGE_ID;
797 : :
798 : : /* set all of the bits to 1 */
799 : 245 : freep = HashPageGetBitmap(pg);
800 : 245 : memset(freep, 0xFF, bmsize);
801 : :
802 : : /*
803 : : * Set pd_lower just past the end of the bitmap page data. We could even
804 : : * set pd_lower equal to pd_upper, but this is more precise and makes the
805 : : * page look compressible to xlog.c.
806 : : */
807 : 245 : ((PageHeader) pg)->pd_lower = ((char *) freep + bmsize) - (char *) pg;
808 : 245 : }
809 : :
810 : :
811 : : /*
812 : : * _hash_squeezebucket(rel, bucket)
813 : : *
814 : : * Try to squeeze the tuples onto pages occurring earlier in the
815 : : * bucket chain in an attempt to free overflow pages. When we start
816 : : * the "squeezing", the page from which we start taking tuples (the
817 : : * "read" page) is the last bucket in the bucket chain and the page
818 : : * onto which we start squeezing tuples (the "write" page) is the
819 : : * first page in the bucket chain. The read page works backward and
820 : : * the write page works forward; the procedure terminates when the
821 : : * read page and write page are the same page.
822 : : *
823 : : * At completion of this procedure, it is guaranteed that all pages in
824 : : * the bucket are nonempty, unless the bucket is totally empty (in
825 : : * which case all overflow pages will be freed). The original implementation
826 : : * required that to be true on entry as well, but it's a lot easier for
827 : : * callers to leave empty overflow pages and let this guy clean it up.
828 : : *
829 : : * Caller must acquire cleanup lock on the primary page of the target
830 : : * bucket to exclude any scans that are in progress, which could easily
831 : : * be confused into returning the same tuple more than once or some tuples
832 : : * not at all by the rearrangement we are performing here. To prevent
833 : : * any concurrent scan to cross the squeeze scan we use lock chaining
834 : : * similar to hashbucketcleanup. Refer comments atop hashbucketcleanup.
835 : : *
836 : : * We need to retain a pin on the primary bucket to ensure that no concurrent
837 : : * split can start.
838 : : *
839 : : * Since this function is invoked in VACUUM, we provide an access strategy
840 : : * parameter that controls fetches of the bucket pages.
841 : : */
842 : : void
843 : 791 : _hash_squeezebucket(Relation rel,
844 : : Bucket bucket,
845 : : BlockNumber bucket_blkno,
846 : : Buffer bucket_buf,
847 : : BufferAccessStrategy bstrategy)
848 : : {
849 : : BlockNumber wblkno;
850 : : BlockNumber rblkno;
851 : : Buffer wbuf;
852 : : Buffer rbuf;
853 : : Page wpage;
854 : : Page rpage;
855 : : HashPageOpaque wopaque;
856 : : HashPageOpaque ropaque;
857 : :
858 : : /*
859 : : * start squeezing into the primary bucket page.
860 : : */
861 : 791 : wblkno = bucket_blkno;
862 : 791 : wbuf = bucket_buf;
863 : 791 : wpage = BufferGetPage(wbuf);
864 : 791 : wopaque = HashPageGetOpaque(wpage);
865 : :
866 : : /*
867 : : * if there aren't any overflow pages, there's nothing to squeeze. caller
868 : : * is responsible for releasing the pin on primary bucket page.
869 : : */
870 [ + + ]: 791 : if (!BlockNumberIsValid(wopaque->hasho_nextblkno))
871 : : {
872 : 741 : LockBuffer(wbuf, BUFFER_LOCK_UNLOCK);
873 : 741 : return;
874 : : }
875 : :
876 : : /*
877 : : * Find the last page in the bucket chain by starting at the base bucket
878 : : * page and working forward. Note: we assume that a hash bucket chain is
879 : : * usually smaller than the buffer ring being used by VACUUM, else using
880 : : * the access strategy here would be counterproductive.
881 : : */
882 : 50 : rbuf = InvalidBuffer;
883 : 50 : ropaque = wopaque;
884 : : do
885 : : {
886 : 254 : rblkno = ropaque->hasho_nextblkno;
887 [ + + ]: 254 : if (rbuf != InvalidBuffer)
888 : 204 : _hash_relbuf(rel, rbuf);
889 : 254 : rbuf = _hash_getbuf_with_strategy(rel,
890 : : rblkno,
891 : : HASH_WRITE,
892 : : LH_OVERFLOW_PAGE,
893 : : bstrategy);
894 : 254 : rpage = BufferGetPage(rbuf);
895 : 254 : ropaque = HashPageGetOpaque(rpage);
896 : : Assert(ropaque->hasho_bucket == bucket);
897 [ + + ]: 254 : } while (BlockNumberIsValid(ropaque->hasho_nextblkno));
898 : :
899 : : /*
900 : : * squeeze the tuples.
901 : : */
902 : : for (;;)
903 : 96 : {
904 : : OffsetNumber roffnum;
905 : : OffsetNumber maxroffnum;
906 : : OffsetNumber deletable[MaxOffsetNumber];
907 : : IndexTuple itups[MaxIndexTuplesPerPage];
908 : : Size tups_size[MaxIndexTuplesPerPage];
909 : : OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
910 : 146 : uint16 ndeletable = 0;
911 : 146 : uint16 nitups = 0;
912 : 146 : Size all_tups_size = 0;
913 : : int i;
914 : 146 : bool retain_pin = false;
915 : :
916 : 150 : readpage:
917 : : /* Scan each tuple in "read" page */
918 : 150 : maxroffnum = PageGetMaxOffsetNumber(rpage);
919 : 150 : for (roffnum = FirstOffsetNumber;
920 [ + + ]: 3780 : roffnum <= maxroffnum;
921 : 3630 : roffnum = OffsetNumberNext(roffnum))
922 : : {
923 : : IndexTuple itup;
924 : : Size itemsz;
925 : :
926 : : /* skip dead tuples */
927 [ - + ]: 3642 : if (ItemIdIsDead(PageGetItemId(rpage, roffnum)))
928 : 0 : continue;
929 : :
930 : 3642 : itup = (IndexTuple) PageGetItem(rpage,
931 : 3642 : PageGetItemId(rpage, roffnum));
932 : 3642 : itemsz = IndexTupleSize(itup);
933 : 3642 : itemsz = MAXALIGN(itemsz);
934 : :
935 : : /*
936 : : * Walk up the bucket chain, looking for a page big enough for
937 : : * this item and all other accumulated items. Exit if we reach
938 : : * the read page.
939 : : */
940 [ + + ]: 3746 : while (PageGetFreeSpaceForMultipleTuples(wpage, nitups + 1) < (all_tups_size + itemsz))
941 : : {
942 : 116 : Buffer next_wbuf = InvalidBuffer;
943 : 116 : bool tups_moved = false;
944 : :
945 : : Assert(!PageIsEmpty(wpage));
946 : :
947 [ + + ]: 116 : if (wblkno == bucket_blkno)
948 : 20 : retain_pin = true;
949 : :
950 : 116 : wblkno = wopaque->hasho_nextblkno;
951 : : Assert(BlockNumberIsValid(wblkno));
952 : :
953 : : /* don't need to move to next page if we reached the read page */
954 [ + + ]: 116 : if (wblkno != rblkno)
955 : 108 : next_wbuf = _hash_getbuf_with_strategy(rel,
956 : : wblkno,
957 : : HASH_WRITE,
958 : : LH_OVERFLOW_PAGE,
959 : : bstrategy);
960 : :
961 [ + + ]: 116 : if (nitups > 0)
962 : : {
963 : : XLogRecPtr recptr;
964 : :
965 : : Assert(nitups == ndeletable);
966 : :
967 : : /*
968 : : * This operation needs to log multiple tuples, prepare
969 : : * WAL for that.
970 : : */
971 [ + - - + : 4 : if (RelationNeedsWAL(rel))
- - - - ]
972 : 4 : XLogEnsureRecordSpace(0, 3 + nitups);
973 : :
974 : 4 : START_CRIT_SECTION();
975 : :
976 : : /*
977 : : * we have to insert tuples on the "write" page, being
978 : : * careful to preserve hashkey ordering. (If we insert
979 : : * many tuples into the same "write" page it would be
980 : : * worth qsort'ing them).
981 : : */
982 : 4 : _hash_pgaddmultitup(rel, wbuf, itups, itup_offsets, nitups);
983 : 4 : MarkBufferDirty(wbuf);
984 : :
985 : : /* Delete tuples we already moved off read page */
986 : 4 : PageIndexMultiDelete(rpage, deletable, ndeletable);
987 : 4 : MarkBufferDirty(rbuf);
988 : :
989 : : /* XLOG stuff */
990 [ + - - + : 4 : if (RelationNeedsWAL(rel))
- - - - ]
991 : 4 : {
992 : : xl_hash_move_page_contents xlrec;
993 : :
994 : 4 : xlrec.ntups = nitups;
995 : 4 : xlrec.is_prim_bucket_same_wrt = (wbuf == bucket_buf);
996 : :
997 : 4 : XLogBeginInsert();
998 : 4 : XLogRegisterData(&xlrec, SizeOfHashMovePageContents);
999 : :
1000 : : /*
1001 : : * bucket buffer was not changed, but still needs to
1002 : : * be registered to ensure that we can acquire a
1003 : : * cleanup lock on it during replay.
1004 : : */
1005 [ - + ]: 4 : if (!xlrec.is_prim_bucket_same_wrt)
1006 : : {
1007 : 0 : int flags = REGBUF_STANDARD | REGBUF_NO_IMAGE | REGBUF_NO_CHANGE;
1008 : :
1009 : 0 : XLogRegisterBuffer(0, bucket_buf, flags);
1010 : : }
1011 : :
1012 : 4 : XLogRegisterBuffer(1, wbuf, REGBUF_STANDARD);
1013 : 4 : XLogRegisterBufData(1, itup_offsets,
1014 : : nitups * sizeof(OffsetNumber));
1015 [ + + ]: 1372 : for (i = 0; i < nitups; i++)
1016 : 1368 : XLogRegisterBufData(1, itups[i], tups_size[i]);
1017 : :
1018 : 4 : XLogRegisterBuffer(2, rbuf, REGBUF_STANDARD);
1019 : 4 : XLogRegisterBufData(2, deletable,
1020 : : ndeletable * sizeof(OffsetNumber));
1021 : :
1022 : 4 : recptr = XLogInsert(RM_HASH_ID, XLOG_HASH_MOVE_PAGE_CONTENTS);
1023 : : }
1024 : : else
1025 : 0 : recptr = XLogGetFakeLSN(rel);
1026 : :
1027 : 4 : PageSetLSN(BufferGetPage(wbuf), recptr);
1028 : 4 : PageSetLSN(BufferGetPage(rbuf), recptr);
1029 : :
1030 : 4 : END_CRIT_SECTION();
1031 : :
1032 : 4 : tups_moved = true;
1033 : : }
1034 : :
1035 : : /*
1036 : : * release the lock on previous page after acquiring the lock
1037 : : * on next page
1038 : : */
1039 [ + + ]: 116 : if (retain_pin)
1040 : 20 : LockBuffer(wbuf, BUFFER_LOCK_UNLOCK);
1041 : : else
1042 : 96 : _hash_relbuf(rel, wbuf);
1043 : :
1044 : : /* nothing more to do if we reached the read page */
1045 [ + + ]: 116 : if (rblkno == wblkno)
1046 : : {
1047 : 8 : _hash_relbuf(rel, rbuf);
1048 : 50 : return;
1049 : : }
1050 : :
1051 : 108 : wbuf = next_wbuf;
1052 : 108 : wpage = BufferGetPage(wbuf);
1053 : 108 : wopaque = HashPageGetOpaque(wpage);
1054 : : Assert(wopaque->hasho_bucket == bucket);
1055 : 108 : retain_pin = false;
1056 : :
1057 : : /* be tidy */
1058 [ + + ]: 1476 : for (i = 0; i < nitups; i++)
1059 : 1368 : pfree(itups[i]);
1060 : 108 : nitups = 0;
1061 : 108 : all_tups_size = 0;
1062 : 108 : ndeletable = 0;
1063 : :
1064 : : /*
1065 : : * after moving the tuples, rpage would have been compacted,
1066 : : * so we need to rescan it.
1067 : : */
1068 [ + + ]: 108 : if (tups_moved)
1069 : 4 : goto readpage;
1070 : : }
1071 : :
1072 : : /* remember tuple for deletion from "read" page */
1073 : 3630 : deletable[ndeletable++] = roffnum;
1074 : :
1075 : : /*
1076 : : * we need a copy of index tuples as they can be freed as part of
1077 : : * overflow page, however we need them to write a WAL record in
1078 : : * _hash_freeovflpage.
1079 : : */
1080 : 3630 : itups[nitups] = CopyIndexTuple(itup);
1081 : 3630 : tups_size[nitups++] = itemsz;
1082 : 3630 : all_tups_size += itemsz;
1083 : : }
1084 : :
1085 : : /*
1086 : : * If we reach here, there are no live tuples on the "read" page ---
1087 : : * it was empty when we got to it, or we moved them all. So we can
1088 : : * just free the page without bothering with deleting tuples
1089 : : * individually. Then advance to the previous "read" page.
1090 : : *
1091 : : * Tricky point here: if our read and write pages are adjacent in the
1092 : : * bucket chain, our write lock on wbuf will conflict with
1093 : : * _hash_freeovflpage's attempt to update the sibling links of the
1094 : : * removed page. In that case, we don't need to lock it again.
1095 : : */
1096 : 138 : rblkno = ropaque->hasho_prevblkno;
1097 : : Assert(BlockNumberIsValid(rblkno));
1098 : :
1099 : : /* free this overflow page (releases rbuf) */
1100 : 138 : _hash_freeovflpage(rel, bucket_buf, rbuf, wbuf, itups, itup_offsets,
1101 : : tups_size, nitups, bstrategy);
1102 : :
1103 : : /* be tidy */
1104 [ + + ]: 2400 : for (i = 0; i < nitups; i++)
1105 : 2262 : pfree(itups[i]);
1106 : :
1107 : : /* are we freeing the page adjacent to wbuf? */
1108 [ + + ]: 138 : if (rblkno == wblkno)
1109 : : {
1110 : : /* retain the pin on primary bucket page till end of bucket scan */
1111 [ + + ]: 42 : if (wblkno == bucket_blkno)
1112 : 30 : LockBuffer(wbuf, BUFFER_LOCK_UNLOCK);
1113 : : else
1114 : 12 : _hash_relbuf(rel, wbuf);
1115 : 42 : return;
1116 : : }
1117 : :
1118 : 96 : rbuf = _hash_getbuf_with_strategy(rel,
1119 : : rblkno,
1120 : : HASH_WRITE,
1121 : : LH_OVERFLOW_PAGE,
1122 : : bstrategy);
1123 : 96 : rpage = BufferGetPage(rbuf);
1124 : 96 : ropaque = HashPageGetOpaque(rpage);
1125 : : Assert(ropaque->hasho_bucket == bucket);
1126 : : }
1127 : :
1128 : : /* NOTREACHED */
1129 : : }
|