Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * bufpage.c
4 : : * POSTGRES standard buffer page code.
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/storage/page/bufpage.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "access/itup.h"
19 : : #include "access/xlog.h"
20 : : #include "pgstat.h"
21 : : #include "storage/checksum.h"
22 : : #include "utils/memdebug.h"
23 : : #include "utils/memutils.h"
24 : :
25 : :
26 : : /* GUC variable */
27 : : bool ignore_checksum_failure = false;
28 : :
29 : :
30 : : /* ----------------------------------------------------------------
31 : : * Page support functions
32 : : * ----------------------------------------------------------------
33 : : */
34 : :
35 : : /*
36 : : * PageInit
37 : : * Initializes the contents of a page.
38 : : * Note that we don't calculate an initial checksum here; that's not done
39 : : * until it's time to write.
40 : : */
41 : : void
11052 scrappy@hub.org 42 :CBC 431595 : PageInit(Page page, Size pageSize, Size specialSize)
43 : : {
10626 bruce@momjian.us 44 : 431595 : PageHeader p = (PageHeader) page;
45 : :
9120 tgl@sss.pgh.pa.us 46 : 431595 : specialSize = MAXALIGN(specialSize);
47 : :
10627 bruce@momjian.us 48 [ - + ]: 431595 : Assert(pageSize == BLCKSZ);
8868 49 [ - + ]: 431595 : Assert(pageSize > specialSize + SizeOfPageHeaderData);
50 : :
51 : : /* Make sure all fields of page are zero, as well as unused space */
9036 tgl@sss.pgh.pa.us 52 [ + - + - : 431595 : MemSet(p, 0, pageSize);
+ - - + -
- ]
53 : :
4952 simon@2ndQuadrant.co 54 : 431595 : p->pd_flags = 0;
8868 bruce@momjian.us 55 : 431595 : p->pd_lower = SizeOfPageHeaderData;
10627 56 : 431595 : p->pd_upper = pageSize - specialSize;
57 : 431595 : p->pd_special = pageSize - specialSize;
8806 tgl@sss.pgh.pa.us 58 : 431595 : PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
59 : : /* p->pd_prune_xid = InvalidTransactionId; done by above MemSet */
9417 vadim4o@yahoo.com 60 : 431595 : }
61 : :
62 : :
63 : : /*
64 : : * PageIsVerified
65 : : * Check that the page header and checksum (if any) appear valid.
66 : : *
67 : : * This is called when a page has just been read in from disk. The idea is
68 : : * to cheaply detect trashed pages before we go nuts following bogus line
69 : : * pointers, testing invalid transaction identifiers, etc.
70 : : *
71 : : * It turns out to be necessary to allow zeroed pages here too. Even though
72 : : * this routine is *not* called when deliberately adding a page to a relation,
73 : : * there are scenarios in which a zeroed page might be found in a table.
74 : : * (Example: a backend extends a relation, then crashes before it can write
75 : : * any WAL entry about the new page. The kernel will already have the
76 : : * zeroed page in the file, and it will stay that way after restart.) So we
77 : : * allow zeroed pages here, and are careful that the page access macros
78 : : * treat such a page as empty and without free space. Eventually, VACUUM
79 : : * will clean up such a page and make it usable.
80 : : *
81 : : * If flag PIV_LOG_WARNING/PIV_LOG_LOG is set, a WARNING/LOG message is logged
82 : : * in the event of a checksum failure.
83 : : *
84 : : * If flag PIV_IGNORE_CHECKSUM_FAILURE is set, checksum failures will cause a
85 : : * message about the failure to be emitted, but will not cause
86 : : * PageIsVerified() to return false.
87 : : *
88 : : * To allow the caller to report statistics about checksum failures,
89 : : * *checksum_failure_p can be passed in. Note that there may be checksum
90 : : * failures even if this function returns true, due to
91 : : * PIV_IGNORE_CHECKSUM_FAILURE.
92 : : */
93 : : bool
562 andres@anarazel.de 94 : 1377732 : PageIsVerified(PageData *page, BlockNumber blkno, int flags, bool *checksum_failure_p)
95 : : {
630 peter@eisentraut.org 96 : 1377732 : const PageHeaderData *p = (const PageHeaderData *) page;
97 : : size_t *pagebytes;
4952 simon@2ndQuadrant.co 98 : 1377732 : bool checksum_failure = false;
99 : 1377732 : bool header_sane = false;
tgl@sss.pgh.pa.us 100 : 1377732 : uint16 checksum = 0;
101 : :
562 andres@anarazel.de 102 [ + - ]: 1377732 : if (checksum_failure_p)
103 : 1377732 : *checksum_failure_p = false;
104 : :
105 : : /*
106 : : * Don't verify page data unless the page passes basic non-zero test
107 : : */
4952 simon@2ndQuadrant.co 108 [ + + ]: 1377732 : if (!PageIsNew(page))
109 : : {
110 : : /*
111 : : * There shouldn't be any check for interrupt calls happening in this
112 : : * codepath, but just to be on the safe side we hold interrupts since
113 : : * if they did happen the data checksum state could change during
114 : : * verifying checksums, which could lead to incorrect verification
115 : : * results.
116 : : */
192 dgustafsson@postgres 117 :GNC 1372830 : HOLD_INTERRUPTS();
118 [ + + ]: 1372830 : if (DataChecksumsNeedVerify())
119 : : {
607 peter@eisentraut.org 120 :CBC 1286727 : checksum = pg_checksum_page(page, blkno);
121 : :
4952 simon@2ndQuadrant.co 122 [ + + ]: 1286727 : if (checksum != p->pd_checksum)
123 : : {
124 : 32 : checksum_failure = true;
562 andres@anarazel.de 125 [ + - ]: 32 : if (checksum_failure_p)
126 : 32 : *checksum_failure_p = true;
127 : : }
128 : : }
192 dgustafsson@postgres 129 [ - + ]:GNC 1372830 : RESUME_INTERRUPTS();
130 : :
131 : : /*
132 : : * The following checks don't prove the header is correct, only that
133 : : * it looks sane enough to allow into the buffer pool. Later usage of
134 : : * the block can still reveal problems, which is why we offer the
135 : : * checksum option.
136 : : */
4952 simon@2ndQuadrant.co 137 [ + - ]:CBC 1372830 : if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
4884 bruce@momjian.us 138 [ + - ]: 1372830 : p->pd_lower <= p->pd_upper &&
139 [ + - ]: 1372830 : p->pd_upper <= p->pd_special &&
140 [ + + ]: 1372830 : p->pd_special <= BLCKSZ &&
141 [ + - ]: 1372748 : p->pd_special == MAXALIGN(p->pd_special))
4952 simon@2ndQuadrant.co 142 : 1372748 : header_sane = true;
143 : :
144 [ + + + + ]: 1372830 : if (header_sane && !checksum_failure)
145 : 1372722 : return true;
146 : : }
147 : :
148 : : /* Check all-zeroes case */
3686 andres@anarazel.de 149 : 5010 : pagebytes = (size_t *) page;
150 : :
693 michael@paquier.xyz 151 [ + + ]: 5010 : if (pg_memory_is_all_zeros(pagebytes, BLCKSZ))
4952 simon@2ndQuadrant.co 152 : 4902 : return true;
153 : :
154 : : /*
155 : : * Throw a WARNING/LOG, as instructed by PIV_LOG_*, if the checksum fails,
156 : : * but only after we've checked for the all-zeroes case.
157 : : */
158 [ + + ]: 108 : if (checksum_failure)
159 : : {
561 andres@anarazel.de 160 [ + - ]: 32 : if ((flags & (PIV_LOG_WARNING | PIV_LOG_LOG)) != 0)
161 [ - + + - : 32 : ereport(flags & PIV_LOG_WARNING ? WARNING : LOG,
+ + + + ]
162 : : (errcode(ERRCODE_DATA_CORRUPTED),
163 : : ((flags & PIV_ZERO_BUFFERS_ON_ERROR) && !(flags & PIV_IGNORE_CHECKSUM_FAILURE)) ?
164 : : errmsg("page verification failed, calculated checksum %u but expected %u, buffer will be zeroed",
165 : : checksum, p->pd_checksum) :
166 : : errmsg("page verification failed, calculated checksum %u but expected %u",
167 : : checksum, p->pd_checksum)));
168 : :
169 [ + + + + ]: 32 : if (header_sane && (flags & PIV_IGNORE_CHECKSUM_FAILURE))
4952 simon@2ndQuadrant.co 170 : 12 : return true;
171 : : }
172 : :
173 : 96 : return false;
174 : : }
175 : :
176 : :
177 : : /*
178 : : * PageAddItemExtended
179 : : *
180 : : * Add an item to a page. Return value is the offset at which it was
181 : : * inserted, or InvalidOffsetNumber if the item is not inserted for any
182 : : * reason. A WARNING is issued indicating the reason for the refusal.
183 : : *
184 : : * offsetNumber must be either InvalidOffsetNumber to specify finding a
185 : : * free line pointer, or a value between FirstOffsetNumber and one past
186 : : * the last existing item, to specify using that particular line pointer.
187 : : *
188 : : * If offsetNumber is valid and flag PAI_OVERWRITE is set, we just store
189 : : * the item at the specified offsetNumber, which must be either a
190 : : * currently-unused line pointer, or one past the last existing item.
191 : : *
192 : : * If offsetNumber is valid and flag PAI_OVERWRITE is not set, insert
193 : : * the item at the specified offsetNumber, moving existing items later
194 : : * in the array to make room.
195 : : *
196 : : * If offsetNumber is not valid, then assign a slot by finding the first
197 : : * one that is both unused and deallocated.
198 : : *
199 : : * If flag PAI_IS_HEAP is set, we enforce that there can't be more than
200 : : * MaxHeapTuplesPerPage line pointers on the page.
201 : : *
202 : : * !!! EREPORT(ERROR) IS DISALLOWED HERE !!!
203 : : */
204 : : OffsetNumber
3787 alvherre@alvh.no-ip. 205 : 47293272 : PageAddItemExtended(Page page,
206 : : const void *item,
207 : : Size size,
208 : : OffsetNumber offsetNumber,
209 : : int flags)
210 : : {
9036 tgl@sss.pgh.pa.us 211 : 47293272 : PageHeader phdr = (PageHeader) page;
212 : : Size alignedSize;
213 : : int lower;
214 : : int upper;
215 : : ItemId itemId;
216 : : OffsetNumber limit;
9579 217 : 47293272 : bool needshuffle = false;
218 : :
219 : : /*
220 : : * Be wary about corrupted page pointers
221 : : */
8868 bruce@momjian.us 222 [ + - ]: 47293272 : if (phdr->pd_lower < SizeOfPageHeaderData ||
9036 tgl@sss.pgh.pa.us 223 [ + - ]: 47293272 : phdr->pd_lower > phdr->pd_upper ||
224 [ + - ]: 47293272 : phdr->pd_upper > phdr->pd_special ||
225 [ - + ]: 47293272 : phdr->pd_special > BLCKSZ)
8481 tgl@sss.pgh.pa.us 226 [ # # ]:UBC 0 : ereport(PANIC,
227 : : (errcode(ERRCODE_DATA_CORRUPTED),
228 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
229 : : phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
230 : :
231 : : /*
232 : : * Select offsetNumber to place the new item at
233 : : */
10627 bruce@momjian.us 234 :CBC 47293272 : limit = OffsetNumberNext(PageGetMaxOffsetNumber(page));
235 : :
236 : : /* was offsetNumber passed in? */
237 [ + + + - : 47293272 : if (OffsetNumberIsValid(offsetNumber))
+ + ]
238 : : {
239 : : /* yes, check it */
3787 alvherre@alvh.no-ip. 240 [ + + ]: 29140586 : if ((flags & PAI_OVERWRITE) != 0)
241 : : {
9379 vadim4o@yahoo.com 242 [ + + ]: 1639938 : if (offsetNumber < limit)
243 : : {
1554 peter@eisentraut.org 244 : 29784 : itemId = PageGetItemId(page, offsetNumber);
6970 tgl@sss.pgh.pa.us 245 [ + - - + ]: 29784 : if (ItemIdIsUsed(itemId) || ItemIdHasStorage(itemId))
246 : : {
8481 tgl@sss.pgh.pa.us 247 [ # # ]:UBC 0 : elog(WARNING, "will not overwrite a used ItemId");
9379 vadim4o@yahoo.com 248 : 0 : return InvalidOffsetNumber;
249 : : }
250 : : }
251 : : }
252 : : else
253 : : {
8833 tgl@sss.pgh.pa.us 254 [ + + ]:CBC 27500648 : if (offsetNumber < limit)
3400 255 : 4264104 : needshuffle = true; /* need to move existing linp's */
256 : : }
257 : : }
258 : : else
259 : : {
260 : : /* offsetNumber was not passed in, so find a free slot */
261 : : /* if no free slot, we'll put it at limit (1st open slot) */
1554 peter@eisentraut.org 262 [ + + ]: 18152686 : if (PageHasFreeLinePointers(page))
263 : : {
264 : : /*
265 : : * Scan line pointer array to locate a "recyclable" (unused)
266 : : * ItemId.
267 : : *
268 : : * Always use earlier items first. PageTruncateLinePointerArray
269 : : * can only truncate unused items when they appear as a contiguous
270 : : * group at the end of the line pointer array.
271 : : */
2014 pg@bowt.ie 272 : 247441 : for (offsetNumber = FirstOffsetNumber;
1979 tgl@sss.pgh.pa.us 273 [ + + ]: 13149222 : offsetNumber < limit; /* limit is maxoff+1 */
2014 pg@bowt.ie 274 : 12901781 : offsetNumber++)
275 : : {
1554 peter@eisentraut.org 276 : 13136581 : itemId = PageGetItemId(page, offsetNumber);
277 : :
278 : : /*
279 : : * We check for no storage as well, just to be paranoid;
280 : : * unused items should never have storage. Assert() that the
281 : : * invariant is respected too.
282 : : */
2024 pg@bowt.ie 283 [ + + - + ]: 13136581 : Assert(ItemIdIsUsed(itemId) || !ItemIdHasStorage(itemId));
284 : :
6970 tgl@sss.pgh.pa.us 285 [ + + + - ]: 13136581 : if (!ItemIdIsUsed(itemId) && !ItemIdHasStorage(itemId))
7164 286 : 234800 : break;
287 : : }
288 [ + + ]: 247441 : if (offsetNumber >= limit)
289 : : {
290 : : /* the hint is wrong, so reset it */
1554 peter@eisentraut.org 291 : 12641 : PageClearHasFreeLinePointers(page);
292 : : }
293 : : }
294 : : else
295 : : {
296 : : /* don't bother searching if hint says there's no free slot */
7164 tgl@sss.pgh.pa.us 297 : 17905245 : offsetNumber = limit;
298 : : }
299 : : }
300 : :
301 : : /* Reject placing items beyond the first unused line pointer */
3685 302 [ - + ]: 47293272 : if (offsetNumber > limit)
303 : : {
8481 tgl@sss.pgh.pa.us 304 [ # # ]:UBC 0 : elog(WARNING, "specified item offset is too large");
8833 305 : 0 : return InvalidOffsetNumber;
306 : : }
307 : :
308 : : /* Reject placing items beyond heap boundary, if heap */
3787 alvherre@alvh.no-ip. 309 [ + + - + ]:CBC 47293272 : if ((flags & PAI_IS_HEAP) != 0 && offsetNumber > MaxHeapTuplesPerPage)
310 : : {
6962 tgl@sss.pgh.pa.us 311 [ # # ]:UBC 0 : elog(WARNING, "can't put more than MaxHeapTuplesPerPage items in a heap page");
312 : 0 : return InvalidOffsetNumber;
313 : : }
314 : :
315 : : /*
316 : : * Compute new lower and upper pointers for page, see if it'll fit.
317 : : *
318 : : * Note: do arithmetic as signed ints, to avoid mistakes if, say,
319 : : * alignedSize > pd_upper.
320 : : */
3685 tgl@sss.pgh.pa.us 321 [ + + + + ]:CBC 47293272 : if (offsetNumber == limit || needshuffle)
9036 322 : 47028688 : lower = phdr->pd_lower + sizeof(ItemIdData);
323 : : else
324 : 264584 : lower = phdr->pd_lower;
325 : :
9947 bruce@momjian.us 326 : 47293272 : alignedSize = MAXALIGN(size);
327 : :
9036 tgl@sss.pgh.pa.us 328 : 47293272 : upper = (int) phdr->pd_upper - (int) alignedSize;
329 : :
10627 bruce@momjian.us 330 [ - + ]: 47293272 : if (lower > upper)
10268 bruce@momjian.us 331 :UBC 0 : return InvalidOffsetNumber;
332 : :
333 : : /*
334 : : * OK to insert the item. First, shuffle the existing pointers if needed.
335 : : */
1554 peter@eisentraut.org 336 :CBC 47293272 : itemId = PageGetItemId(page, offsetNumber);
337 : :
8833 tgl@sss.pgh.pa.us 338 [ + + ]: 47293272 : if (needshuffle)
339 : 4264104 : memmove(itemId + 1, itemId,
340 : 4264104 : (limit - offsetNumber) * sizeof(ItemIdData));
341 : :
342 : : /* set the line pointer */
6970 343 : 47293272 : ItemIdSetNormal(itemId, upper, size);
344 : :
345 : : /*
346 : : * Items normally contain no uninitialized bytes. Core bufpage consumers
347 : : * conform, but this is not a necessary coding rule; a new index AM could
348 : : * opt to depart from it. However, data type input functions and other
349 : : * C-language functions that synthesize datums should initialize all
350 : : * bytes; datumIsEqual() relies on this. Testing here, along with the
351 : : * similar check in printtup(), helps to catch such mistakes.
352 : : *
353 : : * Values of the "name" type retrieved via index-only scans may contain
354 : : * uninitialized bytes; see comment in btrescan(). Valgrind will report
355 : : * this as an error, but it is safe to ignore.
356 : : */
357 : : VALGRIND_CHECK_MEM_IS_DEFINED(item, size);
358 : :
359 : : /* copy the item's data onto the page */
8833 360 : 47293272 : memcpy((char *) page + upper, item, size);
361 : :
362 : : /* adjust page header */
9036 363 : 47293272 : phdr->pd_lower = (LocationIndex) lower;
364 : 47293272 : phdr->pd_upper = (LocationIndex) upper;
365 : :
10268 bruce@momjian.us 366 : 47293272 : return offsetNumber;
367 : : }
368 : :
369 : :
370 : : /*
371 : : * PageGetTempPage
372 : : * Get a temporary page in local memory for special processing.
373 : : * The returned page is not initialized at all; caller must do that.
374 : : */
375 : : Page
630 peter@eisentraut.org 376 : 136 : PageGetTempPage(const PageData *page)
377 : : {
378 : : Size pageSize;
379 : : Page temp;
380 : :
6552 tgl@sss.pgh.pa.us 381 : 136 : pageSize = PageGetPageSize(page);
382 : 136 : temp = (Page) palloc(pageSize);
383 : :
384 : 136 : return temp;
385 : : }
386 : :
387 : : /*
388 : : * PageGetTempPageCopy
389 : : * Get a temporary page in local memory for special processing.
390 : : * The page is initialized by copying the contents of the given page.
391 : : */
392 : : Page
630 peter@eisentraut.org 393 : 7229 : PageGetTempPageCopy(const PageData *page)
394 : : {
395 : : Size pageSize;
396 : : Page temp;
397 : :
10627 bruce@momjian.us 398 : 7229 : pageSize = PageGetPageSize(page);
9579 tgl@sss.pgh.pa.us 399 : 7229 : temp = (Page) palloc(pageSize);
400 : :
9120 401 : 7229 : memcpy(temp, page, pageSize);
402 : :
6552 403 : 7229 : return temp;
404 : : }
405 : :
406 : : /*
407 : : * PageGetTempPageCopySpecial
408 : : * Get a temporary page in local memory for special processing.
409 : : * The page is PageInit'd with the same special-space size as the
410 : : * given page, and the special space is copied from the given page.
411 : : */
412 : : Page
630 peter@eisentraut.org 413 : 36606 : PageGetTempPageCopySpecial(const PageData *page)
414 : : {
415 : : Size pageSize;
416 : : Page temp;
417 : :
6552 tgl@sss.pgh.pa.us 418 : 36606 : pageSize = PageGetPageSize(page);
419 : 36606 : temp = (Page) palloc(pageSize);
420 : :
421 : 36606 : PageInit(temp, pageSize, PageGetSpecialSize(page));
422 : 109818 : memcpy(PageGetSpecialPointer(temp),
423 : 36606 : PageGetSpecialPointer(page),
424 : 36606 : PageGetSpecialSize(page));
425 : :
10268 bruce@momjian.us 426 : 36606 : return temp;
427 : : }
428 : :
429 : : /*
430 : : * PageRestoreTempPage
431 : : * Copy temporary page back to permanent page after special processing
432 : : * and release the temporary page.
433 : : */
434 : : void
11052 scrappy@hub.org 435 : 34080 : PageRestoreTempPage(Page tempPage, Page oldPage)
436 : : {
437 : : Size pageSize;
438 : :
10627 bruce@momjian.us 439 : 34080 : pageSize = PageGetPageSize(tempPage);
607 peter@eisentraut.org 440 : 34080 : memcpy(oldPage, tempPage, pageSize);
441 : :
10627 bruce@momjian.us 442 : 34080 : pfree(tempPage);
11052 scrappy@hub.org 443 : 34080 : }
444 : :
445 : : /*
446 : : * Tuple defrag support for PageRepairFragmentation and PageIndexMultiDelete
447 : : */
448 : : typedef struct itemIdCompactData
449 : : {
450 : : uint16 offsetindex; /* linp array index */
451 : : int16 itemoff; /* page offset of item data */
452 : : uint16 alignedlen; /* MAXALIGN(item data len) */
453 : : } itemIdCompactData;
454 : : typedef itemIdCompactData *itemIdCompact;
455 : :
456 : : /*
457 : : * After removing or marking some line pointers unused, move the tuples to
458 : : * remove the gaps caused by the removed items and reorder them back into
459 : : * reverse line pointer order in the page.
460 : : *
461 : : * This function can often be fairly hot, so it pays to take some measures to
462 : : * make it as optimal as possible.
463 : : *
464 : : * Callers may pass 'presorted' as true if the 'itemidbase' array is sorted in
465 : : * descending order of itemoff. When this is true we can just memmove()
466 : : * tuples towards the end of the page. This is quite a common case as it's
467 : : * the order that tuples are initially inserted into pages. When we call this
468 : : * function to defragment the tuples in the page then any new line pointers
469 : : * added to the page will keep that presorted order, so hitting this case is
470 : : * still very common for tables that are commonly updated.
471 : : *
472 : : * When the 'itemidbase' array is not presorted then we're unable to just
473 : : * memmove() tuples around freely. Doing so could cause us to overwrite the
474 : : * memory belonging to a tuple we've not moved yet. In this case, we copy all
475 : : * the tuples that need to be moved into a temporary buffer. We can then
476 : : * simply memcpy() out of that temp buffer back into the page at the correct
477 : : * location. Tuples are copied back into the page in the same order as the
478 : : * 'itemidbase' array, so we end up reordering the tuples back into reverse
479 : : * line pointer order. This will increase the chances of hitting the
480 : : * presorted case the next time around.
481 : : *
482 : : * Callers must ensure that nitems is > 0
483 : : */
484 : : static void
2217 drowley@postgresql.o 485 : 84200 : compactify_tuples(itemIdCompact itemidbase, int nitems, Page page, bool presorted)
486 : : {
4269 heikki.linnakangas@i 487 : 84200 : PageHeader phdr = (PageHeader) page;
488 : : Offset upper;
489 : : Offset copy_tail;
490 : : Offset copy_head;
491 : : itemIdCompact itemidptr;
492 : : int i;
493 : :
494 : : /* Code within will not work correctly if nitems == 0 */
2217 drowley@postgresql.o 495 [ - + ]: 84200 : Assert(nitems > 0);
496 : :
497 [ + + ]: 84200 : if (presorted)
498 : : {
499 : :
500 : : #ifdef USE_ASSERT_CHECKING
501 : : {
502 : : /*
503 : : * Verify we've not gotten any new callers that are incorrectly
504 : : * passing a true presorted value.
505 : : */
506 : 59757 : Offset lastoff = phdr->pd_special;
507 : :
508 [ + + ]: 2250353 : for (i = 0; i < nitems; i++)
509 : : {
510 : 2190596 : itemidptr = &itemidbase[i];
511 : :
512 [ - + ]: 2190596 : Assert(lastoff > itemidptr->itemoff);
513 : :
514 : 2190596 : lastoff = itemidptr->itemoff;
515 : : }
516 : : }
517 : : #endif /* USE_ASSERT_CHECKING */
518 : :
519 : : /*
520 : : * 'itemidbase' is already in the optimal order, i.e, lower item
521 : : * pointers have a higher offset. This allows us to memmove() the
522 : : * tuples up to the end of the page without having to worry about
523 : : * overwriting other tuples that have not been moved yet.
524 : : *
525 : : * There's a good chance that there are tuples already right at the
526 : : * end of the page that we can simply skip over because they're
527 : : * already in the correct location within the page. We'll do that
528 : : * first...
529 : : */
530 : 59757 : upper = phdr->pd_special;
531 : 59757 : i = 0;
532 : : do
533 : : {
534 : 920390 : itemidptr = &itemidbase[i];
535 [ + + ]: 920390 : if (upper != itemidptr->itemoff + itemidptr->alignedlen)
536 : 50960 : break;
537 : 869430 : upper -= itemidptr->alignedlen;
538 : :
539 : 869430 : i++;
540 [ + + ]: 869430 : } while (i < nitems);
541 : :
542 : : /*
543 : : * Now that we've found the first tuple that needs to be moved, we can
544 : : * do the tuple compactification. We try and make the least number of
545 : : * memmove() calls and only call memmove() when there's a gap. When
546 : : * we see a gap we just move all tuples after the gap up until the
547 : : * point of the last move operation.
548 : : */
549 : 59757 : copy_tail = copy_head = itemidptr->itemoff + itemidptr->alignedlen;
550 [ + + ]: 1380923 : for (; i < nitems; i++)
551 : : {
552 : : ItemId lp;
553 : :
554 : 1321166 : itemidptr = &itemidbase[i];
555 : 1321166 : lp = PageGetItemId(page, itemidptr->offsetindex + 1);
556 : :
557 [ + + ]: 1321166 : if (copy_head != itemidptr->itemoff + itemidptr->alignedlen)
558 : : {
559 : 158750 : memmove((char *) page + upper,
560 : 158750 : page + copy_head,
561 : 158750 : copy_tail - copy_head);
562 : :
563 : : /*
564 : : * We've now moved all tuples already seen, but not the
565 : : * current tuple, so we set the copy_tail to the end of this
566 : : * tuple so it can be moved in another iteration of the loop.
567 : : */
568 : 158750 : copy_tail = itemidptr->itemoff + itemidptr->alignedlen;
569 : : }
570 : : /* shift the target offset down by the length of this tuple */
571 : 1321166 : upper -= itemidptr->alignedlen;
572 : : /* point the copy_head to the start of this tuple */
573 : 1321166 : copy_head = itemidptr->itemoff;
574 : :
575 : : /* update the line pointer to reference the new offset */
576 : 1321166 : lp->lp_off = upper;
577 : : }
578 : :
579 : : /* move the remaining tuples. */
4269 heikki.linnakangas@i 580 : 59757 : memmove((char *) page + upper,
2217 drowley@postgresql.o 581 : 59757 : page + copy_head,
582 : 59757 : copy_tail - copy_head);
583 : : }
584 : : else
585 : : {
586 : : PGAlignedBlock scratch;
587 : 24443 : char *scratchptr = scratch.data;
588 : :
589 : : /*
590 : : * Non-presorted case: The tuples in the itemidbase array may be in
591 : : * any order. So, in order to move these to the end of the page we
592 : : * must make a temp copy of each tuple that needs to be moved before
593 : : * we copy them back into the page at the new offset.
594 : : *
595 : : * If a large percentage of tuples have been pruned (>75%) then we'll
596 : : * copy these into the temp buffer tuple-by-tuple, otherwise, we'll
597 : : * just do a single memcpy() for all tuples that need to be moved.
598 : : * When so many tuples have been removed there's likely to be a lot of
599 : : * gaps and it's unlikely that many non-movable tuples remain at the
600 : : * end of the page.
601 : : */
602 [ + + ]: 24443 : if (nitems < PageGetMaxOffsetNumber(page) / 4)
603 : : {
604 : 1664 : i = 0;
605 : : do
606 : : {
607 : 33381 : itemidptr = &itemidbase[i];
608 : 33381 : memcpy(scratchptr + itemidptr->itemoff, page + itemidptr->itemoff,
609 : 33381 : itemidptr->alignedlen);
610 : 33381 : i++;
611 [ + + ]: 33381 : } while (i < nitems);
612 : :
613 : : /* Set things up for the compactification code below */
614 : 1664 : i = 0;
615 : 1664 : itemidptr = &itemidbase[0];
616 : 1664 : upper = phdr->pd_special;
617 : : }
618 : : else
619 : : {
620 : 22779 : upper = phdr->pd_special;
621 : :
622 : : /*
623 : : * Many tuples are likely to already be in the correct location.
624 : : * There's no need to copy these into the temp buffer. Instead
625 : : * we'll just skip forward in the itemidbase array to the position
626 : : * that we do need to move tuples from so that the code below just
627 : : * leaves these ones alone.
628 : : */
629 : 22779 : i = 0;
630 : : do
631 : : {
632 : 532704 : itemidptr = &itemidbase[i];
633 [ + + ]: 532704 : if (upper != itemidptr->itemoff + itemidptr->alignedlen)
634 : 22779 : break;
635 : 509925 : upper -= itemidptr->alignedlen;
636 : :
637 : 509925 : i++;
638 [ + - ]: 509925 : } while (i < nitems);
639 : :
640 : : /* Copy all tuples that need to be moved into the temp buffer */
641 : 22779 : memcpy(scratchptr + phdr->pd_upper,
642 : 22779 : page + phdr->pd_upper,
643 : 22779 : upper - phdr->pd_upper);
644 : : }
645 : :
646 : : /*
647 : : * Do the tuple compactification. itemidptr is already pointing to
648 : : * the first tuple that we're going to move. Here we collapse the
649 : : * memcpy calls for adjacent tuples into a single call. This is done
650 : : * by delaying the memcpy call until we find a gap that needs to be
651 : : * closed.
652 : : */
653 : 24443 : copy_tail = copy_head = itemidptr->itemoff + itemidptr->alignedlen;
654 [ + + ]: 2346607 : for (; i < nitems; i++)
655 : : {
656 : : ItemId lp;
657 : :
658 : 2322164 : itemidptr = &itemidbase[i];
659 : 2322164 : lp = PageGetItemId(page, itemidptr->offsetindex + 1);
660 : :
661 : : /* copy pending tuples when we detect a gap */
662 [ + + ]: 2322164 : if (copy_head != itemidptr->itemoff + itemidptr->alignedlen)
663 : : {
664 : 638340 : memcpy((char *) page + upper,
665 : 638340 : scratchptr + copy_head,
666 : 638340 : copy_tail - copy_head);
667 : :
668 : : /*
669 : : * We've now copied all tuples already seen, but not the
670 : : * current tuple, so we set the copy_tail to the end of this
671 : : * tuple.
672 : : */
673 : 638340 : copy_tail = itemidptr->itemoff + itemidptr->alignedlen;
674 : : }
675 : : /* shift the target offset down by the length of this tuple */
676 : 2322164 : upper -= itemidptr->alignedlen;
677 : : /* point the copy_head to the start of this tuple */
678 : 2322164 : copy_head = itemidptr->itemoff;
679 : :
680 : : /* update the line pointer to reference the new offset */
681 : 2322164 : lp->lp_off = upper;
682 : : }
683 : :
684 : : /* Copy the remaining chunk */
685 : 24443 : memcpy((char *) page + upper,
686 : 24443 : scratchptr + copy_head,
687 : 24443 : copy_tail - copy_head);
688 : : }
689 : :
4269 heikki.linnakangas@i 690 : 84200 : phdr->pd_upper = upper;
691 : 84200 : }
692 : :
693 : : /*
694 : : * PageRepairFragmentation
695 : : *
696 : : * Frees fragmented space on a heap page following pruning.
697 : : *
698 : : * This routine is usable for heap pages only, but see PageIndexMultiDelete.
699 : : *
700 : : * This routine removes unused line pointers from the end of the line pointer
701 : : * array. This is possible when dead heap-only tuples get removed by pruning,
702 : : * especially when there were HOT chains with several tuples each beforehand.
703 : : *
704 : : * Caller had better have a full cleanup lock on page's buffer. As a side
705 : : * effect the page's PD_HAS_FREE_LINES hint bit will be set or unset as
706 : : * needed. Caller might also need to account for a reduction in the length of
707 : : * the line pointer array following array truncation.
708 : : */
709 : : void
6962 tgl@sss.pgh.pa.us 710 : 81578 : PageRepairFragmentation(Page page)
711 : : {
9120 712 : 81578 : Offset pd_lower = ((PageHeader) page)->pd_lower;
713 : 81578 : Offset pd_upper = ((PageHeader) page)->pd_upper;
714 : 81578 : Offset pd_special = ((PageHeader) page)->pd_special;
715 : : Offset last_offset;
716 : : itemIdCompactData itemidbase[MaxHeapTuplesPerPage];
717 : : itemIdCompact itemidptr;
718 : : ItemId lp;
719 : : int nline,
720 : : nstorage,
721 : : nunused;
1649 pg@bowt.ie 722 : 81578 : OffsetNumber finalusedlp = InvalidOffsetNumber;
723 : : int i;
724 : : Size totallen;
2217 drowley@postgresql.o 725 : 81578 : bool presorted = true; /* For now */
726 : :
727 : : /*
728 : : * It's worth the trouble to be more paranoid here than in most places,
729 : : * because we are about to reshuffle data in (what is usually) a shared
730 : : * disk buffer. If we aren't careful then corrupted pointers, lengths,
731 : : * etc could cause us to clobber adjacent disk buffers, spreading the data
732 : : * loss further. So, check everything.
733 : : */
8868 bruce@momjian.us 734 [ + - + - ]: 81578 : if (pd_lower < SizeOfPageHeaderData ||
9120 tgl@sss.pgh.pa.us 735 [ + - ]: 81578 : pd_lower > pd_upper ||
736 [ + - ]: 81578 : pd_upper > pd_special ||
737 : 81578 : pd_special > BLCKSZ ||
738 [ - + ]: 81578 : pd_special != MAXALIGN(pd_special))
8481 tgl@sss.pgh.pa.us 739 [ # # ]:UBC 0 : ereport(ERROR,
740 : : (errcode(ERRCODE_DATA_CORRUPTED),
741 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
742 : : pd_lower, pd_upper, pd_special)));
743 : :
744 : : /*
745 : : * Run through the line pointer array and collect data about live items.
746 : : */
9120 tgl@sss.pgh.pa.us 747 :CBC 81578 : nline = PageGetMaxOffsetNumber(page);
3265 748 : 81578 : itemidptr = itemidbase;
749 : 81578 : nunused = totallen = 0;
2217 drowley@postgresql.o 750 : 81578 : last_offset = pd_special;
6962 tgl@sss.pgh.pa.us 751 [ + + ]: 8257469 : for (i = FirstOffsetNumber; i <= nline; i++)
752 : : {
753 : 8175891 : lp = PageGetItemId(page, i);
7690 bruce@momjian.us 754 [ + + ]: 8175891 : if (ItemIdIsUsed(lp))
755 : : {
6970 tgl@sss.pgh.pa.us 756 [ + + ]: 7782506 : if (ItemIdHasStorage(lp))
757 : : {
3265 758 : 2076549 : itemidptr->offsetindex = i - 1;
759 : 2076549 : itemidptr->itemoff = ItemIdGetOffset(lp);
760 : :
2217 drowley@postgresql.o 761 [ + + ]: 2076549 : if (last_offset > itemidptr->itemoff)
762 : 1752963 : last_offset = itemidptr->itemoff;
763 : : else
764 : 323586 : presorted = false;
765 : :
3265 tgl@sss.pgh.pa.us 766 [ + - - + : 2076549 : if (unlikely(itemidptr->itemoff < (int) pd_upper ||
- + ]
767 : : itemidptr->itemoff >= (int) pd_special))
3265 tgl@sss.pgh.pa.us 768 [ # # ]:UBC 0 : ereport(ERROR,
769 : : (errcode(ERRCODE_DATA_CORRUPTED),
770 : : errmsg("corrupted line pointer: %u",
771 : : itemidptr->itemoff)));
3265 tgl@sss.pgh.pa.us 772 :CBC 2076549 : itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp));
773 : 2076549 : totallen += itemidptr->alignedlen;
774 : 2076549 : itemidptr++;
775 : : }
776 : :
1649 pg@bowt.ie 777 : 7782506 : finalusedlp = i; /* Could be the final non-LP_UNUSED item */
778 : : }
779 : : else
780 : : {
781 : : /* Unused entries should have lp_len = 0, but make sure */
782 [ - + ]: 393385 : Assert(!ItemIdHasStorage(lp));
6970 tgl@sss.pgh.pa.us 783 : 393385 : ItemIdSetUnused(lp);
6962 784 : 393385 : nunused++;
785 : : }
786 : : }
787 : :
3265 788 : 81578 : nstorage = itemidptr - itemidbase;
6970 789 [ + + ]: 81578 : if (nstorage == 0)
790 : : {
791 : : /* Page is completely empty, so just reset it quickly */
7798 792 : 23941 : ((PageHeader) page)->pd_upper = pd_special;
793 : : }
794 : : else
795 : : {
796 : : /* Need to compact the page the hard way */
9120 797 [ - + ]: 57637 : if (totallen > (Size) (pd_special - pd_lower))
8481 tgl@sss.pgh.pa.us 798 [ # # ]:UBC 0 : ereport(ERROR,
799 : : (errcode(ERRCODE_DATA_CORRUPTED),
800 : : errmsg("corrupted item lengths: total %zu, available space %u",
801 : : totallen, pd_special - pd_lower)));
802 : :
2217 drowley@postgresql.o 803 :CBC 57637 : compactify_tuples(itemidbase, nstorage, page, presorted);
804 : : }
805 : :
1649 pg@bowt.ie 806 [ + + ]: 81578 : if (finalusedlp != nline)
807 : : {
808 : : /* The last line pointer is not the last used line pointer */
1614 tgl@sss.pgh.pa.us 809 : 2500 : int nunusedend = nline - finalusedlp;
810 : :
1649 pg@bowt.ie 811 [ + - - + ]: 2500 : Assert(nunused >= nunusedend && nunusedend > 0);
812 : :
813 : : /* remove trailing unused line pointers from the count */
814 : 2500 : nunused -= nunusedend;
815 : : /* truncate the line pointer array */
816 : 2500 : ((PageHeader) page)->pd_lower -= (sizeof(ItemIdData) * nunusedend);
817 : : }
818 : :
819 : : /* Set hint bit for PageAddItemExtended */
6962 tgl@sss.pgh.pa.us 820 [ + + ]: 81578 : if (nunused > 0)
7164 821 : 21295 : PageSetHasFreeLinePointers(page);
822 : : else
823 : 60283 : PageClearHasFreeLinePointers(page);
11052 scrappy@hub.org 824 : 81578 : }
825 : :
826 : : /*
827 : : * PageTruncateLinePointerArray
828 : : *
829 : : * Removes unused line pointers at the end of the line pointer array.
830 : : *
831 : : * This routine is usable for heap pages only. It is called by VACUUM during
832 : : * its second pass over the heap. We expect at least one LP_UNUSED line
833 : : * pointer on the page (if VACUUM didn't have an LP_DEAD item on the page that
834 : : * it just set to LP_UNUSED then it should not call here).
835 : : *
836 : : * We avoid truncating the line pointer array to 0 items, if necessary by
837 : : * leaving behind a single remaining LP_UNUSED item. This is a little
838 : : * arbitrary, but it seems like a good idea to avoid leaving a PageIsEmpty()
839 : : * page behind.
840 : : *
841 : : * Caller can have either an exclusive lock or a full cleanup lock on page's
842 : : * buffer. The page's PD_HAS_FREE_LINES hint bit will be set or unset based
843 : : * on whether or not we leave behind any remaining LP_UNUSED items.
844 : : */
845 : : void
2014 pg@bowt.ie 846 : 25968 : PageTruncateLinePointerArray(Page page)
847 : : {
848 : 25968 : PageHeader phdr = (PageHeader) page;
849 : 25968 : bool countdone = false,
850 : 25968 : sethint = false;
851 : 25968 : int nunusedend = 0;
852 : :
853 : : /* Scan line pointer array back-to-front */
854 [ + + ]: 1470746 : for (int i = PageGetMaxOffsetNumber(page); i >= FirstOffsetNumber; i--)
855 : : {
856 : 1469664 : ItemId lp = PageGetItemId(page, i);
857 : :
858 [ + + + + ]: 1469664 : if (!countdone && i > FirstOffsetNumber)
859 : : {
860 : : /*
861 : : * Still determining which line pointers from the end of the array
862 : : * will be truncated away. Either count another line pointer as
863 : : * safe to truncate, or notice that it's not safe to truncate
864 : : * additional line pointers (stop counting line pointers).
865 : : */
866 [ + + ]: 1299840 : if (!ItemIdIsUsed(lp))
867 : 1286569 : nunusedend++;
868 : : else
869 : 13271 : countdone = true;
870 : : }
871 : : else
872 : : {
873 : : /*
874 : : * Once we've stopped counting we still need to figure out if
875 : : * there are any remaining LP_UNUSED line pointers somewhere more
876 : : * towards the front of the array.
877 : : */
878 [ + + ]: 169824 : if (!ItemIdIsUsed(lp))
879 : : {
880 : : /*
881 : : * This is an unused line pointer that we won't be truncating
882 : : * away -- so there is at least one. Set hint on page.
883 : : */
884 : 24886 : sethint = true;
885 : 24886 : break;
886 : : }
887 : : }
888 : : }
889 : :
890 [ + + ]: 25968 : if (nunusedend > 0)
891 : : {
892 : 17591 : phdr->pd_lower -= sizeof(ItemIdData) * nunusedend;
893 : :
894 : : #ifdef CLOBBER_FREED_MEMORY
895 : 17591 : memset((char *) page + phdr->pd_lower, 0x7F,
896 : : sizeof(ItemIdData) * nunusedend);
897 : : #endif
898 : : }
899 : : else
900 [ - + ]: 8377 : Assert(sethint);
901 : :
902 : : /* Set hint bit for PageAddItemExtended */
903 [ + + ]: 25968 : if (sethint)
904 : 24886 : PageSetHasFreeLinePointers(page);
905 : : else
906 : 1082 : PageClearHasFreeLinePointers(page);
907 : 25968 : }
908 : :
909 : : /*
910 : : * PageGetFreeSpace
911 : : * Returns the size of the free (allocatable) space on a page,
912 : : * reduced by the space needed for a new line pointer.
913 : : *
914 : : * Note: this should usually only be used on index pages. Use
915 : : * PageGetHeapFreeSpace on heap pages.
916 : : */
917 : : Size
630 peter@eisentraut.org 918 : 42719513 : PageGetFreeSpace(const PageData *page)
919 : : {
920 : 42719513 : const PageHeaderData *phdr = (const PageHeaderData *) page;
921 : : int space;
922 : :
923 : : /*
924 : : * Use signed arithmetic here so that we behave sensibly if pd_lower >
925 : : * pd_upper.
926 : : */
927 : 42719513 : space = (int) phdr->pd_upper - (int) phdr->pd_lower;
928 : :
9036 tgl@sss.pgh.pa.us 929 [ + + ]: 42719513 : if (space < (int) sizeof(ItemIdData))
10268 bruce@momjian.us 930 : 7121 : return 0;
7173 931 : 42712392 : space -= sizeof(ItemIdData);
932 : :
933 : 42712392 : return (Size) space;
934 : : }
935 : :
936 : : /*
937 : : * PageGetFreeSpaceForMultipleTuples
938 : : * Returns the size of the free (allocatable) space on a page,
939 : : * reduced by the space needed for multiple new line pointers.
940 : : *
941 : : * Note: this should usually only be used on index pages. Use
942 : : * PageGetHeapFreeSpace on heap pages.
943 : : */
944 : : Size
630 peter@eisentraut.org 945 : 87418 : PageGetFreeSpaceForMultipleTuples(const PageData *page, int ntups)
946 : : {
947 : 87418 : const PageHeaderData *phdr = (const PageHeaderData *) page;
948 : : int space;
949 : :
950 : : /*
951 : : * Use signed arithmetic here so that we behave sensibly if pd_lower >
952 : : * pd_upper.
953 : : */
954 : 87418 : space = (int) phdr->pd_upper - (int) phdr->pd_lower;
955 : :
3514 rhaas@postgresql.org 956 [ - + ]: 87418 : if (space < (int) (ntups * sizeof(ItemIdData)))
3514 rhaas@postgresql.org 957 :UBC 0 : return 0;
3514 rhaas@postgresql.org 958 :CBC 87418 : space -= ntups * sizeof(ItemIdData);
959 : :
960 : 87418 : return (Size) space;
961 : : }
962 : :
963 : : /*
964 : : * PageGetExactFreeSpace
965 : : * Returns the size of the free (allocatable) space on a page,
966 : : * without any consideration for adding/removing line pointers.
967 : : */
968 : : Size
630 peter@eisentraut.org 969 : 2162777 : PageGetExactFreeSpace(const PageData *page)
970 : : {
971 : 2162777 : const PageHeaderData *phdr = (const PageHeaderData *) page;
972 : : int space;
973 : :
974 : : /*
975 : : * Use signed arithmetic here so that we behave sensibly if pd_lower >
976 : : * pd_upper.
977 : : */
978 : 2162777 : space = (int) phdr->pd_upper - (int) phdr->pd_lower;
979 : :
6819 tgl@sss.pgh.pa.us 980 [ - + ]: 2162777 : if (space < 0)
6819 tgl@sss.pgh.pa.us 981 :UBC 0 : return 0;
982 : :
9036 tgl@sss.pgh.pa.us 983 :CBC 2162777 : return (Size) space;
984 : : }
985 : :
986 : :
987 : : /*
988 : : * PageGetHeapFreeSpace
989 : : * Returns the size of the free (allocatable) space on a page,
990 : : * reduced by the space needed for a new line pointer.
991 : : *
992 : : * The difference between this and PageGetFreeSpace is that this will return
993 : : * zero if there are already MaxHeapTuplesPerPage line pointers in the page
994 : : * and none are free. We use this to enforce that no more than
995 : : * MaxHeapTuplesPerPage line pointers are created on a heap page. (Although
996 : : * no more tuples than that could fit anyway, in the presence of redirected
997 : : * or dead line pointers it'd be possible to have too many line pointers.
998 : : * To avoid breaking code that assumes MaxHeapTuplesPerPage is a hard limit
999 : : * on the number of line pointers, we make this extra check.)
1000 : : */
1001 : : Size
630 peter@eisentraut.org 1002 : 22840616 : PageGetHeapFreeSpace(const PageData *page)
1003 : : {
1004 : : Size space;
1005 : :
6962 tgl@sss.pgh.pa.us 1006 : 22840616 : space = PageGetFreeSpace(page);
1007 [ + + ]: 22840616 : if (space > 0)
1008 : : {
1009 : : OffsetNumber offnum,
1010 : : nline;
1011 : :
1012 : : /*
1013 : : * Are there already MaxHeapTuplesPerPage line pointers in the page?
1014 : : */
1015 : 22820426 : nline = PageGetMaxOffsetNumber(page);
1016 [ + + ]: 22820426 : if (nline >= MaxHeapTuplesPerPage)
1017 : : {
1554 peter@eisentraut.org 1018 [ + + ]: 6136 : if (PageHasFreeLinePointers(page))
1019 : : {
1020 : : /*
1021 : : * Since this is just a hint, we must confirm that there is
1022 : : * indeed a free line pointer
1023 : : */
6726 bruce@momjian.us 1024 [ + + ]: 533057 : for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum))
1025 : : {
630 peter@eisentraut.org 1026 : 532989 : ItemId lp = PageGetItemId(unconstify(PageData *, page), offnum);
1027 : :
6962 tgl@sss.pgh.pa.us 1028 [ + + ]: 532989 : if (!ItemIdIsUsed(lp))
1029 : 4468 : break;
1030 : : }
1031 : :
1032 [ + + ]: 4536 : if (offnum > nline)
1033 : : {
1034 : : /*
1035 : : * The hint is wrong, but we can't clear it here since we
1036 : : * don't have the ability to mark the page dirty.
1037 : : */
1038 : 68 : space = 0;
1039 : : }
1040 : : }
1041 : : else
1042 : : {
1043 : : /*
1044 : : * Although the hint might be wrong, PageAddItem will believe
1045 : : * it anyway, so we must believe it too.
1046 : : */
1047 : 1600 : space = 0;
1048 : : }
1049 : : }
1050 : : }
1051 : 22840616 : return space;
1052 : : }
1053 : :
1054 : :
1055 : : /*
1056 : : * PageIndexTupleDelete
1057 : : *
1058 : : * This routine does the work of removing a tuple from an index page.
1059 : : *
1060 : : * Unlike heap pages, we compact out the line pointer for the removed tuple.
1061 : : */
1062 : : void
11052 scrappy@hub.org 1063 : 629580 : PageIndexTupleDelete(Page page, OffsetNumber offnum)
1064 : : {
9036 tgl@sss.pgh.pa.us 1065 : 629580 : PageHeader phdr = (PageHeader) page;
1066 : : char *addr;
1067 : : ItemId tup;
1068 : : Size size;
1069 : : unsigned offset;
1070 : : int nbytes;
1071 : : int offidx;
1072 : : int nline;
1073 : :
1074 : : /*
1075 : : * As with PageRepairFragmentation, paranoia seems justified.
1076 : : */
8868 bruce@momjian.us 1077 [ + - ]: 629580 : if (phdr->pd_lower < SizeOfPageHeaderData ||
9036 tgl@sss.pgh.pa.us 1078 [ + - ]: 629580 : phdr->pd_lower > phdr->pd_upper ||
1079 [ + - ]: 629580 : phdr->pd_upper > phdr->pd_special ||
3685 1080 [ + - ]: 629580 : phdr->pd_special > BLCKSZ ||
1081 [ - + ]: 629580 : phdr->pd_special != MAXALIGN(phdr->pd_special))
8481 tgl@sss.pgh.pa.us 1082 [ # # ]:UBC 0 : ereport(ERROR,
1083 : : (errcode(ERRCODE_DATA_CORRUPTED),
1084 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
1085 : : phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
1086 : :
9036 tgl@sss.pgh.pa.us 1087 :CBC 629580 : nline = PageGetMaxOffsetNumber(page);
1088 [ + - - + ]: 629580 : if ((int) offnum <= 0 || (int) offnum > nline)
8481 tgl@sss.pgh.pa.us 1089 [ # # ]:UBC 0 : elog(ERROR, "invalid index offnum: %u", offnum);
1090 : :
1091 : : /* change offset number to offset index */
10627 bruce@momjian.us 1092 :CBC 629580 : offidx = offnum - 1;
1093 : :
1094 : 629580 : tup = PageGetItemId(page, offnum);
6970 tgl@sss.pgh.pa.us 1095 [ - + ]: 629580 : Assert(ItemIdHasStorage(tup));
10627 bruce@momjian.us 1096 : 629580 : size = ItemIdGetLength(tup);
9036 tgl@sss.pgh.pa.us 1097 : 629580 : offset = ItemIdGetOffset(tup);
1098 : :
1099 [ + - + - ]: 629580 : if (offset < phdr->pd_upper || (offset + size) > phdr->pd_special ||
3685 1100 [ - + ]: 629580 : offset != MAXALIGN(offset))
8481 tgl@sss.pgh.pa.us 1101 [ # # ]:UBC 0 : ereport(ERROR,
1102 : : (errcode(ERRCODE_DATA_CORRUPTED),
1103 : : errmsg("corrupted line pointer: offset = %u, size = %zu",
1104 : : offset, size)));
1105 : :
1106 : : /* Amount of space to actually be deleted */
3685 tgl@sss.pgh.pa.us 1107 :CBC 629580 : size = MAXALIGN(size);
1108 : :
1109 : : /*
1110 : : * First, we want to get rid of the pd_linp entry for the index tuple. We
1111 : : * copy all subsequent linp's back one slot in the array. We don't use
1112 : : * PageGetItemId, because we are manipulating the _array_, not individual
1113 : : * linp's.
1114 : : */
10627 bruce@momjian.us 1115 : 629580 : nbytes = phdr->pd_lower -
1116 : 629580 : ((char *) &phdr->pd_linp[offidx + 1] - (char *) phdr);
1117 : :
8833 tgl@sss.pgh.pa.us 1118 [ + + ]: 629580 : if (nbytes > 0)
607 peter@eisentraut.org 1119 : 613483 : memmove(&(phdr->pd_linp[offidx]),
1120 : 613483 : &(phdr->pd_linp[offidx + 1]),
1121 : : nbytes);
1122 : :
1123 : : /*
1124 : : * Now move everything between the old upper bound (beginning of tuple
1125 : : * space) and the beginning of the deleted tuple forward, so that space in
1126 : : * the middle of the page is left free. If we've just deleted the tuple
1127 : : * at the beginning of tuple space, then there's no need to do the copy.
1128 : : */
1129 : :
1130 : : /* beginning of tuple space */
9036 tgl@sss.pgh.pa.us 1131 : 629580 : addr = (char *) page + phdr->pd_upper;
1132 : :
1133 [ + + ]: 629580 : if (offset > phdr->pd_upper)
3685 1134 : 614118 : memmove(addr + size, addr, offset - phdr->pd_upper);
1135 : :
1136 : : /* adjust free space boundary pointers */
10627 bruce@momjian.us 1137 : 629580 : phdr->pd_upper += size;
1138 : 629580 : phdr->pd_lower -= sizeof(ItemIdData);
1139 : :
1140 : : /*
1141 : : * Finally, we need to adjust the linp entries that remain.
1142 : : *
1143 : : * Anything that used to be before the deleted tuple's data was moved
1144 : : * forward by the size of the deleted tuple.
1145 : : */
1146 [ + + ]: 629580 : if (!PageIsEmpty(page))
1147 : : {
1148 : : int i;
1149 : :
9036 tgl@sss.pgh.pa.us 1150 : 628729 : nline--; /* there's one less than when we started */
8675 bruce@momjian.us 1151 [ + + ]: 96903641 : for (i = 1; i <= nline; i++)
1152 : : {
1554 peter@eisentraut.org 1153 : 96274912 : ItemId ii = PageGetItemId(page, i);
1154 : :
6970 tgl@sss.pgh.pa.us 1155 [ - + ]: 96274912 : Assert(ItemIdHasStorage(ii));
7690 bruce@momjian.us 1156 [ + + ]: 96274912 : if (ItemIdGetOffset(ii) <= offset)
8161 1157 : 62823064 : ii->lp_off += size;
1158 : : }
1159 : : }
11052 scrappy@hub.org 1160 : 629580 : }
1161 : :
1162 : :
1163 : : /*
1164 : : * PageIndexMultiDelete
1165 : : *
1166 : : * This routine handles the case of deleting multiple tuples from an
1167 : : * index page at once. It is considerably faster than a loop around
1168 : : * PageIndexTupleDelete ... however, the caller *must* supply the array
1169 : : * of item numbers to be deleted in item number order!
1170 : : */
1171 : : void
7874 tgl@sss.pgh.pa.us 1172 : 30210 : PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
1173 : : {
1174 : 30210 : PageHeader phdr = (PageHeader) page;
1175 : 30210 : Offset pd_lower = phdr->pd_lower;
1176 : 30210 : Offset pd_upper = phdr->pd_upper;
1177 : 30210 : Offset pd_special = phdr->pd_special;
1178 : : Offset last_offset;
1179 : : itemIdCompactData itemidbase[MaxIndexTuplesPerPage];
1180 : : ItemIdData newitemids[MaxIndexTuplesPerPage];
1181 : : itemIdCompact itemidptr;
1182 : : ItemId lp;
1183 : : int nline,
1184 : : nused;
1185 : : Size totallen;
1186 : : Size size;
1187 : : unsigned offset;
1188 : : int nextitm;
1189 : : OffsetNumber offnum;
2217 drowley@postgresql.o 1190 : 30210 : bool presorted = true; /* For now */
1191 : :
4164 heikki.linnakangas@i 1192 [ - + ]: 30210 : Assert(nitems <= MaxIndexTuplesPerPage);
1193 : :
1194 : : /*
1195 : : * If there aren't very many items to delete, then retail
1196 : : * PageIndexTupleDelete is the best way. Delete the items in reverse
1197 : : * order so we don't have to think about adjusting item numbers for
1198 : : * previous deletions.
1199 : : *
1200 : : * TODO: tune the magic number here
1201 : : */
7874 tgl@sss.pgh.pa.us 1202 [ + + ]: 30210 : if (nitems <= 2)
1203 : : {
1204 [ + + ]: 7182 : while (--nitems >= 0)
1205 : 4091 : PageIndexTupleDelete(page, itemnos[nitems]);
1206 : 3091 : return;
1207 : : }
1208 : :
1209 : : /*
1210 : : * As with PageRepairFragmentation, paranoia seems justified.
1211 : : */
1212 [ + - + - ]: 27119 : if (pd_lower < SizeOfPageHeaderData ||
1213 [ + - ]: 27119 : pd_lower > pd_upper ||
1214 [ + - ]: 27119 : pd_upper > pd_special ||
1215 : 27119 : pd_special > BLCKSZ ||
1216 [ - + ]: 27119 : pd_special != MAXALIGN(pd_special))
7874 tgl@sss.pgh.pa.us 1217 [ # # ]:UBC 0 : ereport(ERROR,
1218 : : (errcode(ERRCODE_DATA_CORRUPTED),
1219 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
1220 : : pd_lower, pd_upper, pd_special)));
1221 : :
1222 : : /*
1223 : : * Scan the line pointer array and build a list of just the ones we are
1224 : : * going to keep. Notice we do not modify the page yet, since we are
1225 : : * still validity-checking.
1226 : : */
7874 tgl@sss.pgh.pa.us 1227 :CBC 27119 : nline = PageGetMaxOffsetNumber(page);
1228 : 27119 : itemidptr = itemidbase;
1229 : 27119 : totallen = 0;
1230 : 27119 : nused = 0;
1231 : 27119 : nextitm = 0;
2217 drowley@postgresql.o 1232 : 27119 : last_offset = pd_special;
6726 bruce@momjian.us 1233 [ + + ]: 5979604 : for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum))
1234 : : {
7874 tgl@sss.pgh.pa.us 1235 : 5952485 : lp = PageGetItemId(page, offnum);
6970 1236 [ - + ]: 5952485 : Assert(ItemIdHasStorage(lp));
7874 1237 : 5952485 : size = ItemIdGetLength(lp);
1238 : 5952485 : offset = ItemIdGetOffset(lp);
1239 [ + - ]: 5952485 : if (offset < pd_upper ||
1240 [ + - ]: 5952485 : (offset + size) > pd_special ||
1241 [ - + ]: 5952485 : offset != MAXALIGN(offset))
7874 tgl@sss.pgh.pa.us 1242 [ # # ]:UBC 0 : ereport(ERROR,
1243 : : (errcode(ERRCODE_DATA_CORRUPTED),
1244 : : errmsg("corrupted line pointer: offset = %u, size = %zu",
1245 : : offset, size)));
1246 : :
7874 tgl@sss.pgh.pa.us 1247 [ + + + + ]:CBC 5952485 : if (nextitm < nitems && offnum == itemnos[nextitm])
1248 : : {
1249 : : /* skip item to be deleted */
1250 : 3006349 : nextitm++;
1251 : : }
1252 : : else
1253 : : {
3400 1254 : 2946136 : itemidptr->offsetindex = nused; /* where it will go */
7874 1255 : 2946136 : itemidptr->itemoff = offset;
1256 : :
2217 drowley@postgresql.o 1257 [ + + ]: 2946136 : if (last_offset > itemidptr->itemoff)
1258 : 1595558 : last_offset = itemidptr->itemoff;
1259 : : else
1260 : 1350578 : presorted = false;
1261 : :
7874 tgl@sss.pgh.pa.us 1262 : 2946136 : itemidptr->alignedlen = MAXALIGN(size);
1263 : 2946136 : totallen += itemidptr->alignedlen;
4269 heikki.linnakangas@i 1264 : 2946136 : newitemids[nused] = *lp;
7874 tgl@sss.pgh.pa.us 1265 : 2946136 : itemidptr++;
1266 : 2946136 : nused++;
1267 : : }
1268 : : }
1269 : :
1270 : : /* this will catch invalid or out-of-order itemnos[] */
1271 [ - + ]: 27119 : if (nextitm != nitems)
7874 tgl@sss.pgh.pa.us 1272 [ # # ]:UBC 0 : elog(ERROR, "incorrect index offsets supplied");
1273 : :
7874 tgl@sss.pgh.pa.us 1274 [ - + ]:CBC 27119 : if (totallen > (Size) (pd_special - pd_lower))
7874 tgl@sss.pgh.pa.us 1275 [ # # ]:UBC 0 : ereport(ERROR,
1276 : : (errcode(ERRCODE_DATA_CORRUPTED),
1277 : : errmsg("corrupted item lengths: total %zu, available space %u",
1278 : : totallen, pd_special - pd_lower)));
1279 : :
1280 : : /*
1281 : : * Looks good. Overwrite the line pointers with the copy, from which we've
1282 : : * removed all the unused items.
1283 : : */
4269 heikki.linnakangas@i 1284 :CBC 27119 : memcpy(phdr->pd_linp, newitemids, nused * sizeof(ItemIdData));
7798 tgl@sss.pgh.pa.us 1285 : 27119 : phdr->pd_lower = SizeOfPageHeaderData + nused * sizeof(ItemIdData);
1286 : :
1287 : : /* and compactify the tuple data */
2217 drowley@postgresql.o 1288 [ + + ]: 27119 : if (nused > 0)
1289 : 26563 : compactify_tuples(itemidbase, nused, page, presorted);
1290 : : else
1291 : 556 : phdr->pd_upper = pd_special;
1292 : : }
1293 : :
1294 : :
1295 : : /*
1296 : : * PageIndexTupleDeleteNoCompact
1297 : : *
1298 : : * Remove the specified tuple from an index page, but set its line pointer
1299 : : * to "unused" instead of compacting it out, except that it can be removed
1300 : : * if it's the last line pointer on the page.
1301 : : *
1302 : : * This is used for index AMs that require that existing TIDs of live tuples
1303 : : * remain unchanged, and are willing to allow unused line pointers instead.
1304 : : */
1305 : : void
3685 tgl@sss.pgh.pa.us 1306 : 348 : PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
1307 : : {
4357 alvherre@alvh.no-ip. 1308 : 348 : PageHeader phdr = (PageHeader) page;
1309 : : char *addr;
1310 : : ItemId tup;
1311 : : Size size;
1312 : : unsigned offset;
1313 : : int nline;
1314 : :
1315 : : /*
1316 : : * As with PageRepairFragmentation, paranoia seems justified.
1317 : : */
3685 tgl@sss.pgh.pa.us 1318 [ + - ]: 348 : if (phdr->pd_lower < SizeOfPageHeaderData ||
1319 [ + - ]: 348 : phdr->pd_lower > phdr->pd_upper ||
1320 [ + - ]: 348 : phdr->pd_upper > phdr->pd_special ||
1321 [ + - ]: 348 : phdr->pd_special > BLCKSZ ||
1322 [ - + ]: 348 : phdr->pd_special != MAXALIGN(phdr->pd_special))
4357 alvherre@alvh.no-ip. 1323 [ # # ]:UBC 0 : ereport(ERROR,
1324 : : (errcode(ERRCODE_DATA_CORRUPTED),
1325 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
1326 : : phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
1327 : :
3685 tgl@sss.pgh.pa.us 1328 :CBC 348 : nline = PageGetMaxOffsetNumber(page);
1329 [ + - - + ]: 348 : if ((int) offnum <= 0 || (int) offnum > nline)
3685 tgl@sss.pgh.pa.us 1330 [ # # ]:UBC 0 : elog(ERROR, "invalid index offnum: %u", offnum);
1331 : :
3685 tgl@sss.pgh.pa.us 1332 :CBC 348 : tup = PageGetItemId(page, offnum);
1333 [ - + ]: 348 : Assert(ItemIdHasStorage(tup));
1334 : 348 : size = ItemIdGetLength(tup);
1335 : 348 : offset = ItemIdGetOffset(tup);
1336 : :
1337 [ + - + - ]: 348 : if (offset < phdr->pd_upper || (offset + size) > phdr->pd_special ||
1338 [ - + ]: 348 : offset != MAXALIGN(offset))
3685 tgl@sss.pgh.pa.us 1339 [ # # ]:UBC 0 : ereport(ERROR,
1340 : : (errcode(ERRCODE_DATA_CORRUPTED),
1341 : : errmsg("corrupted line pointer: offset = %u, size = %zu",
1342 : : offset, size)));
1343 : :
1344 : : /* Amount of space to actually be deleted */
3685 tgl@sss.pgh.pa.us 1345 :CBC 348 : size = MAXALIGN(size);
1346 : :
1347 : : /*
1348 : : * Either set the line pointer to "unused", or zap it if it's the last
1349 : : * one. (Note: it's possible that the next-to-last one(s) are already
1350 : : * unused, but we do not trouble to try to compact them out if so.)
1351 : : */
1352 [ + + ]: 348 : if ((int) offnum < nline)
1353 : 308 : ItemIdSetUnused(tup);
1354 : : else
1355 : : {
1356 : 40 : phdr->pd_lower -= sizeof(ItemIdData);
1357 : 40 : nline--; /* there's one less than when we started */
1358 : : }
1359 : :
1360 : : /*
1361 : : * Now move everything between the old upper bound (beginning of tuple
1362 : : * space) and the beginning of the deleted tuple forward, so that space in
1363 : : * the middle of the page is left free. If we've just deleted the tuple
1364 : : * at the beginning of tuple space, then there's no need to do the copy.
1365 : : */
1366 : :
1367 : : /* beginning of tuple space */
1368 : 348 : addr = (char *) page + phdr->pd_upper;
1369 : :
1370 [ + + ]: 348 : if (offset > phdr->pd_upper)
1371 : 308 : memmove(addr + size, addr, offset - phdr->pd_upper);
1372 : :
1373 : : /* adjust free space boundary pointer */
1374 : 348 : phdr->pd_upper += size;
1375 : :
1376 : : /*
1377 : : * Finally, we need to adjust the linp entries that remain.
1378 : : *
1379 : : * Anything that used to be before the deleted tuple's data was moved
1380 : : * forward by the size of the deleted tuple.
1381 : : */
1382 [ + + ]: 348 : if (!PageIsEmpty(page))
1383 : : {
1384 : : int i;
1385 : :
1386 [ + + ]: 86594 : for (i = 1; i <= nline; i++)
1387 : : {
1554 peter@eisentraut.org 1388 : 86252 : ItemId ii = PageGetItemId(page, i);
1389 : :
3685 tgl@sss.pgh.pa.us 1390 [ + + + + ]: 86252 : if (ItemIdHasStorage(ii) && ItemIdGetOffset(ii) <= offset)
1391 : 42305 : ii->lp_off += size;
1392 : : }
1393 : : }
4357 alvherre@alvh.no-ip. 1394 : 348 : }
1395 : :
1396 : :
1397 : : /*
1398 : : * PageIndexTupleOverwrite
1399 : : *
1400 : : * Replace a specified tuple on an index page.
1401 : : *
1402 : : * The new tuple is placed exactly where the old one had been, shifting
1403 : : * other tuples' data up or down as needed to keep the page compacted.
1404 : : * This is better than deleting and reinserting the tuple, because it
1405 : : * avoids any data shifting when the tuple size doesn't change; and
1406 : : * even when it does, we avoid moving the line pointers around.
1407 : : * This could be used by an index AM that doesn't want to unset the
1408 : : * LP_DEAD bit when it happens to be set. It could conceivably also be
1409 : : * used by an index AM that cares about the physical order of tuples as
1410 : : * well as their logical/ItemId order.
1411 : : *
1412 : : * If there's insufficient space for the new tuple, return false. Other
1413 : : * errors represent data-corruption problems, so we just elog.
1414 : : */
1415 : : bool
3685 tgl@sss.pgh.pa.us 1416 : 602707 : PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
1417 : : const void *newtup, Size newsize)
1418 : : {
1419 : 602707 : PageHeader phdr = (PageHeader) page;
1420 : : ItemId tupid;
1421 : : int oldsize;
1422 : : unsigned offset;
1423 : : Size alignednewsize;
1424 : : int size_diff;
1425 : : int itemcount;
1426 : :
1427 : : /*
1428 : : * As with PageRepairFragmentation, paranoia seems justified.
1429 : : */
1430 [ + - ]: 602707 : if (phdr->pd_lower < SizeOfPageHeaderData ||
1431 [ + - ]: 602707 : phdr->pd_lower > phdr->pd_upper ||
1432 [ + - ]: 602707 : phdr->pd_upper > phdr->pd_special ||
1433 [ + - ]: 602707 : phdr->pd_special > BLCKSZ ||
1434 [ - + ]: 602707 : phdr->pd_special != MAXALIGN(phdr->pd_special))
3685 tgl@sss.pgh.pa.us 1435 [ # # ]:UBC 0 : ereport(ERROR,
1436 : : (errcode(ERRCODE_DATA_CORRUPTED),
1437 : : errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
1438 : : phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
1439 : :
3685 tgl@sss.pgh.pa.us 1440 :CBC 602707 : itemcount = PageGetMaxOffsetNumber(page);
1441 [ + - - + ]: 602707 : if ((int) offnum <= 0 || (int) offnum > itemcount)
3685 tgl@sss.pgh.pa.us 1442 [ # # ]:UBC 0 : elog(ERROR, "invalid index offnum: %u", offnum);
1443 : :
3685 tgl@sss.pgh.pa.us 1444 :CBC 602707 : tupid = PageGetItemId(page, offnum);
1445 [ - + ]: 602707 : Assert(ItemIdHasStorage(tupid));
1446 : 602707 : oldsize = ItemIdGetLength(tupid);
1447 : 602707 : offset = ItemIdGetOffset(tupid);
1448 : :
1449 [ + - + - ]: 602707 : if (offset < phdr->pd_upper || (offset + oldsize) > phdr->pd_special ||
1450 [ - + ]: 602707 : offset != MAXALIGN(offset))
3685 tgl@sss.pgh.pa.us 1451 [ # # ]:UBC 0 : ereport(ERROR,
1452 : : (errcode(ERRCODE_DATA_CORRUPTED),
1453 : : errmsg("corrupted line pointer: offset = %u, size = %d",
1454 : : offset, oldsize)));
1455 : :
1456 : : /*
1457 : : * Determine actual change in space requirement, check for page overflow.
1458 : : */
3685 tgl@sss.pgh.pa.us 1459 :CBC 602707 : oldsize = MAXALIGN(oldsize);
1460 : 602707 : alignednewsize = MAXALIGN(newsize);
1461 [ - + ]: 602707 : if (alignednewsize > oldsize + (phdr->pd_upper - phdr->pd_lower))
3685 tgl@sss.pgh.pa.us 1462 :UBC 0 : return false;
1463 : :
1464 : : /*
1465 : : * Relocate existing data and update line pointers, unless the new tuple
1466 : : * is the same size as the old (after alignment), in which case there's
1467 : : * nothing to do. Notice that what we have to relocate is data before the
1468 : : * target tuple, not data after, so it's convenient to express size_diff
1469 : : * as the amount by which the tuple's size is decreasing, making it the
1470 : : * delta to add to pd_upper and affected line pointers.
1471 : : */
3685 tgl@sss.pgh.pa.us 1472 :CBC 602707 : size_diff = oldsize - (int) alignednewsize;
1473 [ + + ]: 602707 : if (size_diff != 0)
1474 : : {
1475 : 107450 : char *addr = (char *) page + phdr->pd_upper;
1476 : : int i;
1477 : :
1478 : : /* relocate all tuple data before the target tuple */
1479 : 107450 : memmove(addr + size_diff, addr, offset - phdr->pd_upper);
1480 : :
1481 : : /* adjust free space boundary pointer */
1482 : 107450 : phdr->pd_upper += size_diff;
1483 : :
1484 : : /* adjust affected line pointers too */
1485 [ + + ]: 19971696 : for (i = FirstOffsetNumber; i <= itemcount; i++)
1486 : : {
1554 peter@eisentraut.org 1487 : 19864246 : ItemId ii = PageGetItemId(page, i);
1488 : :
1489 : : /* Allow items without storage; currently only BRIN needs that */
3685 tgl@sss.pgh.pa.us 1490 [ + + + + ]: 19864246 : if (ItemIdHasStorage(ii) && ItemIdGetOffset(ii) <= offset)
1491 : 10529391 : ii->lp_off += size_diff;
1492 : : }
1493 : : }
1494 : :
1495 : : /* Update the item's tuple length without changing its lp_flags field */
2420 pg@bowt.ie 1496 : 602707 : tupid->lp_off = offset + size_diff;
1497 : 602707 : tupid->lp_len = newsize;
1498 : :
1499 : : /* Copy new tuple data onto page */
3685 tgl@sss.pgh.pa.us 1500 : 602707 : memcpy(PageGetItem(page, tupid), newtup, newsize);
1501 : :
1502 : 602707 : return true;
1503 : : }
1504 : :
1505 : :
1506 : : /*
1507 : : * Set checksum on a page.
1508 : : *
1509 : : * If the page is in shared buffers, it needs to be locked in at least
1510 : : * share-exclusive mode.
1511 : : *
1512 : : * If checksums are disabled, or if the page is not initialized, just
1513 : : * return. Otherwise compute and set the checksum.
1514 : : *
1515 : : * In the past this needed to be done on a copy of the page, due to the
1516 : : * possibility of e.g., hint bits being set concurrently. However, this is not
1517 : : * necessary anymore as hint bits won't be set while IO is going on.
1518 : : */
1519 : : void
199 andres@anarazel.de 1520 : 791240 : PageSetChecksum(Page page, BlockNumber blkno)
1521 : : {
192 dgustafsson@postgres 1522 :GNC 791240 : HOLD_INTERRUPTS();
1523 : : /* If we don't need a checksum, just return */
1524 [ + + + + ]: 791240 : if (PageIsNew(page) || !DataChecksumsNeedWrite())
1525 : : {
1526 [ - + ]: 51095 : RESUME_INTERRUPTS();
4952 simon@2ndQuadrant.co 1527 :CBC 51095 : return;
1528 : : }
1529 : :
607 peter@eisentraut.org 1530 : 740145 : ((PageHeader) page)->pd_checksum = pg_checksum_page(page, blkno);
192 dgustafsson@postgres 1531 [ - + ]:GNC 740145 : RESUME_INTERRUPTS();
1532 : : }
|