PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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/predicate.h"
#include "utils/fmgrprotos.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, bool is_build)
 
static IndexTuple gistformdownlink (Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack, bool is_build)
 
SplitPageLayoutgistSplit (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 { \
SplitPageLayout *tmp = (SplitPageLayout *) palloc0(sizeof(SplitPageLayout)); \
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:1347

Definition at line 45 of file gist.c.

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 128 of file gist.c.

129{
131 "GiST temporary context",
133}
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1657 of file gist.c.

1658{
1659 /* It's sufficient to delete the scanCxt */
1660 MemoryContextDelete(giststate->scanCxt);
1661}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
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 139 of file gist.c.

140{
141 Buffer buffer;
142
143 /* Initialize the root page */
146
147 /* Initialize and xlog buffer */
149 GISTInitBuffer(buffer, F_LEAF);
150 MarkBufferDirty(buffer);
151 log_newpage_buffer(buffer, true);
153
154 /* Unlock and release the buffer */
155 UnlockReleaseBuffer(buffer);
156}
int Buffer
Definition: buf.h:23
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:846
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4880
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2530
@ EB_SKIP_EXTENSION_LOCK
Definition: bufmgr.h:74
@ EB_LOCK_FIRST
Definition: bufmgr.h:86
#define BMR_REL(p_rel)
Definition: bufmgr.h:107
#define F_LEAF
Definition: gist.h:49
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
@ INIT_FORKNUM
Definition: relpath.h:61
Definition: type.h:96
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1237

References BMR_REL, EB_LOCK_FIRST, EB_SKIP_EXTENSION_LOCK, END_CRIT_SECTION, ExtendBufferedRel(), F_LEAF, GISTInitBuffer(), INIT_FORKNUM, log_newpage_buffer(), MarkBufferDirty(), 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 639 of file gist.c.

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

Definition at line 1027 of file gist.c.

1028{
1029 GISTInsertStack *parent = child->parent;
1030 ItemId iid;
1031 IndexTuple idxtuple;
1032 OffsetNumber maxoff;
1033 GISTInsertStack *ptr;
1034
1035 gistcheckpage(r, parent->buffer);
1036 parent->page = (Page) BufferGetPage(parent->buffer);
1037 maxoff = PageGetMaxOffsetNumber(parent->page);
1038
1039 /* Check if the downlink is still where it was before */
1040 if (child->downlinkoffnum != InvalidOffsetNumber && child->downlinkoffnum <= maxoff)
1041 {
1042 iid = PageGetItemId(parent->page, child->downlinkoffnum);
1043 idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
1044 if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
1045 return; /* still there */
1046 }
1047
1048 /*
1049 * The page has changed since we looked. During normal operation, every
1050 * update of a page changes its LSN, so the LSN we memorized should have
1051 * changed too. During index build, however, we don't WAL-log the changes
1052 * until we have built the index, so the LSN doesn't change. There is no
1053 * concurrent activity during index build, but we might have changed the
1054 * parent ourselves.
1055 */
1056 Assert(parent->lsn != PageGetLSN(parent->page) || is_build);
1057
1058 /*
1059 * Scan the page to re-find the downlink. If the page was split, it might
1060 * have moved to a different page, so follow the right links until we find
1061 * it.
1062 */
1063 while (true)
1064 {
1066
1067 maxoff = PageGetMaxOffsetNumber(parent->page);
1068 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1069 {
1070 iid = PageGetItemId(parent->page, i);
1071 idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
1072 if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
1073 {
1074 /* yes!!, found */
1075 child->downlinkoffnum = i;
1076 return;
1077 }
1078 }
1079
1080 parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
1082 UnlockReleaseBuffer(parent->buffer);
1083 if (parent->blkno == InvalidBlockNumber)
1084 {
1085 /*
1086 * End of chain and still didn't find parent. It's a very-very
1087 * rare situation when the root was split.
1088 */
1089 break;
1090 }
1091 parent->buffer = ReadBuffer(r, parent->blkno);
1093 gistcheckpage(r, parent->buffer);
1094 parent->page = (Page) BufferGetPage(parent->buffer);
1095 }
1096
1097 /*
1098 * awful!!, we need search tree to find parent ... , but before we should
1099 * release all old parent
1100 */
1101
1102 ptr = child->parent->parent; /* child->parent already released above */
1103 while (ptr)
1104 {
1105 ReleaseBuffer(ptr->buffer);
1106 ptr = ptr->parent;
1107 }
1108
1109 /* ok, find new path */
1110 ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
1111
1112 /* read all buffers as expected by caller */
1113 /* note we don't lock them or gistcheckpage them here! */
1114 while (ptr)
1115 {
1116 ptr->buffer = ReadBuffer(r, ptr->blkno);
1117 ptr->page = (Page) BufferGetPage(ptr->buffer);
1118 ptr = ptr->parent;
1119 }
1120
1121 /* install new chain of parents to stack */
1122 child->parent = parent;
1123
1124 /* make recursive call to normal processing */
1126 gistFindCorrectParent(r, child, is_build);
1127}
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
static GISTInsertStack * gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
Definition: gist.c:914
static void gistFindCorrectParent(Relation r, GISTInsertStack *child, bool is_build)
Definition: gist.c:1027
#define GistPageGetOpaque(page)
Definition: gist.h:168
int i
Definition: isn.c:72
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define FirstOffsetNumber
Definition: off.h:27

References Assert(), GISTInsertStack::blkno, GISTInsertStack::buffer, BufferGetPage(), GISTInsertStack::downlinkoffnum, FirstOffsetNumber, GIST_EXCLUSIVE, gistcheckpage(), gistFindCorrectParent(), 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 gistFindCorrectParent(), gistfinishsplit(), and gistformdownlink().

◆ gistFindPath()

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

Definition at line 914 of file gist.c.

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

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 1347 of file gist.c.

1349{
1350 GISTPageSplitInfo *right;
1351 GISTPageSplitInfo *left;
1352 IndexTuple tuples[2];
1353
1354 /* A split always contains at least two halves */
1355 Assert(list_length(splitinfo) >= 2);
1356
1357 /*
1358 * We need to insert downlinks for each new page, and update the downlink
1359 * for the original (leftmost) page in the split. Begin at the rightmost
1360 * page, inserting one downlink at a time until there's only two pages
1361 * left. Finally insert the downlink for the last new page and update the
1362 * downlink for the original page as one operation.
1363 */
1365
1366 /*
1367 * Insert downlinks for the siblings from right to left, until there are
1368 * only two siblings left.
1369 */
1370 for (int pos = list_length(splitinfo) - 1; pos > 1; pos--)
1371 {
1372 right = (GISTPageSplitInfo *) list_nth(splitinfo, pos);
1373 left = (GISTPageSplitInfo *) list_nth(splitinfo, pos - 1);
1374
1375 gistFindCorrectParent(state->r, stack, state->is_build);
1376 if (gistinserttuples(state, stack->parent, giststate,
1377 &right->downlink, 1,
1379 left->buf, right->buf, false, false))
1380 {
1381 /*
1382 * If the parent page was split, the existing downlink might have
1383 * moved.
1384 */
1386 }
1387 /* gistinserttuples() released the lock on right->buf. */
1388 }
1389
1390 right = (GISTPageSplitInfo *) lsecond(splitinfo);
1391 left = (GISTPageSplitInfo *) linitial(splitinfo);
1392
1393 /*
1394 * Finally insert downlink for the remaining right page and update the
1395 * downlink for the original page to not contain the tuples that were
1396 * moved to the new pages.
1397 */
1398 tuples[0] = left->downlink;
1399 tuples[1] = right->downlink;
1400 gistFindCorrectParent(state->r, stack, state->is_build);
1401 (void) gistinserttuples(state, stack->parent, giststate,
1402 tuples, 2,
1403 stack->downlinkoffnum,
1404 left->buf, right->buf,
1405 true, /* Unlock parent */
1406 unlockbuf /* Unlock stack->buffer if caller
1407 * wants that */
1408 );
1409
1410 /*
1411 * The downlink might have moved when we updated it. Even if the page
1412 * wasn't split, because gistinserttuples() implements updating the old
1413 * tuple by removing and re-inserting it!
1414 */
1416
1417 Assert(left->buf == stack->buffer);
1418
1419 /*
1420 * If we split the page because we had to adjust the downlink on an
1421 * internal page, while descending the tree for inserting a new tuple,
1422 * then this might no longer be the correct page for the new tuple. The
1423 * downlink to this page might not cover the new tuple anymore, it might
1424 * need to go to the newly-created right sibling instead. Tell the caller
1425 * to walk back up the stack, to re-check at the parent which page to
1426 * insert to.
1427 *
1428 * Normally, the LSN-NSN interlock during the tree descend would also
1429 * detect that a concurrent split happened (by ourselves), and cause us to
1430 * retry at the parent. But that mechanism doesn't work during index
1431 * build, because we don't do WAL-logging, and don't update LSNs, during
1432 * index build.
1433 */
1434 stack->retry_from_parent = true;
1435}
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:1287
static int list_length(const List *l)
Definition: pg_list.h:152
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define lsecond(l)
Definition: pg_list.h:183
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 1193 of file gist.c.

1194{
1195 GISTInsertStack *stack = state->stack;
1196 Buffer buf;
1197 Page page;
1198 List *splitinfo = NIL;
1199
1200 ereport(LOG,
1201 (errmsg("fixing incomplete split in index \"%s\", block %u",
1202 RelationGetRelationName(state->r), stack->blkno)));
1203
1204 Assert(GistFollowRight(stack->page));
1206
1207 buf = stack->buffer;
1208
1209 /*
1210 * Read the chain of split pages, following the rightlinks. Construct a
1211 * downlink tuple for each page.
1212 */
1213 for (;;)
1214 {
1216 IndexTuple downlink;
1217
1218 page = BufferGetPage(buf);
1219
1220 /* Form the new downlink tuples to insert to parent */
1221 downlink = gistformdownlink(state->r, buf, giststate, stack, state->is_build);
1222
1223 si->buf = buf;
1224 si->downlink = downlink;
1225
1226 splitinfo = lappend(splitinfo, si);
1227
1228 if (GistFollowRight(page))
1229 {
1230 /* lock next page */
1231 buf = ReadBuffer(state->r, GistPageGetOpaque(page)->rightlink);
1233 }
1234 else
1235 break;
1236 }
1237
1238 /* Insert the downlinks */
1239 gistfinishsplit(state, stack, giststate, splitinfo, false);
1240}
#define LOG
Definition: elog.h:31
static void gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
Definition: gist.c:1347
static IndexTuple gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack, bool is_build)
Definition: gist.c:1133
void * palloc(Size size)
Definition: mcxt.c:1317
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
static char * buf
Definition: pg_test_fsync.c:72

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,
bool  is_build 
)
static

