PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
brin_pageops.c
Go to the documentation of this file.
1 /*
2  * brin_pageops.c
3  * Page-handling routines for BRIN indexes
4  *
5  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  * IDENTIFICATION
9  * src/backend/access/brin/brin_pageops.c
10  */
11 #include "postgres.h"
12 
13 #include "access/brin_pageops.h"
14 #include "access/brin_page.h"
15 #include "access/brin_revmap.h"
16 #include "access/brin_xlog.h"
17 #include "access/xloginsert.h"
18 #include "miscadmin.h"
19 #include "storage/bufmgr.h"
20 #include "storage/freespace.h"
21 #include "storage/lmgr.h"
22 #include "storage/smgr.h"
23 #include "utils/rel.h"
24 
25 
26 /*
27  * Maximum size of an entry in a BRIN_PAGETYPE_REGULAR page. We can tolerate
28  * a single item per page, unlike other index AMs.
29  */
30 #define BrinMaxItemSize \
31  MAXALIGN_DOWN(BLCKSZ - \
32  (MAXALIGN(SizeOfPageHeaderData + \
33  sizeof(ItemIdData)) + \
34  MAXALIGN(sizeof(BrinSpecialSpace))))
35 
36 static Buffer brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
37  bool *extended);
38 static Size br_page_get_freespace(Page page);
39 static void brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer);
40 
41 
42 /*
43  * Update tuple origtup (size origsz), located in offset oldoff of buffer
44  * oldbuf, to newtup (size newsz) as summary tuple for the page range starting
45  * at heapBlk. oldbuf must not be locked on entry, and is not locked at exit.
46  *
47  * If samepage is true, attempt to put the new tuple in the same page, but if
48  * there's no room, use some other one.
49  *
50  * If the update is successful, return true; the revmap is updated to point to
51  * the new tuple. If the update is not done for whatever reason, return false.
52  * Caller may retry the update if this happens.
53  */
54 bool
55 brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
56  BrinRevmap *revmap, BlockNumber heapBlk,
57  Buffer oldbuf, OffsetNumber oldoff,
58  const BrinTuple *origtup, Size origsz,
59  const BrinTuple *newtup, Size newsz,
60  bool samepage)
61 {
62  Page oldpage;
63  ItemId oldlp;
64  BrinTuple *oldtup;
65  Size oldsz;
66  Buffer newbuf;
67  bool extended;
68 
69  Assert(newsz == MAXALIGN(newsz));
70 
71  /* If the item is oversized, don't bother. */
72  if (newsz > BrinMaxItemSize)
73  {
74  ereport(ERROR,
75  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
76  errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
77  (unsigned long) newsz,
78  (unsigned long) BrinMaxItemSize,
79  RelationGetRelationName(idxrel))));
80  return false; /* keep compiler quiet */
81  }
82 
83  /* make sure the revmap is long enough to contain the entry we need */
84  brinRevmapExtend(revmap, heapBlk);
85 
86  if (!samepage)
87  {
88  /* need a page on which to put the item */
89  newbuf = brin_getinsertbuffer(idxrel, oldbuf, newsz, &extended);
90  if (!BufferIsValid(newbuf))
91  {
92  Assert(!extended);
93  return false;
94  }
95 
96  /*
97  * Note: it's possible (though unlikely) that the returned newbuf is
98  * the same as oldbuf, if brin_getinsertbuffer determined that the old
99  * buffer does in fact have enough space.
100  */
101  if (newbuf == oldbuf)
102  {
103  Assert(!extended);
104  newbuf = InvalidBuffer;
105  }
106  }
107  else
108  {
110  newbuf = InvalidBuffer;
111  extended = false;
112  }
113  oldpage = BufferGetPage(oldbuf);
114  oldlp = PageGetItemId(oldpage, oldoff);
115 
116  /*
117  * Check that the old tuple wasn't updated concurrently: it might have
118  * moved someplace else entirely ...
119  */
120  if (!ItemIdIsNormal(oldlp))
121  {
123 
124  /*
125  * If this happens, and the new buffer was obtained by extending the
126  * relation, then we need to ensure we don't leave it uninitialized or
127  * forget about it.
128  */
129  if (BufferIsValid(newbuf))
130  {
131  if (extended)
132  brin_initialize_empty_new_buffer(idxrel, newbuf);
133  UnlockReleaseBuffer(newbuf);
134  if (extended)
135  FreeSpaceMapVacuum(idxrel);
136  }
137  return false;
138  }
139 
140  oldsz = ItemIdGetLength(oldlp);
141  oldtup = (BrinTuple *) PageGetItem(oldpage, oldlp);
142 
143  /*
144  * ... or it might have been updated in place to different contents.
145  */
146  if (!brin_tuples_equal(oldtup, oldsz, origtup, origsz))
147  {
149  if (BufferIsValid(newbuf))
150  {
151  if (extended)
152  brin_initialize_empty_new_buffer(idxrel, newbuf);
153  UnlockReleaseBuffer(newbuf);
154  if (extended)
155  FreeSpaceMapVacuum(idxrel);
156  }
157  return false;
158  }
159 
160  /*
161  * Great, the old tuple is intact. We can proceed with the update.
162  *
163  * If there's enough room in the old page for the new tuple, replace it.
164  *
165  * Note that there might now be enough space on the page even though the
166  * caller told us there isn't, if a concurrent update moved another tuple
167  * elsewhere or replaced a tuple with a smaller one.
168  */
169  if (((BrinPageFlags(oldpage) & BRIN_EVACUATE_PAGE) == 0) &&
170  brin_can_do_samepage_update(oldbuf, origsz, newsz))
171  {
172  if (BufferIsValid(newbuf))
173  {
174  /* as above */
175  if (extended)
176  brin_initialize_empty_new_buffer(idxrel, newbuf);
177  UnlockReleaseBuffer(newbuf);
178  }
179 
181  if (!PageIndexTupleOverwrite(oldpage, oldoff, (Item) newtup, newsz))
182  elog(ERROR, "failed to replace BRIN tuple");
183  MarkBufferDirty(oldbuf);
184 
185  /* XLOG stuff */
186  if (RelationNeedsWAL(idxrel))
187  {
189  XLogRecPtr recptr;
191 
192  xlrec.offnum = oldoff;
193 
194  XLogBeginInsert();
195  XLogRegisterData((char *) &xlrec, SizeOfBrinSamepageUpdate);
196 
198  XLogRegisterBufData(0, (char *) newtup, newsz);
199 
200  recptr = XLogInsert(RM_BRIN_ID, info);
201 
202  PageSetLSN(oldpage, recptr);
203  }
204 
206 
208 
209  if (extended)
210  FreeSpaceMapVacuum(idxrel);
211 
212  return true;
213  }
214  else if (newbuf == InvalidBuffer)
215  {
216  /*
217  * Not enough space, but caller said that there was. Tell them to
218  * start over.
219  */
221  return false;
222  }
223  else
224  {
225  /*
226  * Not enough free space on the oldpage. Put the new tuple on the new
227  * page, and update the revmap.
228  */
229  Page newpage = BufferGetPage(newbuf);
230  Buffer revmapbuf;
231  ItemPointerData newtid;
232  OffsetNumber newoff;
234  Size freespace = 0;
235 
236  revmapbuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
237 
239 
240  /*
241  * We need to initialize the page if it's newly obtained. Note we
242  * will WAL-log the initialization as part of the update, so we don't
243  * need to do that here.
244  */
245  if (extended)
247 
248  PageIndexTupleDeleteNoCompact(oldpage, oldoff);
249  newoff = PageAddItem(newpage, (Item) newtup, newsz,
250  InvalidOffsetNumber, false, false);
251  if (newoff == InvalidOffsetNumber)
252  elog(ERROR, "failed to add BRIN tuple to new page");
253  MarkBufferDirty(oldbuf);
254  MarkBufferDirty(newbuf);
255 
256  /* needed to update FSM below */
257  if (extended)
258  {
259  newblk = BufferGetBlockNumber(newbuf);
260  freespace = br_page_get_freespace(newpage);
261  }
262 
263  ItemPointerSet(&newtid, BufferGetBlockNumber(newbuf), newoff);
264  brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, newtid);
265  MarkBufferDirty(revmapbuf);
266 
267  /* XLOG stuff */
268  if (RelationNeedsWAL(idxrel))
269  {
270  xl_brin_update xlrec;
271  XLogRecPtr recptr;
272  uint8 info;
273 
274  info = XLOG_BRIN_UPDATE | (extended ? XLOG_BRIN_INIT_PAGE : 0);
275 
276  xlrec.insert.offnum = newoff;
277  xlrec.insert.heapBlk = heapBlk;
278  xlrec.insert.pagesPerRange = pagesPerRange;
279  xlrec.oldOffnum = oldoff;
280 
281  XLogBeginInsert();
282 
283  /* new page */
284  XLogRegisterData((char *) &xlrec, SizeOfBrinUpdate);
285 
286  XLogRegisterBuffer(0, newbuf, REGBUF_STANDARD | (extended ? REGBUF_WILL_INIT : 0));
287  XLogRegisterBufData(0, (char *) newtup, newsz);
288 
289  /* revmap page */
290  XLogRegisterBuffer(1, revmapbuf, 0);
291 
292  /* old page */
294 
295  recptr = XLogInsert(RM_BRIN_ID, info);
296 
297  PageSetLSN(oldpage, recptr);
298  PageSetLSN(newpage, recptr);
299  PageSetLSN(BufferGetPage(revmapbuf), recptr);
300  }
301 
303 
304  LockBuffer(revmapbuf, BUFFER_LOCK_UNLOCK);
306  UnlockReleaseBuffer(newbuf);
307 
308  if (extended)
309  {
310  Assert(BlockNumberIsValid(newblk));
311  RecordPageWithFreeSpace(idxrel, newblk, freespace);
312  FreeSpaceMapVacuum(idxrel);
313  }
314 
315  return true;
316  }
317 }
318 
319 /*
320  * Return whether brin_doupdate can do a samepage update.
321  */
322 bool
324 {
325  return
326  ((newsz <= origsz) ||
327  PageGetExactFreeSpace(BufferGetPage(buffer)) >= (newsz - origsz));
328 }
329 
330 /*
331  * Insert an index tuple into the index relation. The revmap is updated to
332  * mark the range containing the given page as pointing to the inserted entry.
333  * A WAL record is written.
334  *
335  * The buffer, if valid, is first checked for free space to insert the new
336  * entry; if there isn't enough, a new buffer is obtained and pinned. No
337  * buffer lock must be held on entry, no buffer lock is held on exit.
338  *
339  * Return value is the offset number where the tuple was inserted.
340  */
342 brin_doinsert(Relation idxrel, BlockNumber pagesPerRange,
343  BrinRevmap *revmap, Buffer *buffer, BlockNumber heapBlk,
344  BrinTuple *tup, Size itemsz)
345 {
346  Page page;
347  BlockNumber blk;
348  OffsetNumber off;
349  Buffer revmapbuf;
350  ItemPointerData tid;
351  bool extended;
352 
353  Assert(itemsz == MAXALIGN(itemsz));
354 
355  /* If the item is oversized, don't even bother. */
356  if (itemsz > BrinMaxItemSize)
357  {
358  ereport(ERROR,
359  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
360  errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
361  (unsigned long) itemsz,
362  (unsigned long) BrinMaxItemSize,
363  RelationGetRelationName(idxrel))));
364  return InvalidOffsetNumber; /* keep compiler quiet */
365  }
366 
367  /* Make sure the revmap is long enough to contain the entry we need */
368  brinRevmapExtend(revmap, heapBlk);
369 
370  /*
371  * Acquire lock on buffer supplied by caller, if any. If it doesn't have
372  * enough space, unpin it to obtain a new one below.
373  */
374  if (BufferIsValid(*buffer))
375  {
376  /*
377  * It's possible that another backend (or ourselves!) extended the
378  * revmap over the page we held a pin on, so we cannot assume that
379  * it's still a regular page.
380  */
382  if (br_page_get_freespace(BufferGetPage(*buffer)) < itemsz)
383  {
384  UnlockReleaseBuffer(*buffer);
385  *buffer = InvalidBuffer;
386  }
387  }
388 
389  /*
390  * If we still don't have a usable buffer, have brin_getinsertbuffer
391  * obtain one for us.
392  */
393  if (!BufferIsValid(*buffer))
394  {
395  do
396  *buffer = brin_getinsertbuffer(idxrel, InvalidBuffer, itemsz, &extended);
397  while (!BufferIsValid(*buffer));
398  }
399  else
400  extended = false;
401 
402  /* Now obtain lock on revmap buffer */
403  revmapbuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
404 
405  page = BufferGetPage(*buffer);
406  blk = BufferGetBlockNumber(*buffer);
407 
408  /* Execute the actual insertion */
410  if (extended)
412  off = PageAddItem(page, (Item) tup, itemsz, InvalidOffsetNumber,
413  false, false);
414  if (off == InvalidOffsetNumber)
415  elog(ERROR, "could not insert new index tuple to page");
416  MarkBufferDirty(*buffer);
417 
418  BRIN_elog((DEBUG2, "inserted tuple (%u,%u) for range starting at %u",
419  blk, off, heapBlk));
420 
421  ItemPointerSet(&tid, blk, off);
422  brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, tid);
423  MarkBufferDirty(revmapbuf);
424 
425  /* XLOG stuff */
426  if (RelationNeedsWAL(idxrel))
427  {
428  xl_brin_insert xlrec;
429  XLogRecPtr recptr;
430  uint8 info;
431 
432  info = XLOG_BRIN_INSERT | (extended ? XLOG_BRIN_INIT_PAGE : 0);
433  xlrec.heapBlk = heapBlk;
434  xlrec.pagesPerRange = pagesPerRange;
435  xlrec.offnum = off;
436 
437  XLogBeginInsert();
438  XLogRegisterData((char *) &xlrec, SizeOfBrinInsert);
439 
440  XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD | (extended ? REGBUF_WILL_INIT : 0));
441  XLogRegisterBufData(0, (char *) tup, itemsz);
442 
443  XLogRegisterBuffer(1, revmapbuf, 0);
444 
445  recptr = XLogInsert(RM_BRIN_ID, info);
446 
447  PageSetLSN(page, recptr);
448  PageSetLSN(BufferGetPage(revmapbuf), recptr);
449  }
450 
452 
453  /* Tuple is firmly on buffer; we can release our locks */
454  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
455  LockBuffer(revmapbuf, BUFFER_LOCK_UNLOCK);
456 
457  if (extended)
458  FreeSpaceMapVacuum(idxrel);
459 
460  return off;
461 }
462 
463 /*
464  * Initialize a page with the given type.
465  *
466  * Caller is responsible for marking it dirty, as appropriate.
467  */
468 void
470 {
471  PageInit(page, BLCKSZ, sizeof(BrinSpecialSpace));
472 
473  BrinPageType(page) = type;
474 }
475 
476 /*
477  * Initialize a new BRIN index' metapage.
478  */
479 void
480 brin_metapage_init(Page page, BlockNumber pagesPerRange, uint16 version)
481 {
482  BrinMetaPageData *metadata;
483 
485 
486  metadata = (BrinMetaPageData *) PageGetContents(page);
487 
488  metadata->brinMagic = BRIN_META_MAGIC;
489  metadata->brinVersion = version;
490  metadata->pagesPerRange = pagesPerRange;
491 
492  /*
493  * Note we cheat here a little. 0 is not a valid revmap block number
494  * (because it's the metapage buffer), but doing this enables the first
495  * revmap page to be created when the index is.
496  */
497  metadata->lastRevmapPage = 0;
498 }
499 
500 /*
501  * Initiate page evacuation protocol.
502  *
503  * The page must be locked in exclusive mode by the caller.
504  *
505  * If the page is not yet initialized or empty, return false without doing
506  * anything; it can be used for revmap without any further changes. If it
507  * contains tuples, mark it for evacuation and return true.
508  */
509 bool
511 {
512  OffsetNumber off;
513  OffsetNumber maxoff;
514  Page page;
515 
516  page = BufferGetPage(buf);
517 
518  if (PageIsNew(page))
519  return false;
520 
521  maxoff = PageGetMaxOffsetNumber(page);
522  for (off = FirstOffsetNumber; off <= maxoff; off++)
523  {
524  ItemId lp;
525 
526  lp = PageGetItemId(page, off);
527  if (ItemIdIsUsed(lp))
528  {
529  /* prevent other backends from adding more stuff to this page */
531  MarkBufferDirtyHint(buf, true);
532 
533  return true;
534  }
535  }
536  return false;
537 }
538 
539 /*
540  * Move all tuples out of a page.
541  *
542  * The caller must hold lock on the page. The lock and pin are released.
543  */
544 void
546  BrinRevmap *revmap, Buffer buf)
547 {
548  OffsetNumber off;
549  OffsetNumber maxoff;
550  Page page;
551 
552  page = BufferGetPage(buf);
553 
555 
556  maxoff = PageGetMaxOffsetNumber(page);
557  for (off = FirstOffsetNumber; off <= maxoff; off++)
558  {
559  BrinTuple *tup;
560  Size sz;
561  ItemId lp;
562 
564 
565  lp = PageGetItemId(page, off);
566  if (ItemIdIsUsed(lp))
567  {
568  sz = ItemIdGetLength(lp);
569  tup = (BrinTuple *) PageGetItem(page, lp);
570  tup = brin_copy_tuple(tup, sz);
571 
573 
574  if (!brin_doupdate(idxRel, pagesPerRange, revmap, tup->bt_blkno,
575  buf, off, tup, sz, tup, sz, false))
576  off--; /* retry */
577 
579 
580  /* It's possible that someone extended the revmap over this page */
581  if (!BRIN_IS_REGULAR_PAGE(page))
582  break;
583  }
584  }
585 
586  UnlockReleaseBuffer(buf);
587 }
588 
589 /*
590  * Given a BRIN index page, initialize it if necessary, and record it into the
591  * FSM if necessary. Return value is true if the FSM itself needs "vacuuming".
592  * The main use for this is when, during vacuuming, an uninitialized page is
593  * found, which could be the result of relation extension followed by a crash
594  * before the page can be used.
595  */
596 bool
598 {
599  Page page = BufferGetPage(buf);
600  Size freespace;
601 
602  /*
603  * If a page was left uninitialized, initialize it now; also record it in
604  * FSM.
605  *
606  * Somebody else might be extending the relation concurrently. To avoid
607  * re-initializing the page before they can grab the buffer lock, we
608  * acquire the extension lock momentarily. Since they hold the extension
609  * lock from before getting the page and after its been initialized, we're
610  * sure to see their initialization.
611  */
612  if (PageIsNew(page))
613  {
616 
618  if (PageIsNew(page))
619  {
622  return true;
623  }
625  }
626 
627  /* Nothing to be done for non-regular index pages */
628  if (BRIN_IS_META_PAGE(BufferGetPage(buf)) ||
630  return false;
631 
632  /* Measure free space and record it */
633  freespace = br_page_get_freespace(page);
634  if (freespace > GetRecordedFreeSpace(idxrel, BufferGetBlockNumber(buf)))
635  {
636  RecordPageWithFreeSpace(idxrel, BufferGetBlockNumber(buf), freespace);
637  return true;
638  }
639 
640  return false;
641 }
642 
643 /*
644  * Return a pinned and exclusively locked buffer which can be used to insert an
645  * index item of size itemsz (caller must ensure not to request sizes
646  * impossible to fulfill). If oldbuf is a valid buffer, it is also locked (in
647  * an order determined to avoid deadlocks.)
648  *
649  * If we find that the old page is no longer a regular index page (because
650  * of a revmap extension), the old buffer is unlocked and we return
651  * InvalidBuffer.
652  *
653  * If there's no existing page with enough free space to accommodate the new
654  * item, the relation is extended. If this happens, *extended is set to true,
655  * and it is the caller's responsibility to initialize the page (and WAL-log
656  * that fact) prior to use.
657  *
658  * Note that in some corner cases it is possible for this routine to extend the
659  * relation and then not return the buffer. It is this routine's
660  * responsibility to WAL-log the page initialization and to record the page in
661  * FSM if that happens. Such a buffer may later be reused by this routine.
662  */
663 static Buffer
665  bool *extended)
666 {
667  BlockNumber oldblk;
668  BlockNumber newblk;
669  Page page;
670  int freespace;
671 
672  /* callers must have checked */
673  Assert(itemsz <= BrinMaxItemSize);
674 
675  *extended = false;
676 
677  if (BufferIsValid(oldbuf))
678  oldblk = BufferGetBlockNumber(oldbuf);
679  else
680  oldblk = InvalidBlockNumber;
681 
682  /*
683  * Loop until we find a page with sufficient free space. By the time we
684  * return to caller out of this loop, both buffers are valid and locked;
685  * if we have to restart here, neither buffer is locked and buf is not a
686  * pinned buffer.
687  */
688  newblk = RelationGetTargetBlock(irel);
689  if (newblk == InvalidBlockNumber)
690  newblk = GetPageWithFreeSpace(irel, itemsz);
691  for (;;)
692  {
693  Buffer buf;
694  bool extensionLockHeld = false;
695 
697 
698  if (newblk == InvalidBlockNumber)
699  {
700  /*
701  * There's not enough free space in any existing index page,
702  * according to the FSM: extend the relation to obtain a shiny new
703  * page.
704  */
705  if (!RELATION_IS_LOCAL(irel))
706  {
708  extensionLockHeld = true;
709  }
710  buf = ReadBuffer(irel, P_NEW);
711  newblk = BufferGetBlockNumber(buf);
712  *extended = true;
713 
714  BRIN_elog((DEBUG2, "brin_getinsertbuffer: extending to page %u",
715  BufferGetBlockNumber(buf)));
716  }
717  else if (newblk == oldblk)
718  {
719  /*
720  * There's an odd corner-case here where the FSM is out-of-date,
721  * and gave us the old page.
722  */
723  buf = oldbuf;
724  }
725  else
726  {
727  buf = ReadBuffer(irel, newblk);
728  }
729 
730  /*
731  * We lock the old buffer first, if it's earlier than the new one; but
732  * before we do, we need to check that it hasn't been turned into a
733  * revmap page concurrently; if we detect that it happened, give up
734  * and tell caller to start over.
735  */
736  if (BufferIsValid(oldbuf) && oldblk < newblk)
737  {
739  if (!BRIN_IS_REGULAR_PAGE(BufferGetPage(oldbuf)))
740  {
742 
743  /*
744  * It is possible that the new page was obtained from
745  * extending the relation. In that case, we must be sure to
746  * record it in the FSM before leaving, because otherwise the
747  * space would be lost forever. However, we cannot let an
748  * uninitialized page get in the FSM, so we need to initialize
749  * it first.
750  */
751  if (*extended)
752  {
754  /* shouldn't matter, but don't confuse caller */
755  *extended = false;
756  }
757 
758  if (extensionLockHeld)
760 
761  ReleaseBuffer(buf);
762  return InvalidBuffer;
763  }
764  }
765 
767 
768  if (extensionLockHeld)
770 
771  page = BufferGetPage(buf);
772 
773  /*
774  * We have a new buffer to insert into. Check that the new page has
775  * enough free space, and return it if it does; otherwise start over.
776  * Note that we allow for the FSM to be out of date here, and in that
777  * case we update it and move on.
778  *
779  * (br_page_get_freespace also checks that the FSM didn't hand us a
780  * page that has since been repurposed for the revmap.)
781  */
782  freespace = *extended ?
784  if (freespace >= itemsz)
785  {
787 
788  /*
789  * Since the target block specification can get lost on cache
790  * invalidations, make sure we update the more permanent FSM with
791  * data about it before going away.
792  */
793  if (*extended)
795  freespace);
796 
797  /*
798  * Lock the old buffer if not locked already. Note that in this
799  * case we know for sure it's a regular page: it's later than the
800  * new page we just got, which is not a revmap page, and revmap
801  * pages are always consecutive.
802  */
803  if (BufferIsValid(oldbuf) && oldblk > newblk)
804  {
807  }
808 
809  return buf;
810  }
811 
812  /* This page is no good. */
813 
814  /*
815  * If an entirely new page does not contain enough free space for the
816  * new item, then surely that item is oversized. Complain loudly; but
817  * first make sure we initialize the page and record it as free, for
818  * next time.
819  */
820  if (*extended)
821  {
823 
824  ereport(ERROR,
825  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
826  errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
827  (unsigned long) itemsz,
828  (unsigned long) freespace,
829  RelationGetRelationName(irel))));
830  return InvalidBuffer; /* keep compiler quiet */
831  }
832 
833  if (newblk != oldblk)
834  UnlockReleaseBuffer(buf);
835  if (BufferIsValid(oldbuf) && oldblk <= newblk)
837 
838  newblk = RecordAndGetPageWithFreeSpace(irel, newblk, freespace, itemsz);
839  }
840 }
841 
842 /*
843  * Initialize a page as an empty regular BRIN page, WAL-log this, and record
844  * the page in FSM.
845  *
846  * There are several corner situations in which we extend the relation to
847  * obtain a new page and later find that we cannot use it immediately. When
848  * that happens, we don't want to leave the page go unrecorded in FSM, because
849  * there is no mechanism to get the space back and the index would bloat.
850  * Also, because we would not WAL-log the action that would initialize the
851  * page, the page would go uninitialized in a standby (or after recovery).
852  */
853 static void
855 {
856  Page page;
857 
858  BRIN_elog((DEBUG2,
859  "brin_initialize_empty_new_buffer: initializing blank page %u",
860  BufferGetBlockNumber(buffer)));
861 
863  page = BufferGetPage(buffer);
865  MarkBufferDirty(buffer);
866  log_newpage_buffer(buffer, true);
868 
869  /*
870  * We update the FSM for this page, but this is not WAL-logged. This is
871  * acceptable because VACUUM will scan the index and update the FSM with
872  * pages whose FSM records were forgotten in a crash.
873  */
875  br_page_get_freespace(page));
876 }
877 
878 
879 /*
880  * Return the amount of free space on a regular BRIN index page.
881  *
882  * If the page is not a regular page, or has been marked with the
883  * BRIN_EVACUATE_PAGE flag, returns 0.
884  */
885 static Size
887 {
888  if (!BRIN_IS_REGULAR_PAGE(page) ||
889  (BrinPageFlags(page) & BRIN_EVACUATE_PAGE) != 0)
890  return 0;
891  else
892  return PageGetFreeSpace(page);
893 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
uint32 brinVersion
Definition: brin_page.h:67
BlockNumber heapBlk
Definition: brin_xlog.h:65
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
#define BRIN_elog(args)
Definition: brin_internal.h:81
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1010
void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail)
Definition: freespace.c:181
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3362
#define SizeOfBrinInsert
Definition: brin_xlog.h:74
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1445
#define ExclusiveLock
Definition: lockdefs.h:44
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:520
#define END_CRIT_SECTION()
Definition: miscadmin.h:132
#define ItemIdIsUsed(itemId)
Definition: itemid.h:91
OffsetNumber offnum
Definition: brin_xlog.h:104
unsigned char uint8
Definition: c.h:263
Pointer Item
Definition: item.h:17
#define InvalidBuffer
Definition: buf.h:25
Buffer brinLockRevmapPageForUpdate(BrinRevmap *revmap, BlockNumber heapBlk)
Definition: brin_revmap.c:137
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:130
int errcode(int sqlerrcode)
Definition: elog.c:575
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:413
#define BRIN_IS_REGULAR_PAGE(page)
Definition: brin_page.h:57
uint32 brinMagic
Definition: brin_page.h:66
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3292
#define P_NEW
Definition: bufmgr.h:82
BrinTuple * brin_copy_tuple(BrinTuple *tuple, Size len)
Definition: brin_tuple.c:317
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:89
bool brin_doupdate(Relation idxrel, BlockNumber pagesPerRange, BrinRevmap *revmap, BlockNumber heapBlk, Buffer oldbuf, OffsetNumber oldoff, const BrinTuple *origtup, Size origsz, const BrinTuple *newtup, Size newsz, bool samepage)
Definition: brin_pageops.c:55
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
#define RelationGetTargetBlock(relation)
Definition: rel.h:485
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:582
uint16 OffsetNumber
Definition: off.h:24
OffsetNumber brin_doinsert(Relation idxrel, BlockNumber pagesPerRange, BrinRevmap *revmap, Buffer *buffer, BlockNumber heapBlk, BrinTuple *tup, Size itemsz)
Definition: brin_pageops.c:342
#define SizeOfBrinUpdate
Definition: brin_xlog.h:95
#define XLOG_BRIN_UPDATE
Definition: brin_xlog.h:33
unsigned short uint16
Definition: c.h:264
#define ItemIdGetLength(itemId)
Definition: itemid.h:58
void brin_metapage_init(Page page, BlockNumber pagesPerRange, uint16 version)
Definition: brin_pageops.c:480
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3315
#define ERROR
Definition: elog.h:43
#define BRIN_PAGETYPE_META
Definition: brin_page.h:51
#define BRIN_PAGETYPE_REGULAR
Definition: brin_page.h:53
#define DEBUG2
Definition: elog.h:24
void brinRevmapExtend(BrinRevmap *revmap, BlockNumber heapBlk)
Definition: brin_revmap.c:115
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1039
bool brin_tuples_equal(const BrinTuple *a, Size alen, const BrinTuple *b, Size blen)
Definition: brin_tuple.c:331
BlockNumber pagesPerRange
Definition: brin_xlog.h:68
static char * buf
Definition: pg_test_fsync.c:65
#define FirstOffsetNumber
Definition: off.h:27
#define REGBUF_STANDARD
Definition: xloginsert.h:35
static Size br_page_get_freespace(Page page)
Definition: brin_pageops.c:886
#define RelationGetRelationName(relation)
Definition: rel.h:433
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
bool brin_can_do_samepage_update(Buffer buffer, Size origsz, Size newsz)
Definition: brin_pageops.c:323
void FreeSpaceMapVacuum(Relation rel)
Definition: freespace.c:379
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
BlockNumber lastRevmapPage
Definition: brin_page.h:69
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
#define XLOG_BRIN_INIT_PAGE
Definition: brin_xlog.h:43
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define BRIN_IS_META_PAGE(page)
Definition: brin_page.h:55
Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk)
Definition: freespace.c:270
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
#define PageGetContents(page)
Definition: bufpage.h:243
#define XLOG_BRIN_INSERT
Definition: brin_xlog.h:32
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3529
#define InvalidOffsetNumber
Definition: off.h:26
xl_brin_insert insert
Definition: brin_xlog.h:92
BlockNumber pagesPerRange
Definition: brin_page.h:68
#define BlockNumberIsValid(blockNumber)
Definition: block.h:70
BlockNumber bt_blkno
Definition: brin_tuple.h:52
void PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
Definition: bufpage.c:931
#define BrinMaxItemSize
Definition: brin_pageops.c:30
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:671
#define ItemIdIsNormal(itemId)
Definition: itemid.h:98
#define SizeOfBrinSamepageUpdate
Definition: brin_xlog.h:107
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define BRIN_IS_REVMAP_PAGE(page)
Definition: brin_page.h:56
#define RelationSetTargetBlock(relation, targblock)
Definition: rel.h:492
size_t Size
Definition: c.h:353
#define XLOG_BRIN_SAMEPAGE_UPDATE
Definition: brin_xlog.h:34
void brin_evacuate_page(Relation idxRel, BlockNumber pagesPerRange, BrinRevmap *revmap, Buffer buf)
Definition: brin_pageops.c:545
#define InvalidBlockNumber
Definition: block.h:33
OffsetNumber offnum
Definition: brin_xlog.h:71
static void brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer)
Definition: brin_pageops.c:854
#define MAXALIGN(LEN)
Definition: c.h:584
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
#define RelationNeedsWAL(relation)
Definition: rel.h:502
#define BrinPageType(page)
Definition: brin_page.h:42
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:606
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2588
#define PageIsNew(page)
Definition: bufpage.h:226
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define ShareLock
Definition: lockdefs.h:41
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:88
BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded)
Definition: freespace.c:132
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:97
bool brin_page_cleanup(Relation idxrel, Buffer buf)
Definition: brin_pageops.c:597
static Buffer brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz, bool *extended)
Definition: brin_pageops.c:664
#define BRIN_EVACUATE_PAGE
Definition: brin_page.h:60
bool brin_start_evacuating_page(Relation idxRel, Buffer buf)
Definition: brin_pageops.c:510
BlockNumber RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage, Size oldSpaceAvail, Size spaceNeeded)
Definition: freespace.c:149
#define elog
Definition: elog.h:219
#define BrinPageFlags(page)
Definition: brin_page.h:46
void brinSetHeapBlockItemptr(Buffer buf, BlockNumber pagesPerRange, BlockNumber heapBlk, ItemPointerData tid)
Definition: brin_revmap.c:158
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:365
OffsetNumber oldOffnum
Definition: brin_xlog.h:90
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:86
void brin_page_init(Page page, uint16 type)
Definition: brin_pageops.c:469
#define BRIN_META_MAGIC
Definition: brin_page.h:73
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:41