/* ---------------- * An extended workhorse version of gistinserttuple(). This version allows * inserting multiple tuples, or replacing a single tuple with multiple tuples. * This is used to recursively update the downlinks in the parent when a page * is split. * * If leftchild and rightchild are valid, we're inserting/replacing the * downlink for rightchild, and leftchild is its left sibling. We clear the * F_FOLLOW_RIGHT flag and update NSN on leftchild, atomically with the * insertion of the downlink. * * To avoid holding locks for longer than necessary, when recursing up the * tree to update the parents, the locking is a bit peculiar here. On entry, * the caller must hold an exclusive lock on stack->buffer, as well as * leftchild and rightchild if given. On return: * * - Lock on stack->buffer is released, if 'unlockbuf' is true. The page is * always kept pinned, however. * - Lock on 'leftchild' is released, if 'unlockleftchild' is true. The page * is kept pinned. * - Lock and pin on 'rightchild' are always released. * * Returns 'true' if the page had to be split. Note that if the page was * split, the inserted/updated tuples might've been inserted to a right * sibling of stack->buffer instead of stack->buffer itself. */ static bool gistinserttuples(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild, Buffer rightchild, bool unlockbuf, bool unlockleftchild) { List *splitinfo; bool is_split; /* * Check for any rw conflicts (in serializable isolation level) just * before we intend to modify the page */ CheckForSerializableConflictIn(state->r, NULL, stack->buffer); /* Insert the tuple(s) to the page, splitting the page if necessary */ is_split = gistplacetopage(state->r, state->freespace, giststate, stack->buffer, tuples, ntup, oldoffnum, NULL, leftchild, &splitinfo, true, state->heapRel, state->is_build); /* * Before recursing up in case the page was split, release locks on the * child pages. We don't need to keep them locked when updating the * parent. */ if (BufferIsValid(rightchild)) UnlockReleaseBuffer(rightchild); if (BufferIsValid(leftchild) && unlockleftchild) LockBuffer(leftchild, GIST_UNLOCK); /* * If we had to split, insert/update the downlinks in the parent. If the * caller requested us to release the lock on stack->buffer, tell * gistfinishsplit() to do that as soon as it's safe to do so. If we * didn't have to split, release it ourselves. */ if (splitinfo) gistfinishsplit(state, stack, giststate, splitinfo, unlockbuf); else if (unlockbuf) LockBuffer(stack->buffer, GIST_UNLOCK); return is_split; }
/* * Complete the incomplete split of state->stack->page. */ static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate) { GISTInsertStack *stack = state->stack; Buffer buf; Page page; List *splitinfo = NIL; elog(LOG, "fixing incomplete split in index \"%s\", block %u", RelationGetRelationName(state->r), stack->blkno); Assert(GistFollowRight(stack->page)); Assert(OffsetNumberIsValid(stack->downlinkoffnum)); buf = stack->buffer; /* * Read the chain of split pages, following the rightlinks. Construct a * downlink tuple for each page. */ for (;;) { GISTPageSplitInfo *si = palloc(sizeof(GISTPageSplitInfo)); IndexTuple downlink; page = BufferGetPage(buf); /* Form the new downlink tuples to insert to parent */ downlink = gistformdownlink(state->r, buf, giststate, stack); si->buf = buf; si->downlink = downlink; splitinfo = lappend(splitinfo, si); if (GistFollowRight(page)) { /* lock next page */ buf = ReadBuffer(state->r, GistPageGetOpaque(page)->rightlink); LockBuffer(buf, GIST_EXCLUSIVE); } else break; } /* Insert the downlinks */ gistfinishsplit(state, stack, giststate, splitinfo, false); }
/* * Insert tuples to stack->buffer. If 'oldoffnum' is valid, the new tuples * replace an old tuple at oldoffnum. The caller must hold an exclusive lock * on the page. * * If leftchild is valid, we're inserting/updating the downlink for the * page to the right of leftchild. We clear the F_FOLLOW_RIGHT flag and * update NSN on leftchild, atomically with the insertion of the downlink. * * Returns 'true' if the page had to be split. On return, we will continue * to hold an exclusive lock on state->stack->buffer, but if we had to split * the page, it might not contain the tuple we just inserted/updated. */ static bool gistinserttuples(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild) { List *splitinfo; bool is_split; is_split = gistplacetopage(state, giststate, stack->buffer, tuples, ntup, oldoffnum, leftchild, &splitinfo); if (splitinfo) gistfinishsplit(state, stack, giststate, splitinfo); return is_split; }