Definition at line 1133 of file gist.c.

1135{
1136 Page page = BufferGetPage(buf);
1137 OffsetNumber maxoff;
1138 OffsetNumber offset;
1139 IndexTuple downlink = NULL;
1140
1141 maxoff = PageGetMaxOffsetNumber(page);
1142 for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
1143 {
1144 IndexTuple ituple = (IndexTuple)
1145 PageGetItem(page, PageGetItemId(page, offset));
1146
1147 if (downlink == NULL)
1148 downlink = CopyIndexTuple(ituple);
1149 else
1150 {
1151 IndexTuple newdownlink;
1152
1153 newdownlink = gistgetadjusted(rel, downlink, ituple,
1154 giststate);
1155 if (newdownlink)
1156 downlink = newdownlink;
1157 }
1158 }
1159
1160 /*
1161 * If the page is completely empty, we can't form a meaningful downlink
1162 * for it. But we have to insert a downlink for the page. Any key will do,
1163 * as long as its consistent with the downlink of parent page, so that we
1164 * can legally insert it to the parent. A minimal one that matches as few
1165 * scans as possible would be best, to keep scans from doing useless work,
1166 * but we don't know how to construct that. So we just use the downlink of
1167 * the original page that was split - that's as far from optimal as it can
1168 * get but will do..
1169 */
1170 if (!downlink)
1171 {
1172 ItemId iid;
1173
1175 gistFindCorrectParent(rel, stack, is_build);
1176 iid = PageGetItemId(stack->parent->page, stack->downlinkoffnum);
1177 downlink = (IndexTuple) PageGetItem(stack->parent->page, iid);
1178 downlink = CopyIndexTuple(downlink);
1180 }
1181
1183 GistTupleSetValid(downlink);
1184
1185 return downlink;
1186}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3720
#define GistTupleSetValid(itup)
Definition: gist_private.h:289
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:547
static void ItemPointerSetBlockNumber(ItemPointerData *pointer, BlockNumber blockNumber)
Definition: itemptr.h:147

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 59 of file gist.c.

