PostgreSQL Source Code  git master
gist.c File Reference
#include "postgres.h"
#include "access/gist_private.h"
#include "access/gistscan.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for gist.c:

Go to the source code of this file.

Macros

#define ROTATEDIST(d)
 

Functions

static void gistfixsplit (GISTInsertState *state, GISTSTATE *giststate)
 
static bool gistinserttuple (GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
 
static bool gistinserttuples (GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild, Buffer rightchild, bool unlockbuf, bool unlockleftchild)
 
static void gistfinishsplit (GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
 
static void gistprunepage (Relation rel, Page page, Buffer buffer, Relation heapRel)
 
Datum gisthandler (PG_FUNCTION_ARGS)
 
MemoryContext createTempGistContext (void)
 
void gistbuildempty (Relation index)
 
bool gistinsert (Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
 
bool gistplacetopage (Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright, Relation heapRel, bool is_build)
 
void gistdoinsert (Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
 
static GISTInsertStackgistFindPath (Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
 
static void gistFindCorrectParent (Relation r, GISTInsertStack *child)
 
static IndexTuple gistformdownlink (Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack)
 
SplitedPageLayoutgistSplit (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
 
GISTSTATEinitGISTstate (Relation index)
 
void freeGISTstate (GISTSTATE *giststate)
 

Macro Definition Documentation

◆ ROTATEDIST

#define ROTATEDIST (   d)
Value:
do { \
SplitedPageLayout *tmp=(SplitedPageLayout*)palloc0(sizeof(SplitedPageLayout)); \
tmp->block.blkno = InvalidBlockNumber; \
tmp->buffer = InvalidBuffer; \
tmp->next = (d); \
(d)=tmp; \
} while(0)
#define InvalidBlockNumber
Definition: block.h:33
#define InvalidBuffer
Definition: buf.h:25
void * palloc0(Size size)
Definition: mcxt.c:1099

Definition at line 46 of file gist.c.

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 120 of file gist.c.

121 {
123  "GiST temporary context",
125 }
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

Referenced by gist_xlog_startup(), gistbeginscan(), gistbuild(), and gistinsert().

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1632 of file gist.c.

1633 {
1634  /* It's sufficient to delete the scanCxt */
1635  MemoryContextDelete(giststate->scanCxt);
1636 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
MemoryContext scanCxt
Definition: gist_private.h:77

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 131 of file gist.c.

132 {
133  Buffer buffer;
134 
135  /* Initialize the root page */
138 
139  /* Initialize and xlog buffer */
141  GISTInitBuffer(buffer, F_LEAF);
142  MarkBufferDirty(buffer);
143  log_newpage_buffer(buffer, true);
145 
146  /* Unlock and release the buffer */
147  UnlockReleaseBuffer(buffer);
148 }
int Buffer
Definition: buf.h:23
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3938
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1573
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4156
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:749
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
@ RBM_NORMAL
Definition: bufmgr.h:39
#define F_LEAF
Definition: gist.h:46
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
#define START_CRIT_SECTION()
Definition: miscadmin.h:148
#define END_CRIT_SECTION()
Definition: miscadmin.h:150
@ INIT_FORKNUM
Definition: relpath.h:46
Definition: type.h:90
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1177

References BUFFER_LOCK_EXCLUSIVE, END_CRIT_SECTION, F_LEAF, GISTInitBuffer(), INIT_FORKNUM, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), P_NEW, RBM_NORMAL, ReadBufferExtended(), START_CRIT_SECTION, and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistdoinsert()

void gistdoinsert ( Relation  r,
IndexTuple  itup,
Size  freespace,
GISTSTATE giststate,
Relation  heapRel,
bool  is_build 
)

Definition at line 633 of file gist.c.

635 {
636  ItemId iid;
637  IndexTuple idxtuple;
638  GISTInsertStack firststack;
639  GISTInsertStack *stack;
641  bool xlocked = false;
642 
643  memset(&state, 0, sizeof(GISTInsertState));
644  state.freespace = freespace;
645  state.r = r;
646  state.heapRel = heapRel;
647  state.is_build = is_build;
648 
649  /* Start from the root */
650  firststack.blkno = GIST_ROOT_BLKNO;
651  firststack.lsn = 0;
652  firststack.retry_from_parent = false;
653  firststack.parent = NULL;
654  firststack.downlinkoffnum = InvalidOffsetNumber;
655  state.stack = stack = &firststack;
656 
657  /*
658  * Walk down along the path of smallest penalty, updating the parent
659  * pointers with the key we're inserting as we go. If we crash in the
660  * middle, the tree is consistent, although the possible parent updates
661  * were a waste.
662  */
663  for (;;)
664  {
665  /*
666  * If we split an internal page while descending the tree, we have to
667  * retry at the parent. (Normally, the LSN-NSN interlock below would
668  * also catch this and cause us to retry. But LSNs are not updated
669  * during index build.)
670  */
671  while (stack->retry_from_parent)
672  {
673  if (xlocked)
674  LockBuffer(stack->buffer, GIST_UNLOCK);
675  xlocked = false;
676  ReleaseBuffer(stack->buffer);
677  state.stack = stack = stack->parent;
678  }
679 
680  if (XLogRecPtrIsInvalid(stack->lsn))
681  stack->buffer = ReadBuffer(state.r, stack->blkno);
682 
683  /*
684  * Be optimistic and grab shared lock first. Swap it for an exclusive
685  * lock later if we need to update the page.
686  */
687  if (!xlocked)
688  {
689  LockBuffer(stack->buffer, GIST_SHARE);
690  gistcheckpage(state.r, stack->buffer);
691  }
692 
693  stack->page = (Page) BufferGetPage(stack->buffer);
694  stack->lsn = xlocked ?
695  PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
697 
698  /*
699  * If this page was split but the downlink was never inserted to the
700  * parent because the inserting backend crashed before doing that, fix
701  * that now.
702  */
703  if (GistFollowRight(stack->page))
704  {
705  if (!xlocked)
706  {
707  LockBuffer(stack->buffer, GIST_UNLOCK);
709  xlocked = true;
710  /* someone might've completed the split when we unlocked */
711  if (!GistFollowRight(stack->page))
712  continue;
713  }
714  gistfixsplit(&state, giststate);
715 
716  UnlockReleaseBuffer(stack->buffer);
717  xlocked = false;
718  state.stack = stack = stack->parent;
719  continue;
720  }
721 
722  if ((stack->blkno != GIST_ROOT_BLKNO &&
723  stack->parent->lsn < GistPageGetNSN(stack->page)) ||
724  GistPageIsDeleted(stack->page))
725  {
726  /*
727  * Concurrent split or page deletion detected. There's no
728  * guarantee that the downlink for this page is consistent with
729  * the tuple we're inserting anymore, so go back to parent and
730  * rechoose the best child.
731  */
732  UnlockReleaseBuffer(stack->buffer);
733  xlocked = false;
734  state.stack = stack = stack->parent;
735  continue;
736  }
737 
738  if (!GistPageIsLeaf(stack->page))
739  {
740  /*
741  * This is an internal page so continue to walk down the tree.
742  * Find the child node that has the minimum insertion penalty.
743  */
744  BlockNumber childblkno;
745  IndexTuple newtup;
746  GISTInsertStack *item;
747  OffsetNumber downlinkoffnum;
748 
749  downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
750  iid = PageGetItemId(stack->page, downlinkoffnum);
751  idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
752  childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
753 
754  /*
755  * Check that it's not a leftover invalid tuple from pre-9.1
756  */
757  if (GistTupleIsInvalid(idxtuple))
758  ereport(ERROR,
759  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
761  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
762  errhint("Please REINDEX it.")));
763 
764  /*
765  * Check that the key representing the target child node is
766  * consistent with the key we're inserting. Update it if it's not.
767  */
768  newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
769  if (newtup)
770  {
771  /*
772  * Swap shared lock for an exclusive one. Beware, the page may
773  * change while we unlock/lock the page...
774  */
775  if (!xlocked)
776  {
777  LockBuffer(stack->buffer, GIST_UNLOCK);
779  xlocked = true;
780  stack->page = (Page) BufferGetPage(stack->buffer);
781 
782  if (PageGetLSN(stack->page) != stack->lsn)
783  {
784  /* the page was changed while we unlocked it, retry */
785  continue;
786  }
787  }
788 
789  /*
790  * Update the tuple.
791  *
792  * We still hold the lock after gistinserttuple(), but it
793  * might have to split the page to make the updated tuple fit.
794  * In that case the updated tuple might migrate to the other
795  * half of the split, so we have to go back to the parent and
796  * descend back to the half that's a better fit for the new
797  * tuple.
798  */
799  if (gistinserttuple(&state, stack, giststate, newtup,
800  downlinkoffnum))
801  {
802  /*
803  * If this was a root split, the root page continues to be
804  * the parent and the updated tuple went to one of the
805  * child pages, so we just need to retry from the root
806  * page.
807  */
808  if (stack->blkno != GIST_ROOT_BLKNO)
809  {
810  UnlockReleaseBuffer(stack->buffer);
811  xlocked = false;
812  state.stack = stack = stack->parent;
813  }
814  continue;
815  }
816  }
817  LockBuffer(stack->buffer, GIST_UNLOCK);
818  xlocked = false;
819 
820  /* descend to the chosen child */
821  item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
822  item->blkno = childblkno;
823  item->parent = stack;
824  item->downlinkoffnum = downlinkoffnum;
825  state.stack = stack = item;
826  }
827  else
828  {
829  /*
830  * Leaf page. Insert the new key. We've already updated all the
831  * parents on the way down, but we might have to split the page if
832  * it doesn't fit. gistinserttuple() will take care of that.
833  */
834 
835  /*
836  * Swap shared lock for an exclusive one. Be careful, the page may
837  * change while we unlock/lock the page...
838  */
839  if (!xlocked)
840  {
841  LockBuffer(stack->buffer, GIST_UNLOCK);
843  xlocked = true;
844  stack->page = (Page) BufferGetPage(stack->buffer);
845  stack->lsn = PageGetLSN(stack->page);
846 
847  if (stack->blkno == GIST_ROOT_BLKNO)
848  {
849  /*
850  * the only page that can become inner instead of leaf is
851  * the root page, so for root we should recheck it
852  */
853  if (!GistPageIsLeaf(stack->page))
854  {
855  /*
856  * very rare situation: during unlock/lock index with
857  * number of pages = 1 was increased
858  */
859  LockBuffer(stack->buffer, GIST_UNLOCK);
860  xlocked = false;
861  continue;
862  }
863 
864  /*
865  * we don't need to check root split, because checking
866  * leaf/inner is enough to recognize split for root
867  */
868  }
869  else if ((GistFollowRight(stack->page) ||
870  stack->parent->lsn < GistPageGetNSN(stack->page)) ||
871  GistPageIsDeleted(stack->page))
872  {
873  /*
874  * The page was split or deleted while we momentarily
875  * unlocked the page. Go back to parent.
876  */
877  UnlockReleaseBuffer(stack->buffer);
878  xlocked = false;
879  state.stack = stack = stack->parent;
880  continue;
881  }
882  }
883 
884  /* now state.stack->(page, buffer and blkno) points to leaf page */
885 
886  gistinserttuple(&state, stack, giststate, itup,
888  LockBuffer(stack->buffer, GIST_UNLOCK);
889 
890  /* Release any pins we might still hold before exiting */
891  for (; stack; stack = stack->parent)
892  ReleaseBuffer(stack->buffer);
893  break;
894  }
895  }
896 }
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3915
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:3004
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:702
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Pointer Page
Definition: bufpage.h:78
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:234
#define PageGetItem(page, itemId)
Definition: bufpage.h:339
#define PageGetLSN(page)
Definition: bufpage.h:365
int errdetail(const char *fmt,...)
Definition: elog.c:1037
int errhint(const char *fmt,...)
Definition: elog.c:1151
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1168
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1228
#define GistPageIsLeaf(page)
Definition: gist.h:167
#define GistFollowRight(page)
Definition: gist.h:180
#define GistPageIsDeleted(page)
Definition: gist.h:170
#define GistPageGetNSN(page)
Definition: gist.h:184
#define GIST_UNLOCK
Definition: gist_private.h:44
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_EXCLUSIVE
Definition: gist_private.h:43
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:288
#define GIST_SHARE
Definition: gist_private.h:42
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:316
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:374
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:785
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
IndexTupleData * IndexTuple
Definition: itup.h:53
Assert(fmt[strlen(fmt) - 1] !='\n')
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define RelationGetRelationName(relation)
Definition: rel.h:522
#define RelationNeedsWAL(relation)
Definition: rel.h:612
BlockNumber blkno
Definition: gist_private.h:210
OffsetNumber downlinkoffnum
Definition: gist_private.h:228
struct GISTInsertStack * parent
Definition: gist_private.h:231
ItemPointerData t_tid
Definition: itup.h:37
Definition: regguts.h:318
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29

References Assert(), GISTInsertStack::blkno, GISTInsertStack::buffer, BufferGetLSNAtomic(), BufferGetPage, GISTInsertStack::downlinkoffnum, ereport, errdetail(), errhint(), errmsg(), ERROR, GIST_EXCLUSIVE, GIST_ROOT_BLKNO, GIST_SHARE, GIST_UNLOCK, gistcheckpage(), gistchoose(), gistfixsplit(), GistFollowRight, gistgetadjusted(), gistinserttuple(), GistPageGetNSN, GistPageIsDeleted, GistPageIsLeaf, GistTupleIsInvalid, InvalidOffsetNumber, ItemPointerGetBlockNumber, LockBuffer(), GISTInsertStack::lsn, GISTInsertStack::page, PageGetItem, PageGetItemId, PageGetLSN, palloc0(), GISTInsertStack::parent, ReadBuffer(), RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), GISTInsertStack::retry_from_parent, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogRecPtrIsInvalid.

Referenced by gistBuildCallback(), and gistinsert().

◆ gistFindCorrectParent()

static void gistFindCorrectParent ( Relation  r,
GISTInsertStack child 
)
static

Definition at line 1021 of file gist.c.

1022 {
1023  GISTInsertStack *parent = child->parent;
1024 
1025  gistcheckpage(r, parent->buffer);
1026  parent->page = (Page) BufferGetPage(parent->buffer);
1027 
1028  /* here we don't need to distinguish between split and page update */
1029  if (child->downlinkoffnum == InvalidOffsetNumber ||
1030  parent->lsn != PageGetLSN(parent->page))
1031  {
1032  /* parent is changed, look child in right links until found */
1033  OffsetNumber i,
1034  maxoff;
1035  ItemId iid;
1036  IndexTuple idxtuple;
1037  GISTInsertStack *ptr;
1038 
1039  while (true)
1040  {
1041  maxoff = PageGetMaxOffsetNumber(parent->page);
1042  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1043  {
1044  iid = PageGetItemId(parent->page, i);
1045  idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
1046  if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
1047  {
1048  /* yes!!, found */
1049  child->downlinkoffnum = i;
1050  return;
1051  }
1052  }
1053 
1054  parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
1055  UnlockReleaseBuffer(parent->buffer);
1056  if (parent->blkno == InvalidBlockNumber)
1057  {
1058  /*
1059  * End of chain and still didn't find parent. It's a very-very
1060  * rare situation when root splitted.
1061  */
1062  break;
1063  }
1064  parent->buffer = ReadBuffer(r, parent->blkno);
1065  LockBuffer(parent->buffer, GIST_EXCLUSIVE);
1066  gistcheckpage(r, parent->buffer);
1067  parent->page = (Page) BufferGetPage(parent->buffer);
1068  }
1069 
1070  /*
1071  * awful!!, we need search tree to find parent ... , but before we
1072  * should release all old parent
1073  */
1074 
1075  ptr = child->parent->parent; /* child->parent already released
1076  * above */
1077  while (ptr)
1078  {
1079  ReleaseBuffer(ptr->buffer);
1080  ptr = ptr->parent;
1081  }
1082 
1083  /* ok, find new path */
1084  ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
1085 
1086  /* read all buffers as expected by caller */
1087  /* note we don't lock them or gistcheckpage them here! */
1088  while (ptr)
1089  {
1090  ptr->buffer = ReadBuffer(r, ptr->blkno);
1091  ptr->page = (Page) BufferGetPage(ptr->buffer);
1092  ptr = ptr->parent;
1093  }
1094 
1095  /* install new chain of parents to stack */
1096  child->parent = parent;
1097 
1098  /* make recursive call to normal processing */
1100  gistFindCorrectParent(r, child);
1101  }
1102 }
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:356
static GISTInsertStack * gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
Definition: gist.c:908
static void gistFindCorrectParent(Relation r, GISTInsertStack *child)
Definition: gist.c:1021
#define GistPageGetOpaque(page)
Definition: gist.h:165
int i
Definition: isn.c:73
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define FirstOffsetNumber
Definition: off.h:27

References GISTInsertStack::blkno, GISTInsertStack::buffer, BufferGetPage, GISTInsertStack::downlinkoffnum, FirstOffsetNumber, GIST_EXCLUSIVE, gistcheckpage(), gistFindPath(), GistPageGetOpaque, i, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerGetBlockNumber, LockBuffer(), GISTInsertStack::lsn, OffsetNumberNext, GISTInsertStack::page, PageGetItem, PageGetItemId, PageGetLSN, PageGetMaxOffsetNumber, GISTInsertStack::parent, ReadBuffer(), ReleaseBuffer(), IndexTupleData::t_tid, and UnlockReleaseBuffer().

Referenced by gistfinishsplit(), and gistformdownlink().

◆ gistFindPath()

static GISTInsertStack* gistFindPath ( Relation  r,
BlockNumber  child,
OffsetNumber downlinkoffnum 
)
static

Definition at line 908 of file gist.c.

909 {
910  Page page;
911  Buffer buffer;
912  OffsetNumber i,
913  maxoff;
914  ItemId iid;
915  IndexTuple idxtuple;
916  List *fifo;
917  GISTInsertStack *top,
918  *ptr;
919  BlockNumber blkno;
920 
921  top = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
922  top->blkno = GIST_ROOT_BLKNO;
924 
925  fifo = list_make1(top);
926  while (fifo != NIL)
927  {
928  /* Get next page to visit */
929  top = linitial(fifo);
930  fifo = list_delete_first(fifo);
931 
932  buffer = ReadBuffer(r, top->blkno);
933  LockBuffer(buffer, GIST_SHARE);
934  gistcheckpage(r, buffer);
935  page = (Page) BufferGetPage(buffer);
936 
937  if (GistPageIsLeaf(page))
938  {
939  /*
940  * Because we scan the index top-down, all the rest of the pages
941  * in the queue must be leaf pages as well.
942  */
943  UnlockReleaseBuffer(buffer);
944  break;
945  }
946 
947  /* currently, internal pages are never deleted */
948  Assert(!GistPageIsDeleted(page));
949 
950  top->lsn = BufferGetLSNAtomic(buffer);
951 
952  /*
953  * If F_FOLLOW_RIGHT is set, the page to the right doesn't have a
954  * downlink. This should not normally happen..
955  */
956  if (GistFollowRight(page))
957  elog(ERROR, "concurrent GiST page split was incomplete");
958 
959  if (top->parent && top->parent->lsn < GistPageGetNSN(page) &&
960  GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ )
961  {
962  /*
963  * Page was split while we looked elsewhere. We didn't see the
964  * downlink to the right page when we scanned the parent, so add
965  * it to the queue now.
966  *
967  * Put the right page ahead of the queue, so that we visit it
968  * next. That's important, because if this is the lowest internal
969  * level, just above leaves, we might already have queued up some
970  * leaf pages, and we assume that there can't be any non-leaf
971  * pages behind leaf pages.
972  */
973  ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
974  ptr->blkno = GistPageGetOpaque(page)->rightlink;
976  ptr->parent = top->parent;
977 
978  fifo = lcons(ptr, fifo);
979  }
980 
981  maxoff = PageGetMaxOffsetNumber(page);
982 
983  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
984  {
985  iid = PageGetItemId(page, i);
986  idxtuple = (IndexTuple) PageGetItem(page, iid);
987  blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
988  if (blkno == child)
989  {
990  /* Found it! */
991  UnlockReleaseBuffer(buffer);
992  *downlinkoffnum = i;
993  return top;
994  }
995  else
996  {
997  /* Append this child to the list of pages to visit later */
998  ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
999  ptr->blkno = blkno;
1000  ptr->downlinkoffnum = i;
1001  ptr->parent = top;
1002 
1003  fifo = lappend(fifo, ptr);
1004  }
1005  }
1006 
1007  UnlockReleaseBuffer(buffer);
1008  }
1009 
1010  elog(ERROR, "failed to re-find parent of a page in index \"%s\", block %u",
1011  RelationGetRelationName(r), child);
1012  return NULL; /* keep compiler quiet */
1013 }
#define elog(elevel,...)
Definition: elog.h:218
List * lappend(List *list, void *datum)
Definition: list.c:336
List * list_delete_first(List *list)
Definition: list.c:902
List * lcons(void *datum, List *list)
Definition: list.c:474
#define NIL
Definition: pg_list.h:65
#define list_make1(x1)
Definition: pg_list.h:206
#define linitial(l)
Definition: pg_list.h:174
Definition: pg_list.h:51

References Assert(), GISTInsertStack::blkno, BufferGetLSNAtomic(), BufferGetPage, GISTInsertStack::downlinkoffnum, elog, ERROR, FirstOffsetNumber, GIST_ROOT_BLKNO, GIST_SHARE, gistcheckpage(), GistFollowRight, GistPageGetNSN, GistPageGetOpaque, GistPageIsDeleted, GistPageIsLeaf, i, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerGetBlockNumber, lappend(), lcons(), linitial, list_delete_first(), list_make1, LockBuffer(), GISTInsertStack::lsn, NIL, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, palloc0(), GISTInsertStack::parent, ReadBuffer(), RelationGetRelationName, IndexTupleData::t_tid, and UnlockReleaseBuffer().

Referenced by gistFindCorrectParent().

◆ gistfinishsplit()

static void gistfinishsplit ( GISTInsertState state,
GISTInsertStack stack,
GISTSTATE giststate,
List splitinfo,
bool  unlockbuf 
)
static

Definition at line 1322 of file gist.c.

1324 {
1325  GISTPageSplitInfo *right;
1326  GISTPageSplitInfo *left;
1327  IndexTuple tuples[2];
1328 
1329  /* A split always contains at least two halves */
1330  Assert(list_length(splitinfo) >= 2);
1331 
1332  /*
1333  * We need to insert downlinks for each new page, and update the downlink
1334  * for the original (leftmost) page in the split. Begin at the rightmost
1335  * page, inserting one downlink at a time until there's only two pages
1336  * left. Finally insert the downlink for the last new page and update the
1337  * downlink for the original page as one operation.
1338  */
1340 
1341  /*
1342  * Insert downlinks for the siblings from right to left, until there are
1343  * only two siblings left.
1344  */
1345  for (int pos = list_length(splitinfo) - 1; pos > 1; pos--)
1346  {
1347  right = (GISTPageSplitInfo *) list_nth(splitinfo, pos);
1348  left = (GISTPageSplitInfo *) list_nth(splitinfo, pos - 1);
1349 
1350  gistFindCorrectParent(state->r, stack);
1351  if (gistinserttuples(state, stack->parent, giststate,
1352  &right->downlink, 1,
1354  left->buf, right->buf, false, false))
1355  {
1356  /*
1357  * If the parent page was split, the existing downlink might have
1358  * moved.
1359  */
1361  }
1362  /* gistinserttuples() released the lock on right->buf. */
1363  }
1364 
1365  right = (GISTPageSplitInfo *) lsecond(splitinfo);
1366  left = (GISTPageSplitInfo *) linitial(splitinfo);
1367 
1368  /*
1369  * Finally insert downlink for the remaining right page and update the
1370  * downlink for the original page to not contain the tuples that were
1371  * moved to the new pages.
1372  */
1373  tuples[0] = left->downlink;
1374  tuples[1] = right->downlink;
1375  gistFindCorrectParent(state->r, stack);
1376  if (gistinserttuples(state, stack->parent, giststate,
1377  tuples, 2,
1378  stack->downlinkoffnum,
1379  left->buf, right->buf,
1380  true, /* Unlock parent */
1381  unlockbuf /* Unlock stack->buffer if caller wants
1382  * that */
1383  ))
1384  {
1385  /*
1386  * If the parent page was split, the downlink might have moved.
1387  */
1389  }
1390 
1391  Assert(left->buf == stack->buffer);
1392 
1393  /*
1394  * If we split the page because we had to adjust the downlink on an
1395  * internal page, while descending the tree for inserting a new tuple,
1396  * then this might no longer be the correct page for the new tuple. The
1397  * downlink to this page might not cover the new tuple anymore, it might
1398  * need to go to the newly-created right sibling instead. Tell the caller
1399  * to walk back up the stack, to re-check at the parent which page to
1400  * insert to.
1401  *
1402  * Normally, the LSN-NSN interlock during the tree descend would also
1403  * detect that a concurrent split happened (by ourselves), and cause us to
1404  * retry at the parent. But that mechanism doesn't work during index
1405  * build, because we don't do WAL-logging, and don't update LSNs, during
1406  * index build.
1407  */
1408  stack->retry_from_parent = true;
1409 }
static bool gistinserttuples(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild, Buffer rightchild, bool unlockbuf, bool unlockleftchild)
Definition: gist.c:1262
static int list_length(const List *l)
Definition: pg_list.h:149
#define lsecond(l)
Definition: pg_list.h:179
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
IndexTuple downlink
Definition: gist_private.h:422

References Assert(), GISTPageSplitInfo::buf, GISTInsertStack::buffer, GISTPageSplitInfo::downlink, GISTInsertStack::downlinkoffnum, GIST_EXCLUSIVE, gistFindCorrectParent(), gistinserttuples(), InvalidOffsetNumber, linitial, list_length(), list_nth(), LockBuffer(), lsecond, GISTInsertStack::parent, and GISTInsertStack::retry_from_parent.

Referenced by gistfixsplit(), and gistinserttuples().

◆ gistfixsplit()

static void gistfixsplit ( GISTInsertState state,
GISTSTATE giststate 
)
static

Definition at line 1168 of file gist.c.

1169 {
1170  GISTInsertStack *stack = state->stack;
1171  Buffer buf;
1172  Page page;
1173  List *splitinfo = NIL;
1174 
1175  ereport(LOG,
1176  (errmsg("fixing incomplete split in index \"%s\", block %u",
1177  RelationGetRelationName(state->r), stack->blkno)));
1178 
1179  Assert(GistFollowRight(stack->page));
1181 
1182  buf = stack->buffer;
1183 
1184  /*
1185  * Read the chain of split pages, following the rightlinks. Construct a
1186  * downlink tuple for each page.
1187  */
1188  for (;;)
1189  {
1191  IndexTuple downlink;
1192 
1193  page = BufferGetPage(buf);
1194 
1195  /* Form the new downlink tuples to insert to parent */
1196  downlink = gistformdownlink(state->r, buf, giststate, stack);
1197 
1198  si->buf = buf;
1199  si->downlink = downlink;
1200 
1201  splitinfo = lappend(splitinfo, si);
1202 
1203  if (GistFollowRight(page))
1204  {
1205  /* lock next page */
1206  buf = ReadBuffer(state->r, GistPageGetOpaque(page)->rightlink);
1208  }
1209  else
1210  break;
1211  }
1212 
1213  /* Insert the downlinks */
1214  gistfinishsplit(state, stack, giststate, splitinfo, false);
1215 }
#define LOG
Definition: elog.h:25
static IndexTuple gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack)
Definition: gist.c:1108
static void gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
Definition: gist.c:1322
void * palloc(Size size)
Definition: mcxt.c:1068
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
static char * buf
Definition: pg_test_fsync.c:67

References Assert(), GISTInsertStack::blkno, buf, GISTPageSplitInfo::buf, GISTInsertStack::buffer, BufferGetPage, GISTPageSplitInfo::downlink, GISTInsertStack::downlinkoffnum, ereport, errmsg(), GIST_EXCLUSIVE, gistfinishsplit(), GistFollowRight, gistformdownlink(), GistPageGetOpaque, lappend(), LockBuffer(), LOG, NIL, OffsetNumberIsValid, GISTInsertStack::page, palloc(), ReadBuffer(), and RelationGetRelationName.

Referenced by gistdoinsert().

◆ gistformdownlink()

static IndexTuple gistformdownlink ( Relation  rel,
Buffer  buf,
GISTSTATE giststate,
GISTInsertStack stack 
)
static

Definition at line 1108 of file gist.c.

1110 {
1111  Page page = BufferGetPage(buf);
1112  OffsetNumber maxoff;
1113  OffsetNumber offset;
1114  IndexTuple downlink = NULL;
1115 
1116  maxoff = PageGetMaxOffsetNumber(page);
1117  for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
1118  {
1119  IndexTuple ituple = (IndexTuple)
1120  PageGetItem(page, PageGetItemId(page, offset));
1121 
1122  if (downlink == NULL)
1123  downlink = CopyIndexTuple(ituple);
1124  else
1125  {
1126  IndexTuple newdownlink;
1127 
1128  newdownlink = gistgetadjusted(rel, downlink, ituple,
1129  giststate);
1130  if (newdownlink)
1131  downlink = newdownlink;
1132  }
1133  }
1134 
1135  /*
1136  * If the page is completely empty, we can't form a meaningful downlink
1137  * for it. But we have to insert a downlink for the page. Any key will do,
1138  * as long as its consistent with the downlink of parent page, so that we
1139  * can legally insert it to the parent. A minimal one that matches as few
1140  * scans as possible would be best, to keep scans from doing useless work,
1141  * but we don't know how to construct that. So we just use the downlink of
1142  * the original page that was split - that's as far from optimal as it can
1143  * get but will do..
1144  */
1145  if (!downlink)
1146  {
1147  ItemId iid;
1148 
1150  gistFindCorrectParent(rel, stack);
1151  iid = PageGetItemId(stack->parent->page, stack->downlinkoffnum);
1152  downlink = (IndexTuple) PageGetItem(stack->parent->page, iid);
1153  downlink = CopyIndexTuple(downlink);
1154  LockBuffer(stack->parent->buffer, GIST_UNLOCK);
1155  }
1156 
1158  GistTupleSetValid(downlink);
1159 
1160  return downlink;
1161 }
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2755
#define GistTupleSetValid(itup)
Definition: gist_private.h:289
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:528
#define ItemPointerSetBlockNumber(pointer, blockNumber)
Definition: itemptr.h:138

References buf, GISTInsertStack::buffer, BufferGetBlockNumber(), BufferGetPage, CopyIndexTuple(), GISTInsertStack::downlinkoffnum, FirstOffsetNumber, GIST_EXCLUSIVE, GIST_UNLOCK, gistFindCorrectParent(), gistgetadjusted(), GistTupleSetValid, ItemPointerSetBlockNumber, LockBuffer(), OffsetNumberNext, GISTInsertStack::page, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, GISTInsertStack::parent, and IndexTupleData::t_tid.

Referenced by gistfixsplit().

◆ gisthandler()

Datum gisthandler ( PG_FUNCTION_ARGS  )

Definition at line 60 of file gist.c.

61 {
63 
64  amroutine->amstrategies = 0;
65  amroutine->amsupport = GISTNProcs;
66  amroutine->amoptsprocnum = GIST_OPTIONS_PROC;
67  amroutine->amcanorder = false;
68  amroutine->amcanorderbyop = true;
69  amroutine->amcanbackward = false;
70  amroutine->amcanunique = false;
71  amroutine->amcanmulticol = true;
72  amroutine->amoptionalkey = true;
73  amroutine->amsearcharray = false;
74  amroutine->amsearchnulls = true;
75  amroutine->amstorage = true;
76  amroutine->amclusterable = true;
77  amroutine->ampredlocks = true;
78  amroutine->amcanparallel = false;
79  amroutine->amcaninclude = true;
80  amroutine->amusemaintenanceworkmem = false;
81  amroutine->amparallelvacuumoptions =
83  amroutine->amkeytype = InvalidOid;
84 
85  amroutine->ambuild = gistbuild;
86  amroutine->ambuildempty = gistbuildempty;
87  amroutine->aminsert = gistinsert;
88  amroutine->ambulkdelete = gistbulkdelete;
90  amroutine->amcanreturn = gistcanreturn;
91  amroutine->amcostestimate = gistcostestimate;
92  amroutine->amoptions = gistoptions;
93  amroutine->amproperty = gistproperty;
94  amroutine->ambuildphasename = NULL;
95  amroutine->amvalidate = gistvalidate;
97  amroutine->ambeginscan = gistbeginscan;
98  amroutine->amrescan = gistrescan;
99  amroutine->amgettuple = gistgettuple;
100  amroutine->amgetbitmap = gistgetbitmap;
101  amroutine->amendscan = gistendscan;
102  amroutine->ammarkpos = NULL;
103  amroutine->amrestrpos = NULL;
104  amroutine->amestimateparallelscan = NULL;
105  amroutine->aminitparallelscan = NULL;
106  amroutine->amparallelrescan = NULL;
107 
108  PG_RETURN_POINTER(amroutine);
109 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
bool gistinsert(Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: gist.c:157
void gistbuildempty(Relation index)
Definition: gist.c:131
#define GISTNProcs
Definition: gist.h:41
#define GIST_OPTIONS_PROC
Definition: gist.h:39
IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: gistbuild.c:182
bool gistgettuple(IndexScanDesc scan, ScanDirection dir)
Definition: gistget.c:614
int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: gistget.c:745
bool gistcanreturn(Relation index, int attno)
Definition: gistget.c:795
IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys)
Definition: gistscan.c:74
void gistendscan(IndexScanDesc scan)
Definition: gistscan.c:349
void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)
Definition: gistscan.c:127
bool gistproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: gistutil.c:942
bytea * gistoptions(Datum reloptions, bool validate)
Definition: gistutil.c:921
IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:59
IndexBulkDeleteResult * gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: gistvacuum.c:75
void gistadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: gistvalidate.c:291
bool gistvalidate(Oid opclassoid)
Definition: gistvalidate.c:34
#define makeNode(_type_)
Definition: nodes.h:621
#define InvalidOid
Definition: postgres_ext.h:36
void gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:6989
ambuildphasename_function ambuildphasename
Definition: amapi.h:268
ambuildempty_function ambuildempty
Definition: amapi.h:260
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:263
bool amclusterable
Definition: amapi.h:238
amoptions_function amoptions
Definition: amapi.h:266
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:280
amrestrpos_function amrestrpos
Definition: amapi.h:277
aminsert_function aminsert
Definition: amapi.h:261
amendscan_function amendscan
Definition: amapi.h:275
uint16 amoptsprocnum
Definition: amapi.h:218
amparallelrescan_function amparallelrescan
Definition: amapi.h:282
Oid amkeytype
Definition: amapi.h:250
bool ampredlocks
Definition: amapi.h:240
uint16 amsupport
Definition: amapi.h:216
amcostestimate_function amcostestimate
Definition: amapi.h:265
bool amcanorderbyop
Definition: amapi.h:222
amadjustmembers_function amadjustmembers
Definition: amapi.h:270
ambuild_function ambuild
Definition: amapi.h:259
bool amstorage
Definition: amapi.h:236
uint16 amstrategies
Definition: amapi.h:214
bool amoptionalkey
Definition: amapi.h:230
amgettuple_function amgettuple
Definition: amapi.h:273
amcanreturn_function amcanreturn
Definition: amapi.h:264
bool amcanunique
Definition: amapi.h:226
amgetbitmap_function amgetbitmap
Definition: amapi.h:274
amproperty_function amproperty
Definition: amapi.h:267
ambulkdelete_function ambulkdelete
Definition: amapi.h:262
bool amsearcharray
Definition: amapi.h:232
amvalidate_function amvalidate
Definition: amapi.h:269
ammarkpos_function ammarkpos
Definition: amapi.h:276
bool amcanmulticol
Definition: amapi.h:228
bool amusemaintenanceworkmem
Definition: amapi.h:246
ambeginscan_function ambeginscan
Definition: amapi.h:271
bool amcanparallel
Definition: amapi.h:242
amrescan_function amrescan
Definition: amapi.h:272
bool amcanorder
Definition: amapi.h:220
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:281
uint8 amparallelvacuumoptions
Definition: amapi.h:248
bool amcanbackward
Definition: amapi.h:224
bool amcaninclude
Definition: amapi.h:244
bool amsearchnulls
Definition: amapi.h:234
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:47
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:54

References IndexAmRoutine::amadjustmembers, IndexAmRoutine::ambeginscan, IndexAmRoutine::ambuild, IndexAmRoutine::ambuildempty, IndexAmRoutine::ambuildphasename, IndexAmRoutine::ambulkdelete, IndexAmRoutine::amcanbackward, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanorderbyop, IndexAmRoutine::amcanparallel, IndexAmRoutine::amcanreturn, IndexAmRoutine::amcanunique, IndexAmRoutine::amclusterable, IndexAmRoutine::amcostestimate, IndexAmRoutine::amendscan, IndexAmRoutine::amestimateparallelscan, IndexAmRoutine::amgetbitmap, IndexAmRoutine::amgettuple, IndexAmRoutine::aminitparallelscan, IndexAmRoutine::aminsert, IndexAmRoutine::amkeytype, IndexAmRoutine::ammarkpos, IndexAmRoutine::amoptionalkey, IndexAmRoutine::amoptions, IndexAmRoutine::amoptsprocnum, IndexAmRoutine::amparallelrescan, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::ampredlocks, IndexAmRoutine::amproperty, IndexAmRoutine::amrescan, IndexAmRoutine::amrestrpos, IndexAmRoutine::amsearcharray, IndexAmRoutine::amsearchnulls, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsupport, IndexAmRoutine::amusemaintenanceworkmem, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, GIST_OPTIONS_PROC, gistadjustmembers(), gistbeginscan(), gistbuild(), gistbuildempty(), gistbulkdelete(), gistcanreturn(), gistcostestimate(), gistendscan(), gistgetbitmap(), gistgettuple(), gistinsert(), GISTNProcs, gistoptions(), gistproperty(), gistrescan(), gistvacuumcleanup(), gistvalidate(), InvalidOid, makeNode, PG_RETURN_POINTER, VACUUM_OPTION_PARALLEL_BULKDEL, and VACUUM_OPTION_PARALLEL_COND_CLEANUP.

◆ gistinsert()

bool gistinsert ( Relation  r,
Datum values,
bool isnull,
ItemPointer  ht_ctid,
Relation  heapRel,
IndexUniqueCheck  checkUnique,
bool  indexUnchanged,
IndexInfo indexInfo 
)

Definition at line 157 of file gist.c.

162 {
163  GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
164  IndexTuple itup;
165  MemoryContext oldCxt;
166 
167  /* Initialize GISTSTATE cache if first call in this statement */
168  if (giststate == NULL)
169  {
170  oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
171  giststate = initGISTstate(r);
172  giststate->tempCxt = createTempGistContext();
173  indexInfo->ii_AmCache = (void *) giststate;
174  MemoryContextSwitchTo(oldCxt);
175  }
176 
177  oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
178 
179  itup = gistFormTuple(giststate, r,
180  values, isnull, true /* size is currently bogus */ );
181  itup->t_tid = *ht_ctid;
182 
183  gistdoinsert(r, itup, 0, giststate, heapRel, false);
184 
185  /* cleanup */
186  MemoryContextSwitchTo(oldCxt);
187  MemoryContextReset(giststate->tempCxt);
188 
189  return false;
190 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
Definition: gist.c:633
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1504
MemoryContext createTempGistContext(void)
Definition: gist.c:120
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf)
Definition: gistutil.c:575
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
MemoryContext tempCxt
Definition: gist_private.h:78
void * ii_AmCache
Definition: execnodes.h:184
MemoryContext ii_Context
Definition: execnodes.h:185

References createTempGistContext(), gistdoinsert(), gistFormTuple(), if(), IndexInfo::ii_AmCache, IndexInfo::ii_Context, initGISTstate(), MemoryContextReset(), MemoryContextSwitchTo(), GISTSTATE::tempCxt, and values.

Referenced by gisthandler().

◆ gistinserttuple()

static bool gistinserttuple ( GISTInsertState state,
GISTInsertStack stack,
GISTSTATE giststate,
IndexTuple  tuple,
OffsetNumber  oldoffnum 
)
static

Definition at line 1228 of file gist.c.

1230 {
1231  return gistinserttuples(state, stack, giststate, &tuple, 1, oldoffnum,
1232  InvalidBuffer, InvalidBuffer, false, false);
1233 }

References gistinserttuples(), and InvalidBuffer.

Referenced by gistdoinsert().

◆ gistinserttuples()

static bool gistinserttuples ( GISTInsertState state,
GISTInsertStack stack,
GISTSTATE giststate,
IndexTuple tuples,
int  ntup,
OffsetNumber  oldoffnum,
Buffer  leftchild,
Buffer  rightchild,
bool  unlockbuf,
bool  unlockleftchild 
)
static

Definition at line 1262 of file gist.c.

1267 {
1268  List *splitinfo;
1269  bool is_split;
1270 
1271  /*
1272  * Check for any rw conflicts (in serializable isolation level) just
1273  * before we intend to modify the page
1274  */
1276 
1277  /* Insert the tuple(s) to the page, splitting the page if necessary */
1278  is_split = gistplacetopage(state->r, state->freespace, giststate,
1279  stack->buffer,
1280  tuples, ntup,
1281  oldoffnum, NULL,
1282  leftchild,
1283  &splitinfo,
1284  true,
1285  state->heapRel,
1286  state->is_build);
1287 
1288  /*
1289  * Before recursing up in case the page was split, release locks on the
1290  * child pages. We don't need to keep them locked when updating the
1291  * parent.
1292  */
1295  if (BufferIsValid(leftchild) && unlockleftchild)
1297 
1298  /*
1299  * If we had to split, insert/update the downlinks in the parent. If the
1300  * caller requested us to release the lock on stack->buffer, tell
1301  * gistfinishsplit() to do that as soon as it's safe to do so. If we
1302  * didn't have to split, release it ourselves.
1303  */
1304  if (splitinfo)
1305  gistfinishsplit(state, stack, giststate, splitinfo, unlockbuf);
1306  else if (unlockbuf)
1307  LockBuffer(stack->buffer, GIST_UNLOCK);
1308 
1309  return is_split;
1310 }
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define rightchild(x)
Definition: fsmpage.c:30
#define leftchild(x)
Definition: fsmpage.c:29
bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright, Relation heapRel, bool is_build)
Definition: gist.c:223
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4448

References GISTInsertStack::buffer, BufferGetBlockNumber(), BufferIsValid, CheckForSerializableConflictIn(), GIST_UNLOCK, gistfinishsplit(), gistplacetopage(), leftchild, LockBuffer(), rightchild, and UnlockReleaseBuffer().

Referenced by gistfinishsplit(), and gistinserttuple().

◆ gistplacetopage()

bool gistplacetopage ( Relation  rel,
Size  freespace,
GISTSTATE giststate,
Buffer  buffer,
IndexTuple itup,
int  ntup,
OffsetNumber  oldoffnum,
BlockNumber newblkno,
Buffer  leftchildbuf,
List **  splitinfo,
bool  markfollowright,
Relation  heapRel,
bool  is_build 
)

Definition at line 223 of file gist.c.

232 {
233  BlockNumber blkno = BufferGetBlockNumber(buffer);
234  Page page = BufferGetPage(buffer);
235  bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
236  XLogRecPtr recptr;
237  int i;
238  bool is_split;
239 
240  /*
241  * Refuse to modify a page that's incompletely split. This should not
242  * happen because we finish any incomplete splits while we walk down the
243  * tree. However, it's remotely possible that another concurrent inserter
244  * splits a parent page, and errors out before completing the split. We
245  * will just throw an error in that case, and leave any split we had in
246  * progress unfinished too. The next insert that comes along will clean up
247  * the mess.
248  */
249  if (GistFollowRight(page))
250  elog(ERROR, "concurrent GiST page split was incomplete");
251 
252  /* should never try to insert to a deleted page */
253  Assert(!GistPageIsDeleted(page));
254 
255  *splitinfo = NIL;
256 
257  /*
258  * if isupdate, remove old key: This node's key has been modified, either
259  * because a child split occurred or because we needed to adjust our key
260  * for an insert in a child node. Therefore, remove the old version of
261  * this node's key.
262  *
263  * for WAL replay, in the non-split case we handle this by setting up a
264  * one-element todelete array; in the split case, it's handled implicitly
265  * because the tuple vector passed to gistSplit won't include this tuple.
266  */
267  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
268 
269  /*
270  * If leaf page is full, try at first to delete dead tuples. And then
271  * check again.
272  */
273  if (is_split && GistPageIsLeaf(page) && GistPageHasGarbage(page))
274  {
275  gistprunepage(rel, page, buffer, heapRel);
276  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
277  }
278 
279  if (is_split)
280  {
281  /* no space for insertion */
282  IndexTuple *itvec;
283  int tlen;
284  SplitedPageLayout *dist = NULL,
285  *ptr;
286  BlockNumber oldrlink = InvalidBlockNumber;
287  GistNSN oldnsn = 0;
288  SplitedPageLayout rootpg;
289  bool is_rootsplit;
290  int npage;
291 
292  is_rootsplit = (blkno == GIST_ROOT_BLKNO);
293 
294  /*
295  * Form index tuples vector to split. If we're replacing an old tuple,
296  * remove the old version from the vector.
297  */
298  itvec = gistextractpage(page, &tlen);
299  if (OffsetNumberIsValid(oldoffnum))
300  {
301  /* on inner page we should remove old tuple */
302  int pos = oldoffnum - FirstOffsetNumber;
303 
304  tlen--;
305  if (pos != tlen)
306  memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
307  }
308  itvec = gistjoinvector(itvec, &tlen, itup, ntup);
309  dist = gistSplit(rel, page, itvec, tlen, giststate);
310 
311  /*
312  * Check that split didn't produce too many pages.
313  */
314  npage = 0;
315  for (ptr = dist; ptr; ptr = ptr->next)
316  npage++;
317  /* in a root split, we'll add one more page to the list below */
318  if (is_rootsplit)
319  npage++;
320  if (npage > GIST_MAX_SPLIT_PAGES)
321  elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
322  npage, GIST_MAX_SPLIT_PAGES);
323 
324  /*
325  * Set up pages to work with. Allocate new buffers for all but the
326  * leftmost page. The original page becomes the new leftmost page, and
327  * is just replaced with the new contents.
328  *
329  * For a root-split, allocate new buffers for all child pages, the
330  * original page is overwritten with new root page containing
331  * downlinks to the new child pages.
332  */
333  ptr = dist;
334  if (!is_rootsplit)
335  {
336  /* save old rightlink and NSN */
337  oldrlink = GistPageGetOpaque(page)->rightlink;
338  oldnsn = GistPageGetNSN(page);
339 
340  dist->buffer = buffer;
341  dist->block.blkno = BufferGetBlockNumber(buffer);
343 
344  /* clean all flags except F_LEAF */
345  GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
346 
347  ptr = ptr->next;
348  }
349  for (; ptr; ptr = ptr->next)
350  {
351  /* Allocate new page */
352  ptr->buffer = gistNewBuffer(rel);
353  GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
354  ptr->page = BufferGetPage(ptr->buffer);
355  ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
357  BufferGetBlockNumber(buffer),
358  BufferGetBlockNumber(ptr->buffer));
359  }
360 
361  /*
362  * Now that we know which blocks the new pages go to, set up downlink
363  * tuples to point to them.
364  */
365  for (ptr = dist; ptr; ptr = ptr->next)
366  {
367  ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
368  GistTupleSetValid(ptr->itup);
369  }
370 
371  /*
372  * If this is a root split, we construct the new root page with the
373  * downlinks here directly, instead of requiring the caller to insert
374  * them. Add the new root page to the list along with the child pages.
375  */
376  if (is_rootsplit)
377  {
378  IndexTuple *downlinks;
379  int ndownlinks = 0;
380  int i;
381 
382  rootpg.buffer = buffer;
384  GistPageGetOpaque(rootpg.page)->flags = 0;
385 
386  /* Prepare a vector of all the downlinks */
387  for (ptr = dist; ptr; ptr = ptr->next)
388  ndownlinks++;
389  downlinks = palloc(sizeof(IndexTuple) * ndownlinks);
390  for (i = 0, ptr = dist; ptr; ptr = ptr->next)
391  downlinks[i++] = ptr->itup;
392 
393  rootpg.block.blkno = GIST_ROOT_BLKNO;
394  rootpg.block.num = ndownlinks;
395  rootpg.list = gistfillitupvec(downlinks, ndownlinks,
396  &(rootpg.lenlist));
397  rootpg.itup = NULL;
398 
399  rootpg.next = dist;
400  dist = &rootpg;
401  }
402  else
403  {
404  /* Prepare split-info to be returned to caller */
405  for (ptr = dist; ptr; ptr = ptr->next)
406  {
408 
409  si->buf = ptr->buffer;
410  si->downlink = ptr->itup;
411  *splitinfo = lappend(*splitinfo, si);
412  }
413  }
414 
415  /*
416  * Fill all pages. All the pages are new, ie. freshly allocated empty
417  * pages, or a temporary copy of the old page.
418  */
419  for (ptr = dist; ptr; ptr = ptr->next)
420  {
421  char *data = (char *) (ptr->list);
422 
423  for (i = 0; i < ptr->block.num; i++)
424  {
425  IndexTuple thistup = (IndexTuple) data;
426 
427  if (PageAddItem(ptr->page, (Item) data, IndexTupleSize(thistup), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
428  elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(rel));
429 
430  /*
431  * If this is the first inserted/updated tuple, let the caller
432  * know which page it landed on.
433  */
434  if (newblkno && ItemPointerEquals(&thistup->t_tid, &(*itup)->t_tid))
435  *newblkno = ptr->block.blkno;
436 
437  data += IndexTupleSize(thistup);
438  }
439 
440  /* Set up rightlinks */
441  if (ptr->next && ptr->block.blkno != GIST_ROOT_BLKNO)
442  GistPageGetOpaque(ptr->page)->rightlink =
443  ptr->next->block.blkno;
444  else
445  GistPageGetOpaque(ptr->page)->rightlink = oldrlink;
446 
447  /*
448  * Mark the all but the right-most page with the follow-right
449  * flag. It will be cleared as soon as the downlink is inserted
450  * into the parent, but this ensures that if we error out before
451  * that, the index is still consistent. (in buffering build mode,
452  * any error will abort the index build anyway, so this is not
453  * needed.)
454  */
455  if (ptr->next && !is_rootsplit && markfollowright)
456  GistMarkFollowRight(ptr->page);
457  else
458  GistClearFollowRight(ptr->page);
459 
460  /*
461  * Copy the NSN of the original page to all pages. The
462  * F_FOLLOW_RIGHT flags ensure that scans will follow the
463  * rightlinks until the downlinks are inserted.
464  */
465  GistPageSetNSN(ptr->page, oldnsn);
466  }
467 
468  /*
469  * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
470  * insertion for that. NB: The number of pages and data segments
471  * specified here must match the calculations in gistXLogSplit()!
472  */
473  if (!is_build && RelationNeedsWAL(rel))
474  XLogEnsureRecordSpace(npage, 1 + npage * 2);
475 
477 
478  /*
479  * Must mark buffers dirty before XLogInsert, even though we'll still
480  * be changing their opaque fields below.
481  */
482  for (ptr = dist; ptr; ptr = ptr->next)
483  MarkBufferDirty(ptr->buffer);
484  if (BufferIsValid(leftchildbuf))
485  MarkBufferDirty(leftchildbuf);
486 
487  /*
488  * The first page in the chain was a temporary working copy meant to
489  * replace the old page. Copy it over the old page.
490  */
492  dist->page = BufferGetPage(dist->buffer);
493 
494  /*
495  * Write the WAL record.
496  *
497  * If we're building a new index, however, we don't WAL-log changes
498  * yet. The LSN-NSN interlock between parent and child requires that
499  * LSNs never move backwards, so set the LSNs to a value that's
500  * smaller than any real or fake unlogged LSN that might be generated
501  * later. (There can't be any concurrent scans during index build, so
502  * we don't need to be able to detect concurrent splits yet.)
503  */
504  if (is_build)
505  recptr = GistBuildLSN;
506  else
507  {
508  if (RelationNeedsWAL(rel))
509  recptr = gistXLogSplit(is_leaf,
510  dist, oldrlink, oldnsn, leftchildbuf,
511  markfollowright);
512  else
513  recptr = gistGetFakeLSN(rel);
514  }
515 
516  for (ptr = dist; ptr; ptr = ptr->next)
517  PageSetLSN(ptr->page, recptr);
518 
519  /*
520  * Return the new child buffers to the caller.
521  *
522  * If this was a root split, we've already inserted the downlink
523  * pointers, in the form of a new root page. Therefore we can release
524  * all the new buffers, and keep just the root page locked.
525  */
526  if (is_rootsplit)
527  {
528  for (ptr = dist->next; ptr; ptr = ptr->next)
529  UnlockReleaseBuffer(ptr->buffer);
530  }
531  }
532  else
533  {
534  /*
535  * Enough space. We always get here if ntup==0.
536  */
538 
539  /*
540  * Delete old tuple if any, then insert new tuple(s) if any. If
541  * possible, use the fast path of PageIndexTupleOverwrite.
542  */
543  if (OffsetNumberIsValid(oldoffnum))
544  {
545  if (ntup == 1)
546  {
547  /* One-for-one replacement, so use PageIndexTupleOverwrite */
548  if (!PageIndexTupleOverwrite(page, oldoffnum, (Item) *itup,
549  IndexTupleSize(*itup)))
550  elog(ERROR, "failed to add item to index page in \"%s\"",
552  }
553  else
554  {
555  /* Delete old, then append new tuple(s) to page */
556  PageIndexTupleDelete(page, oldoffnum);
557  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
558  }
559  }
560  else
561  {
562  /* Just append new tuples at the end of the page */
563  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
564  }
565 
566  MarkBufferDirty(buffer);
567 
568  if (BufferIsValid(leftchildbuf))
569  MarkBufferDirty(leftchildbuf);
570 
571  if (is_build)
572  recptr = GistBuildLSN;
573  else
574  {
575  if (RelationNeedsWAL(rel))
576  {
577  OffsetNumber ndeloffs = 0,
578  deloffs[1];
579 
580  if (OffsetNumberIsValid(oldoffnum))
581  {
582  deloffs[0] = oldoffnum;
583  ndeloffs = 1;
584  }
585 
586  recptr = gistXLogUpdate(buffer,
587  deloffs, ndeloffs, itup, ntup,
588  leftchildbuf);
589  }
590  else
591  recptr = gistGetFakeLSN(rel);
592  }
593  PageSetLSN(page, recptr);
594 
595  if (newblkno)
596  *newblkno = blkno;
597  }
598 
599  /*
600  * If we inserted the downlink for a child page, set NSN and clear
601  * F_FOLLOW_RIGHT flag on the left child, so that concurrent scans know to
602  * follow the rightlink if and only if they looked at the parent page
603  * before we inserted the downlink.
604  *
605  * Note that we do this *after* writing the WAL record. That means that
606  * the possible full page image in the WAL record does not include these
607  * changes, and they must be replayed even if the page is restored from
608  * the full page image. There's a chicken-and-egg problem: if we updated
609  * the child pages first, we wouldn't know the recptr of the WAL record
610  * we're about to write.
611  */
612  if (BufferIsValid(leftchildbuf))
613  {
614  Page leftpg = BufferGetPage(leftchildbuf);
615 
616  GistPageSetNSN(leftpg, recptr);
617  GistClearFollowRight(leftpg);
618 
619  PageSetLSN(leftpg, recptr);
620  }
621 
623 
624  return is_split;
625 }
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:424
Page PageGetTempPageCopySpecial(Page page)
Definition: bufpage.c:402
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1405
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1052
#define PageSetLSN(page, lsn)
Definition: bufpage.h:367
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:415
static void gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
Definition: gist.c:1643
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1417
#define GistMarkFollowRight(page)
Definition: gist.h:181
#define GistClearFollowRight(page)
Definition: gist.h:182
#define GistPageSetNSN(page, val)
Definition: gist.h:185
#define GistPageHasGarbage(page)
Definition: gist.h:176
XLogRecPtr GistNSN
Definition: gist.h:60
#define GistBuildLSN
Definition: gist.h:67
#define GIST_MAX_SPLIT_PAGES
Definition: gist_private.h:39
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:824
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:59
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:34
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:95
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:1025
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:114
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:127
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:632
XLogRecPtr gistXLogSplit(bool page_is_leaf, SplitedPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:500
Pointer Item
Definition: item.h:17
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define IndexTupleSize(itup)
Definition: itup.h:70
const void * data
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3169
gistxlogPage block
Definition: gist_private.h:193
IndexTupleData * list
Definition: gist_private.h:194
struct SplitedPageLayout * next
Definition: gist_private.h:200
BlockNumber blkno
Definition: gist_private.h:186
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:176

References Assert(), gistxlogPage::blkno, SplitedPageLayout::block, GISTPageSplitInfo::buf, SplitedPageLayout::buffer, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, data, GISTPageSplitInfo::downlink, elog, END_CRIT_SECTION, ERROR, F_LEAF, FirstOffsetNumber, GIST_MAX_SPLIT_PAGES, GIST_ROOT_BLKNO, GistBuildLSN, GistClearFollowRight, gistextractpage(), gistfillbuffer(), gistfillitupvec(), GistFollowRight, gistGetFakeLSN(), GISTInitBuffer(), gistjoinvector(), GistMarkFollowRight, gistNewBuffer(), gistnospace(), GistPageGetNSN, GistPageGetOpaque, GistPageHasGarbage, GistPageIsDeleted, GistPageIsLeaf, GistPageSetNSN, gistprunepage(), gistSplit(), GistTupleSetValid, gistXLogSplit(), gistXLogUpdate(), i, IndexTupleSize, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerEquals(), ItemPointerSetBlockNumber, SplitedPageLayout::itup, lappend(), SplitedPageLayout::lenlist, SplitedPageLayout::list, MarkBufferDirty(), SplitedPageLayout::next, NIL, gistxlogPage::num, OffsetNumberIsValid, SplitedPageLayout::page, PageAddItem, PageGetTempPageCopySpecial(), PageIndexTupleDelete(), PageIndexTupleOverwrite(), PageRestoreTempPage(), PageSetLSN, palloc(), PredicateLockPageSplit(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogEnsureRecordSpace().

Referenced by gistbufferinginserttuples(), and gistinserttuples().

◆ gistprunepage()

static void gistprunepage ( Relation  rel,
Page  page,
Buffer  buffer,
Relation  heapRel 
)
static

Definition at line 1643 of file gist.c.

1644 {
1646  int ndeletable = 0;
1647  OffsetNumber offnum,
1648  maxoff;
1649 
1650  Assert(GistPageIsLeaf(page));
1651 
1652  /*
1653  * Scan over all items to see which ones need to be deleted according to
1654  * LP_DEAD flags.
1655  */
1656  maxoff = PageGetMaxOffsetNumber(page);
1657  for (offnum = FirstOffsetNumber;
1658  offnum <= maxoff;
1659  offnum = OffsetNumberNext(offnum))
1660  {
1661  ItemId itemId = PageGetItemId(page, offnum);
1662 
1663  if (ItemIdIsDead(itemId))
1664  deletable[ndeletable++] = offnum;
1665  }
1666 
1667  if (ndeletable > 0)
1668  {
1669  TransactionId latestRemovedXid = InvalidTransactionId;
1670 
1672  latestRemovedXid =
1673  index_compute_xid_horizon_for_tuples(rel, heapRel, buffer,
1674  deletable, ndeletable);
1675 
1677 
1678  PageIndexMultiDelete(page, deletable, ndeletable);
1679 
1680  /*
1681  * Mark the page as not containing any LP_DEAD items. This is not
1682  * certainly true (there might be some that have recently been marked,
1683  * but weren't included in our target-item list), but it will almost
1684  * always be true and it doesn't seem worth an additional page scan to
1685  * check it. Remember that F_HAS_GARBAGE is only a hint anyway.
1686  */
1688 
1689  MarkBufferDirty(buffer);
1690 
1691  /* XLOG stuff */
1692  if (RelationNeedsWAL(rel))
1693  {
1694  XLogRecPtr recptr;
1695 
1696  recptr = gistXLogDelete(buffer,
1697  deletable, ndeletable,
1698  latestRemovedXid);
1699 
1700  PageSetLSN(page, recptr);
1701  }
1702  else
1703  PageSetLSN(page, gistGetFakeLSN(rel));
1704 
1705  END_CRIT_SECTION();
1706  }
1707 
1708  /*
1709  * Note: if we didn't find any LP_DEAD items, then the page's
1710  * F_HAS_GARBAGE hint bit is falsely set. We do not bother expending a
1711  * separate write to clear it, however. We will clear it when we split
1712  * the page.
1713  */
1714 }
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1161
uint32 TransactionId
Definition: c.h:587
TransactionId index_compute_xid_horizon_for_tuples(Relation irel, Relation hrel, Buffer ibuf, OffsetNumber *itemnos, int nitems)
Definition: genam.c:293
#define GistClearPageHasGarbage(page)
Definition: gist.h:178
XLogRecPtr gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId latestRemovedXid)
Definition: gistxlog.c:673
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define MaxIndexTuplesPerPage
Definition: itup.h:144
#define InvalidTransactionId
Definition: transam.h:31
#define XLogStandbyInfoActive()
Definition: xlog.h:118

References Assert(), END_CRIT_SECTION, FirstOffsetNumber, GistClearPageHasGarbage, gistGetFakeLSN(), GistPageIsLeaf, gistXLogDelete(), index_compute_xid_horizon_for_tuples(), InvalidTransactionId, ItemIdIsDead, MarkBufferDirty(), MaxIndexTuplesPerPage, OffsetNumberNext, PageGetItemId, PageGetMaxOffsetNumber, PageIndexMultiDelete(), PageSetLSN, RelationNeedsWAL, START_CRIT_SECTION, and XLogStandbyInfoActive.

Referenced by gistplacetopage().

◆ gistSplit()

SplitedPageLayout* gistSplit ( Relation  r,
Page  page,
IndexTuple itup,
int  len,
GISTSTATE giststate 
)

Definition at line 1417 of file gist.c.

1422 {
1423  IndexTuple *lvectup,
1424  *rvectup;
1425  GistSplitVector v;
1426  int i;
1427  SplitedPageLayout *res = NULL;
1428 
1429  /* this should never recurse very deeply, but better safe than sorry */
1431 
1432  /* there's no point in splitting an empty page */
1433  Assert(len > 0);
1434 
1435  /*
1436  * If a single tuple doesn't fit on a page, no amount of splitting will
1437  * help.
1438  */
1439  if (len == 1)
1440  ereport(ERROR,
1441  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1442  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1443  IndexTupleSize(itup[0]), GiSTPageSize,
1445 
1446  memset(v.spl_lisnull, true,
1447  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1448  memset(v.spl_risnull, true,
1449  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1450  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1451 
1452  /* form left and right vector */
1453  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1454  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1455 
1456  for (i = 0; i < v.splitVector.spl_nleft; i++)
1457  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1458 
1459  for (i = 0; i < v.splitVector.spl_nright; i++)
1460  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1461 
1462  /* finalize splitting (may need another split) */
1463  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1464  {
1465  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1466  }
1467  else
1468  {
1469  ROTATEDIST(res);
1470  res->block.num = v.splitVector.spl_nright;
1471  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1472  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1473  }
1474 
1475  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1476  {
1477  SplitedPageLayout *resptr,
1478  *subres;
1479 
1480  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1481 
1482  /* install on list's tail */
1483  while (resptr->next)
1484  resptr = resptr->next;
1485 
1486  resptr->next = res;
1487  res = subres;
1488  }
1489  else
1490  {
1491  ROTATEDIST(res);
1492  res->block.num = v.splitVector.spl_nleft;
1493  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1494  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1495  }
1496 
1497  return res;
1498 }
int errcode(int sqlerrcode)
Definition: elog.c:693
#define ROTATEDIST(d)
Definition: gist.c:46
#define GiSTPageSize
Definition: gist_private.h:475
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:79
const void size_t len
void check_stack_depth(void)
Definition: postgres.c:3500
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
int spl_nleft
Definition: gist.h:141
OffsetNumber * spl_right
Definition: gist.h:145
int spl_nright
Definition: gist.h:146
OffsetNumber * spl_left
Definition: gist.h:140
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245

References Assert(), check_stack_depth(), ereport, errcode(), errmsg(), ERROR, gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplitByKey(), i, if(), IndexTupleSize, len, TupleDescData::natts, SplitedPageLayout::next, GISTSTATE::nonLeafTupdesc, palloc(), RelationGetRelationName, res, ROTATEDIST, GistSplitVector::spl_lattr, GIST_SPLITVEC::spl_left, GistSplitVector::spl_lisnull, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GistSplitVector::spl_rattr, GIST_SPLITVEC::spl_right, GistSplitVector::spl_risnull, and GistSplitVector::splitVector.

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ initGISTstate()

GISTSTATE* initGISTstate ( Relation  index)

Definition at line 1504 of file gist.c.

1505 {
1506  GISTSTATE *giststate;
1507  MemoryContext scanCxt;
1508  MemoryContext oldCxt;
1509  int i;
1510 
1511  /* safety check to protect fixed-size arrays in GISTSTATE */
1512  if (index->rd_att->natts > INDEX_MAX_KEYS)
1513  elog(ERROR, "numberOfAttributes %d > %d",
1514  index->rd_att->natts, INDEX_MAX_KEYS);
1515 
1516  /* Create the memory context that will hold the GISTSTATE */
1518  "GiST scan context",
1520  oldCxt = MemoryContextSwitchTo(scanCxt);
1521 
1522  /* Create and fill in the GISTSTATE */
1523  giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1524 
1525  giststate->scanCxt = scanCxt;
1526  giststate->tempCxt = scanCxt; /* caller must change this if needed */
1527  giststate->leafTupdesc = index->rd_att;
1528 
1529  /*
1530  * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1531  * the INCLUDE attributes.
1532  *
1533  * It is used to form tuples during tuple adjustment and page split.
1534  * B-tree creates shortened tuple descriptor for every truncated tuple,
1535  * because it is doing this less often: it does not have to form truncated
1536  * tuples during page split. Also, B-tree is not adjusting tuples on
1537  * internal pages the way GiST does.
1538  */
1539  giststate->nonLeafTupdesc = CreateTupleDescCopyConstr(index->rd_att);
1540  giststate->nonLeafTupdesc->natts =
1542 
1544  {
1545  fmgr_info_copy(&(giststate->consistentFn[i]),
1547  scanCxt);
1548  fmgr_info_copy(&(giststate->unionFn[i]),
1550  scanCxt);
1551 
1552  /* opclasses are not required to provide a Compress method */
1554  fmgr_info_copy(&(giststate->compressFn[i]),
1556  scanCxt);
1557  else
1558  giststate->compressFn[i].fn_oid = InvalidOid;
1559 
1560  /* opclasses are not required to provide a Decompress method */
1562  fmgr_info_copy(&(giststate->decompressFn[i]),
1564  scanCxt);
1565  else
1566  giststate->decompressFn[i].fn_oid = InvalidOid;
1567 
1568  fmgr_info_copy(&(giststate->penaltyFn[i]),
1570  scanCxt);
1571  fmgr_info_copy(&(giststate->picksplitFn[i]),
1573  scanCxt);
1574  fmgr_info_copy(&(giststate->equalFn[i]),
1576  scanCxt);
1577 
1578  /* opclasses are not required to provide a Distance method */
1580  fmgr_info_copy(&(giststate->distanceFn[i]),
1582  scanCxt);
1583  else
1584  giststate->distanceFn[i].fn_oid = InvalidOid;
1585 
1586  /* opclasses are not required to provide a Fetch method */
1588  fmgr_info_copy(&(giststate->fetchFn[i]),
1590  scanCxt);
1591  else
1592  giststate->fetchFn[i].fn_oid = InvalidOid;
1593 
1594  /*
1595  * If the index column has a specified collation, we should honor that
1596  * while doing comparisons. However, we may have a collatable storage
1597  * type for a noncollatable indexed data type. If there's no index
1598  * collation then specify default collation in case the support
1599  * functions need collation. This is harmless if the support
1600  * functions don't care about collation, so we just do it
1601  * unconditionally. (We could alternatively call get_typcollation,
1602  * but that seems like expensive overkill --- there aren't going to be
1603  * any cases where a GiST storage type has a nondefault collation.)
1604  */
1605  if (OidIsValid(index->rd_indcollation[i]))
1606  giststate->supportCollation[i] = index->rd_indcollation[i];
1607  else
1608  giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1609  }
1610 
1611  /* No opclass information for INCLUDE attributes */
1612  for (; i < index->rd_att->natts; i++)
1613  {
1614  giststate->consistentFn[i].fn_oid = InvalidOid;
1615  giststate->unionFn[i].fn_oid = InvalidOid;
1616  giststate->compressFn[i].fn_oid = InvalidOid;
1617  giststate->decompressFn[i].fn_oid = InvalidOid;
1618  giststate->penaltyFn[i].fn_oid = InvalidOid;
1619  giststate->picksplitFn[i].fn_oid = InvalidOid;
1620  giststate->equalFn[i].fn_oid = InvalidOid;
1621  giststate->distanceFn[i].fn_oid = InvalidOid;
1622  giststate->fetchFn[i].fn_oid = InvalidOid;
1623  giststate->supportCollation[i] = InvalidOid;
1624  }
1625 
1626  MemoryContextSwitchTo(oldCxt);
1627 
1628  return giststate;
1629 }
#define OidIsValid(objectId)
Definition: c.h:710
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:594
#define GIST_DECOMPRESS_PROC
Definition: gist.h:33
#define GIST_PICKSPLIT_PROC
Definition: gist.h:35
#define GIST_CONSISTENT_PROC
Definition: gist.h:30
#define GIST_UNION_PROC
Definition: gist.h:31
#define GIST_FETCH_PROC
Definition: gist.h:38
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define GIST_PENALTY_PROC
Definition: gist.h:34
#define GIST_DISTANCE_PROC
Definition: gist.h:37
#define GIST_EQUAL_PROC
Definition: gist.h:36
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:803
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:769
#define INDEX_MAX_KEYS
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:507
Oid fn_oid
Definition: fmgr.h:59
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:94
TupleDesc leafTupdesc
Definition: gist_private.h:80
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
FmgrInfo distanceFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
FmgrInfo consistentFn[INDEX_MAX_KEYS]
Definition: gist_private.h:86
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
FmgrInfo picksplitFn[INDEX_MAX_KEYS]
Definition: gist_private.h:91
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:151

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, GISTSTATE::compressFn, GISTSTATE::consistentFn, CreateTupleDescCopyConstr(), CurrentMemoryContext, GISTSTATE::decompressFn, GISTSTATE::distanceFn, elog, GISTSTATE::equalFn, ERROR, GISTSTATE::fetchFn, fmgr_info_copy(), FmgrInfo::fn_oid, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_UNION_PROC, i, index_getprocid(), index_getprocinfo(), INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidOid, GISTSTATE::leafTupdesc, MemoryContextSwitchTo(), TupleDescData::natts, GISTSTATE::nonLeafTupdesc, OidIsValid, palloc(), GISTSTATE::penaltyFn, GISTSTATE::picksplitFn, GISTSTATE::scanCxt, GISTSTATE::supportCollation, GISTSTATE::tempCxt, and GISTSTATE::unionFn.

Referenced by gistbeginscan(), gistbuild(), and gistinsert().