60{
62
63 amroutine->amstrategies = 0;
64 amroutine->amsupport = GISTNProcs;
66 amroutine->amcanorder = false;
67 amroutine->amcanorderbyop = true;
68 amroutine->amcanhash = false;
69 amroutine->amconsistentequality = false;
70 amroutine->amconsistentordering = false;
71 amroutine->amcanbackward = false;
72 amroutine->amcanunique = false;
73 amroutine->amcanmulticol = true;
74 amroutine->amoptionalkey = true;
75 amroutine->amsearcharray = false;
76 amroutine->amsearchnulls = true;
77 amroutine->amstorage = true;
78 amroutine->amclusterable = true;
79 amroutine->ampredlocks = true;
80 amroutine->amcanparallel = false;
81 amroutine->amcanbuildparallel = false;
82 amroutine->amcaninclude = true;
83 amroutine->amusemaintenanceworkmem = false;
84 amroutine->amsummarizing = false;
85 amroutine->amparallelvacuumoptions =
87 amroutine->amkeytype = InvalidOid;
88
89 amroutine->ambuild = gistbuild;
90 amroutine->ambuildempty = gistbuildempty;
91 amroutine->aminsert = gistinsert;
92 amroutine->aminsertcleanup = NULL;
93 amroutine->ambulkdelete = gistbulkdelete;
95 amroutine->amcanreturn = gistcanreturn;
97 amroutine->amgettreeheight = NULL;
98 amroutine->amoptions = gistoptions;
99 amroutine->amproperty = gistproperty;
100 amroutine->ambuildphasename = NULL;
101 amroutine->amvalidate = gistvalidate;
103 amroutine->ambeginscan = gistbeginscan;
104 amroutine->amrescan = gistrescan;
105 amroutine->amgettuple = gistgettuple;
106 amroutine->amgetbitmap = gistgetbitmap;
107 amroutine->amendscan = gistendscan;
108 amroutine->ammarkpos = NULL;
109 amroutine->amrestrpos = NULL;
110 amroutine->amestimateparallelscan = NULL;
111 amroutine->aminitparallelscan = NULL;
112 amroutine->amparallelrescan = NULL;
113 amroutine->amtranslatestrategy = NULL;
115
116 PG_RETURN_POINTER(amroutine);
117}
#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:165
void gistbuildempty(Relation index)
Definition: gist.c:139
#define GISTNProcs
Definition: gist.h:44
#define GIST_OPTIONS_PROC
Definition: gist.h:41
IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: gistbuild.c:179
bool gistgettuple(IndexScanDesc scan, ScanDirection dir)
Definition: gistget.c:612
int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: gistget.c:745
bool gistcanreturn(Relation index, int attno)
Definition: gistget.c:797
IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys)
Definition: gistscan.c:74
void gistendscan(IndexScanDesc scan)
Definition: gistscan.c:347
void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)
Definition: gistscan.c:127
bytea * gistoptions(Datum reloptions, bool validate)
Definition: gistutil.c:912
bool gistproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: gistutil.c:933
StrategyNumber gisttranslatecmptype(CompareType cmptype, Oid opfamily)
Definition: gistutil.c:1098
IndexBulkDeleteResult * gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: gistvacuum.c:75
IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:59
void gistadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: gistvalidate.c:288
bool gistvalidate(Oid opclassoid)
Definition: gistvalidate.c:32
#define makeNode(_type_)
Definition: nodes.h:155
#define InvalidOid
Definition: postgres_ext.h:37
void gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7391
ambuildphasename_function ambuildphasename
Definition: amapi.h:303
ambuildempty_function ambuildempty
Definition: amapi.h:293
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:297
bool amclusterable
Definition: amapi.h:267
amoptions_function amoptions
Definition: amapi.h:301
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:315
amrestrpos_function amrestrpos
Definition: amapi.h:312
aminsert_function aminsert
Definition: amapi.h:294
amendscan_function amendscan
Definition: amapi.h:310
amtranslate_strategy_function amtranslatestrategy
Definition: amapi.h:320
uint16 amoptsprocnum
Definition: amapi.h:241
amparallelrescan_function amparallelrescan
Definition: amapi.h:317
Oid amkeytype
Definition: amapi.h:283
bool amconsistentordering
Definition: amapi.h:251
bool ampredlocks
Definition: amapi.h:269
uint16 amsupport
Definition: amapi.h:239
amtranslate_cmptype_function amtranslatecmptype
Definition: amapi.h:321
amcostestimate_function amcostestimate
Definition: amapi.h:299
bool amcanorderbyop
Definition: amapi.h:245
amadjustmembers_function amadjustmembers
Definition: amapi.h:305
ambuild_function ambuild
Definition: amapi.h:292
bool amstorage
Definition: amapi.h:265
uint16 amstrategies
Definition: amapi.h:237
bool amoptionalkey
Definition: amapi.h:259
amgettuple_function amgettuple
Definition: amapi.h:308
amcanreturn_function amcanreturn
Definition: amapi.h:298
bool amcanunique
Definition: amapi.h:255
amgetbitmap_function amgetbitmap
Definition: amapi.h:309
amproperty_function amproperty
Definition: amapi.h:302
ambulkdelete_function ambulkdelete
Definition: amapi.h:296
bool amsearcharray
Definition: amapi.h:261
bool amsummarizing
Definition: amapi.h:279
amvalidate_function amvalidate
Definition: amapi.h:304
ammarkpos_function ammarkpos
Definition: amapi.h:311
bool amcanmulticol
Definition: amapi.h:257
bool amusemaintenanceworkmem
Definition: amapi.h:277
ambeginscan_function ambeginscan
Definition: amapi.h:306
bool amcanparallel
Definition: amapi.h:271
amrescan_function amrescan
Definition: amapi.h:307
bool amcanorder
Definition: amapi.h:243
bool amcanbuildparallel
Definition: amapi.h:273
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:316
uint8 amparallelvacuumoptions
Definition: amapi.h:281
aminsertcleanup_function aminsertcleanup
Definition: amapi.h:295
bool amcanbackward
Definition: amapi.h:253
amgettreeheight_function amgettreeheight
Definition: amapi.h:300
bool amcaninclude
Definition: amapi.h:275
bool amsearchnulls
Definition: amapi.h:263
bool amconsistentequality
Definition: amapi.h:249
bool amcanhash
Definition: amapi.h:247
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55

References IndexAmRoutine::amadjustmembers, IndexAmRoutine::ambeginscan, IndexAmRoutine::ambuild, IndexAmRoutine::ambuildempty, IndexAmRoutine::ambuildphasename, IndexAmRoutine::ambulkdelete, IndexAmRoutine::amcanbackward, IndexAmRoutine::amcanbuildparallel, IndexAmRoutine::amcanhash, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanorderbyop, IndexAmRoutine::amcanparallel, IndexAmRoutine::amcanreturn, IndexAmRoutine::amcanunique, IndexAmRoutine::amclusterable, IndexAmRoutine::amconsistentequality, IndexAmRoutine::amconsistentordering, IndexAmRoutine::amcostestimate, IndexAmRoutine::amendscan, IndexAmRoutine::amestimateparallelscan, IndexAmRoutine::amgetbitmap, IndexAmRoutine::amgettreeheight, IndexAmRoutine::amgettuple, IndexAmRoutine::aminitparallelscan, IndexAmRoutine::aminsert, IndexAmRoutine::aminsertcleanup, 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::amsummarizing, IndexAmRoutine::amsupport, IndexAmRoutine::amtranslatecmptype, IndexAmRoutine::amtranslatestrategy, IndexAmRoutine::amusemaintenanceworkmem, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, GIST_OPTIONS_PROC, gistadjustmembers(), gistbeginscan(), gistbuild(), gistbuildempty(), gistbulkdelete(), gistcanreturn(), gistcostestimate(), gistendscan(), gistgetbitmap(), gistgettuple(), gistinsert(), GISTNProcs, gistoptions(), gistproperty(), gistrescan(), gisttranslatecmptype(), 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 165 of file gist.c.

170{
171 GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
172 IndexTuple itup;
173 MemoryContext oldCxt;
174
175 /* Initialize GISTSTATE cache if first call in this statement */
176 if (giststate == NULL)
177 {
178 oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
179 giststate = initGISTstate(r);
180 giststate->tempCxt = createTempGistContext();
181 indexInfo->ii_AmCache = giststate;
182 MemoryContextSwitchTo(oldCxt);
183 }
184
185 oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
186
187 itup = gistFormTuple(giststate, r, values, isnull, true);
188 itup->t_tid = *ht_ctid;
189
190 gistdoinsert(r, itup, 0, giststate, heapRel, false);
191
192 /* cleanup */
193 MemoryContextSwitchTo(oldCxt);
194 MemoryContextReset(giststate->tempCxt);
195
196 return false;
197}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1530
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
Definition: gist.c:639
MemoryContext createTempGistContext(void)
Definition: gist.c:128
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
Definition: gistutil.c:575
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
MemoryContext tempCxt
Definition: gist_private.h:78
void * ii_AmCache
Definition: execnodes.h:220
MemoryContext ii_Context
Definition: execnodes.h:221

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 1253 of file gist.c.

1255{
1256 return gistinserttuples(state, stack, giststate, &tuple, 1, oldoffnum,
1257 InvalidBuffer, InvalidBuffer, false, false);
1258}

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 1287 of file gist.c.

1292{
1293 List *splitinfo;
1294 bool is_split;
1295
1296 /*
1297 * Check for any rw conflicts (in serializable isolation level) just
1298 * before we intend to modify the page
1299 */
1301
1302 /* Insert the tuple(s) to the page, splitting the page if necessary */
1303 is_split = gistplacetopage(state->r, state->freespace, giststate,
1304 stack->buffer,
1305 tuples, ntup,
1306 oldoffnum, NULL,
1307 leftchild,
1308 &splitinfo,
1309 true,
1310 state->heapRel,
1311 state->is_build);
1312
1313 /*
1314 * Before recursing up in case the page was split, release locks on the
1315 * child pages. We don't need to keep them locked when updating the
1316 * parent.
1317 */
1320 if (BufferIsValid(leftchild) && unlockleftchild)
1322
1323 /*
1324 * If we had to split, insert/update the downlinks in the parent. If the
1325 * caller requested us to release the lock on stack->buffer, tell
1326 * gistfinishsplit() to do that as soon as it's safe to do so. If we
1327 * didn't have to split, release it ourselves.
1328 */
1329 if (splitinfo)
1330 gistfinishsplit(state, stack, giststate, splitinfo, unlockbuf);
1331 else if (unlockbuf)
1332 LockBuffer(stack->buffer, GIST_UNLOCK);
1333
1334 return is_split;
1335}
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:347
#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:230
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4326

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 230 of file gist.c.

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

References Assert(), gistxlogPage::blkno, SplitPageLayout::block, GISTPageSplitInfo::buf, SplitPageLayout::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(), SplitPageLayout::itup, lappend(), SplitPageLayout::lenlist, SplitPageLayout::list, MarkBufferDirty(), SplitPageLayout::next, NIL, gistxlogPage::num, OffsetNumberIsValid, SplitPageLayout::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 1668 of file gist.c.

1669{
1671 int ndeletable = 0;
1672 OffsetNumber offnum,
1673 maxoff;
1674
1675 Assert(GistPageIsLeaf(page));
1676
1677 /*
1678 * Scan over all items to see which ones need to be deleted according to
1679 * LP_DEAD flags.
1680 */
1681 maxoff = PageGetMaxOffsetNumber(page);
1682 for (offnum = FirstOffsetNumber;
1683 offnum <= maxoff;
1684 offnum = OffsetNumberNext(offnum))
1685 {
1686 ItemId itemId = PageGetItemId(page, offnum);
1687
1688 if (ItemIdIsDead(itemId))
1689 deletable[ndeletable++] = offnum;
1690 }
1691
1692 if (ndeletable > 0)
1693 {
1694 TransactionId snapshotConflictHorizon = InvalidTransactionId;
1695
1697 snapshotConflictHorizon =
1698 index_compute_xid_horizon_for_tuples(rel, heapRel, buffer,
1699 deletable, ndeletable);
1700
1702
1703 PageIndexMultiDelete(page, deletable, ndeletable);
1704
1705 /*
1706 * Mark the page as not containing any LP_DEAD items. This is not
1707 * certainly true (there might be some that have recently been marked,
1708 * but weren't included in our target-item list), but it will almost
1709 * always be true and it doesn't seem worth an additional page scan to
1710 * check it. Remember that F_HAS_GARBAGE is only a hint anyway.
1711 */
1713
1714 MarkBufferDirty(buffer);
1715
1716 /* XLOG stuff */
1717 if (RelationNeedsWAL(rel))
1718 {
1719 XLogRecPtr recptr;
1720
1721 recptr = gistXLogDelete(buffer,
1722 deletable, ndeletable,
1723 snapshotConflictHorizon,
1724 heapRel);
1725
1726 PageSetLSN(page, recptr);
1727 }
1728 else
1729 PageSetLSN(page, gistGetFakeLSN(rel));
1730
1732 }
1733
1734 /*
1735 * Note: if we didn't find any LP_DEAD items, then the page's
1736 * F_HAS_GARBAGE hint bit is falsely set. We do not bother expending a
1737 * separate write to clear it, however. We will clear it when we split
1738 * the page.
1739 */
1740}
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1150
uint32 TransactionId
Definition: c.h:623
TransactionId index_compute_xid_horizon_for_tuples(Relation irel, Relation hrel, Buffer ibuf, OffsetNumber *itemnos, int nitems)
Definition: genam.c:295
#define GistClearPageHasGarbage(page)
Definition: gist.h:181
XLogRecPtr gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon, Relation heaprel)
Definition: gistxlog.c:670
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define MaxIndexTuplesPerPage
Definition: itup.h:181
#define InvalidTransactionId
Definition: transam.h:31
#define XLogStandbyInfoActive()
Definition: xlog.h:123

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()

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

Definition at line 1443 of file gist.c.

1448{
1449 IndexTuple *lvectup,
1450 *rvectup;
1452 int i;
1453 SplitPageLayout *res = NULL;
1454
1455 /* this should never recurse very deeply, but better safe than sorry */
1457
1458 /* there's no point in splitting an empty page */
1459 Assert(len > 0);
1460
1461 /*
1462 * If a single tuple doesn't fit on a page, no amount of splitting will
1463 * help.
1464 */
1465 if (len == 1)
1466 ereport(ERROR,
1467 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1468 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1469 IndexTupleSize(itup[0]), GiSTPageSize,
1471
1472 memset(v.spl_lisnull, true,
1473 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1474 memset(v.spl_risnull, true,
1475 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1476 gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1477
1478 /* form left and right vector */
1479 lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1480 rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1481
1482 for (i = 0; i < v.splitVector.spl_nleft; i++)
1483 lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1484
1485 for (i = 0; i < v.splitVector.spl_nright; i++)
1486 rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1487
1488 /* finalize splitting (may need another split) */
1489 if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1490 {
1491 res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1492 }
1493 else
1494 {
1495 ROTATEDIST(res);
1497 res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1498 res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1499 }
1500
1501 if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1502 {
1503 SplitPageLayout *resptr,
1504 *subres;
1505
1506 resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1507
1508 /* install on list's tail */
1509 while (resptr->next)
1510 resptr = resptr->next;
1511
1512 resptr->next = res;
1513 res = subres;
1514 }
1515 else
1516 {
1517 ROTATEDIST(res);
1518 res->block.num = v.splitVector.spl_nleft;
1519 res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1520 res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1521 }
1522
1523 return res;
1524}
int errcode(int sqlerrcode)
Definition: elog.c:853
#define ROTATEDIST(d)
Definition: gist.c:45
#define GiSTPageSize
Definition: gist_private.h:476
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
for(;;)
const void size_t len
void check_stack_depth(void)
Definition: stack_depth.c:95
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
int spl_nleft
Definition: gist.h:144
OffsetNumber * spl_right
Definition: gist.h:148
int spl_nright
Definition: gist.h:149
OffsetNumber * spl_left
Definition: gist.h:143
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(), SplitPageLayout::block, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, for(), gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplit(), gistSplitByKey(), i, if(), IndexTupleSize(), SplitPageLayout::itup, len, SplitPageLayout::lenlist, SplitPageLayout::list, TupleDescData::natts, SplitPageLayout::next, GISTSTATE::nonLeafTupdesc, gistxlogPage::num, palloc(), RelationGetRelationName, 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(), gistplacetopage(), and gistSplit().

◆ initGISTstate()

GISTSTATE * initGISTstate ( Relation  index)

Definition at line 1530 of file gist.c.

1531{
1532 GISTSTATE *giststate;
1533 MemoryContext scanCxt;
1534 MemoryContext oldCxt;
1535 int i;
1536
1537 /* safety check to protect fixed-size arrays in GISTSTATE */
1538 if (index->rd_att->natts > INDEX_MAX_KEYS)
1539 elog(ERROR, "numberOfAttributes %d > %d",
1540 index->rd_att->natts, INDEX_MAX_KEYS);
1541
1542 /* Create the memory context that will hold the GISTSTATE */
1544 "GiST scan context",
1546 oldCxt = MemoryContextSwitchTo(scanCxt);
1547
1548 /* Create and fill in the GISTSTATE */
1549 giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1550
1551 giststate->scanCxt = scanCxt;
1552 giststate->tempCxt = scanCxt; /* caller must change this if needed */
1553 giststate->leafTupdesc = index->rd_att;
1554
1555 /*
1556 * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1557 * the INCLUDE attributes.
1558 *
1559 * It is used to form tuples during tuple adjustment and page split.
1560 * B-tree creates shortened tuple descriptor for every truncated tuple,
1561 * because it is doing this less often: it does not have to form truncated
1562 * tuples during page split. Also, B-tree is not adjusting tuples on
1563 * internal pages the way GiST does.
1564 */
1567
1569 {
1570 fmgr_info_copy(&(giststate->consistentFn[i]),
1572 scanCxt);
1573 fmgr_info_copy(&(giststate->unionFn[i]),
1575 scanCxt);
1576
1577 /* opclasses are not required to provide a Compress method */
1579 fmgr_info_copy(&(giststate->compressFn[i]),
1581 scanCxt);
1582 else
1583 giststate->compressFn[i].fn_oid = InvalidOid;
1584
1585 /* opclasses are not required to provide a Decompress method */
1587 fmgr_info_copy(&(giststate->decompressFn[i]),
1589 scanCxt);
1590 else
1591 giststate->decompressFn[i].fn_oid = InvalidOid;
1592
1593 fmgr_info_copy(&(giststate->penaltyFn[i]),
1595 scanCxt);
1596 fmgr_info_copy(&(giststate->picksplitFn[i]),
1598 scanCxt);
1599 fmgr_info_copy(&(giststate->equalFn[i]),
1601 scanCxt);
1602
1603 /* opclasses are not required to provide a Distance method */
1605 fmgr_info_copy(&(giststate->distanceFn[i]),
1607 scanCxt);
1608 else
1609 giststate->distanceFn[i].fn_oid = InvalidOid;
1610
1611 /* opclasses are not required to provide a Fetch method */
1613 fmgr_info_copy(&(giststate->fetchFn[i]),
1615 scanCxt);
1616 else
1617 giststate->fetchFn[i].fn_oid = InvalidOid;
1618
1619 /*
1620 * If the index column has a specified collation, we should honor that
1621 * while doing comparisons. However, we may have a collatable storage
1622 * type for a noncollatable indexed data type. If there's no index
1623 * collation then specify default collation in case the support
1624 * functions need collation. This is harmless if the support
1625 * functions don't care about collation, so we just do it
1626 * unconditionally. (We could alternatively call get_typcollation,
1627 * but that seems like expensive overkill --- there aren't going to be
1628 * any cases where a GiST storage type has a nondefault collation.)
1629 */
1630 if (OidIsValid(index->rd_indcollation[i]))
1631 giststate->supportCollation[i] = index->rd_indcollation[i];
1632 else
1633 giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1634 }
1635
1636 /* No opclass information for INCLUDE attributes */
1637 for (; i < index->rd_att->natts; i++)
1638 {
1639 giststate->consistentFn[i].fn_oid = InvalidOid;
1640 giststate->unionFn[i].fn_oid = InvalidOid;
1641 giststate->compressFn[i].fn_oid = InvalidOid;
1642 giststate->decompressFn[i].fn_oid = InvalidOid;
1643 giststate->penaltyFn[i].fn_oid = InvalidOid;
1644 giststate->picksplitFn[i].fn_oid = InvalidOid;
1645 giststate->equalFn[i].fn_oid = InvalidOid;
1646 giststate->distanceFn[i].fn_oid = InvalidOid;
1647 giststate->fetchFn[i].fn_oid = InvalidOid;
1648 giststate->supportCollation[i] = InvalidOid;
1649 }
1650
1651 MemoryContextSwitchTo(oldCxt);
1652
1653 return giststate;
1654}
#define OidIsValid(objectId)
Definition: c.h:746
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:580
#define GIST_DECOMPRESS_PROC
Definition: gist.h:35
#define GIST_PICKSPLIT_PROC
Definition: gist.h:37
#define GIST_CONSISTENT_PROC
Definition: gist.h:32
#define GIST_UNION_PROC
Definition: gist.h:33
#define GIST_FETCH_PROC
Definition: gist.h:40
#define GIST_COMPRESS_PROC
Definition: gist.h:34
#define GIST_PENALTY_PROC
Definition: gist.h:36
#define GIST_DISTANCE_PROC
Definition: gist.h:39
#define GIST_EQUAL_PROC
Definition: gist.h:38
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:906
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:872
#define INDEX_MAX_KEYS
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:531
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 CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
Definition: tupdesc.c:278

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, GISTSTATE::compressFn, GISTSTATE::consistentFn, CreateTupleDescTruncatedCopy(), 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(), GISTSTATE::nonLeafTupdesc, OidIsValid, palloc(), GISTSTATE::penaltyFn, GISTSTATE::picksplitFn, GISTSTATE::scanCxt, GISTSTATE::supportCollation, GISTSTATE::tempCxt, and GISTSTATE::unionFn.

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