Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / access / gist / gist.c
blobb565f5474bc2baf2947f89e5d22724abd5100ef6
1 /*-------------------------------------------------------------------------
3 * gist.c
4 * interface routines for the postgres GiST index access method.
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/genam.h"
18 #include "access/gist_private.h"
19 #include "catalog/index.h"
20 #include "miscadmin.h"
21 #include "storage/bufmgr.h"
22 #include "storage/indexfsm.h"
23 #include "utils/memutils.h"
25 const XLogRecPtr XLogRecPtrForTemp = {1, 1};
27 /* Working state for gistbuild and its callback */
28 typedef struct
30 GISTSTATE giststate;
31 int numindexattrs;
32 double indtuples;
33 MemoryContext tmpCtx;
34 } GISTBuildState;
37 /* non-export function prototypes */
38 static void gistbuildCallback(Relation index,
39 HeapTuple htup,
40 Datum *values,
41 bool *isnull,
42 bool tupleIsAlive,
43 void *state);
44 static void gistdoinsert(Relation r,
45 IndexTuple itup,
46 Size freespace,
47 GISTSTATE *GISTstate);
48 static void gistfindleaf(GISTInsertState *state,
49 GISTSTATE *giststate);
52 #define ROTATEDIST(d) do { \
53 SplitedPageLayout *tmp=(SplitedPageLayout*)palloc(sizeof(SplitedPageLayout)); \
54 memset(tmp,0,sizeof(SplitedPageLayout)); \
55 tmp->block.blkno = InvalidBlockNumber; \
56 tmp->buffer = InvalidBuffer; \
57 tmp->next = (d); \
58 (d)=tmp; \
59 } while(0)
63 * Create and return a temporary memory context for use by GiST. We
64 * _always_ invoke user-provided methods in a temporary memory
65 * context, so that memory leaks in those functions cannot cause
66 * problems. Also, we use some additional temporary contexts in the
67 * GiST code itself, to avoid the need to do some awkward manual
68 * memory management.
70 MemoryContext
71 createTempGistContext(void)
73 return AllocSetContextCreate(CurrentMemoryContext,
74 "GiST temporary context",
75 ALLOCSET_DEFAULT_MINSIZE,
76 ALLOCSET_DEFAULT_INITSIZE,
77 ALLOCSET_DEFAULT_MAXSIZE);
81 * Routine to build an index. Basically calls insert over and over.
83 * XXX: it would be nice to implement some sort of bulk-loading
84 * algorithm, but it is not clear how to do that.
86 Datum
87 gistbuild(PG_FUNCTION_ARGS)
89 Relation heap = (Relation) PG_GETARG_POINTER(0);
90 Relation index = (Relation) PG_GETARG_POINTER(1);
91 IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
92 IndexBuildResult *result;
93 double reltuples;
94 GISTBuildState buildstate;
95 Buffer buffer;
96 Page page;
99 * We expect to be called exactly once for any index relation. If that's
100 * not the case, big trouble's what we have.
102 if (RelationGetNumberOfBlocks(index) != 0)
103 elog(ERROR, "index \"%s\" already contains data",
104 RelationGetRelationName(index));
106 /* no locking is needed */
107 initGISTstate(&buildstate.giststate, index);
109 /* initialize the root page */
110 buffer = gistNewBuffer(index);
111 Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO);
112 page = BufferGetPage(buffer);
114 START_CRIT_SECTION();
116 GISTInitBuffer(buffer, F_LEAF);
118 MarkBufferDirty(buffer);
120 if (!index->rd_istemp)
122 XLogRecPtr recptr;
123 XLogRecData rdata;
125 rdata.data = (char *) &(index->rd_node);
126 rdata.len = sizeof(RelFileNode);
127 rdata.buffer = InvalidBuffer;
128 rdata.next = NULL;
130 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_CREATE_INDEX, &rdata);
131 PageSetLSN(page, recptr);
132 PageSetTLI(page, ThisTimeLineID);
134 else
135 PageSetLSN(page, XLogRecPtrForTemp);
137 UnlockReleaseBuffer(buffer);
139 END_CRIT_SECTION();
141 /* build the index */
142 buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs;
143 buildstate.indtuples = 0;
146 * create a temporary memory context that is reset once for each tuple
147 * inserted into the index
149 buildstate.tmpCtx = createTempGistContext();
151 /* do the heap scan */
152 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
153 gistbuildCallback, (void *) &buildstate);
155 /* okay, all heap tuples are indexed */
156 MemoryContextDelete(buildstate.tmpCtx);
158 freeGISTstate(&buildstate.giststate);
161 * Return statistics
163 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
165 result->heap_tuples = reltuples;
166 result->index_tuples = buildstate.indtuples;
168 PG_RETURN_POINTER(result);
172 * Per-tuple callback from IndexBuildHeapScan
174 static void
175 gistbuildCallback(Relation index,
176 HeapTuple htup,
177 Datum *values,
178 bool *isnull,
179 bool tupleIsAlive,
180 void *state)
182 GISTBuildState *buildstate = (GISTBuildState *) state;
183 IndexTuple itup;
184 MemoryContext oldCtx;
186 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
188 /* form an index tuple and point it at the heap tuple */
189 itup = gistFormTuple(&buildstate->giststate, index,
190 values, isnull, true /* size is currently bogus */ );
191 itup->t_tid = htup->t_self;
194 * Since we already have the index relation locked, we call gistdoinsert
195 * directly. Normal access method calls dispatch through gistinsert,
196 * which locks the relation for write. This is the right thing to do if
197 * you're inserting single tups, but not when you're initializing the
198 * whole index at once.
200 * In this path we respect the fillfactor setting, whereas insertions
201 * after initial build do not.
203 gistdoinsert(index, itup,
204 RelationGetTargetPageFreeSpace(index, GIST_DEFAULT_FILLFACTOR),
205 &buildstate->giststate);
207 buildstate->indtuples += 1;
208 MemoryContextSwitchTo(oldCtx);
209 MemoryContextReset(buildstate->tmpCtx);
213 * gistinsert -- wrapper for GiST tuple insertion.
215 * This is the public interface routine for tuple insertion in GiSTs.
216 * It doesn't do any work; just locks the relation and passes the buck.
218 Datum
219 gistinsert(PG_FUNCTION_ARGS)
221 Relation r = (Relation) PG_GETARG_POINTER(0);
222 Datum *values = (Datum *) PG_GETARG_POINTER(1);
223 bool *isnull = (bool *) PG_GETARG_POINTER(2);
224 ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
226 #ifdef NOT_USED
227 Relation heapRel = (Relation) PG_GETARG_POINTER(4);
228 bool checkUnique = PG_GETARG_BOOL(5);
229 #endif
230 IndexTuple itup;
231 GISTSTATE giststate;
232 MemoryContext oldCtx;
233 MemoryContext insertCtx;
235 insertCtx = createTempGistContext();
236 oldCtx = MemoryContextSwitchTo(insertCtx);
238 initGISTstate(&giststate, r);
240 itup = gistFormTuple(&giststate, r,
241 values, isnull, true /* size is currently bogus */ );
242 itup->t_tid = *ht_ctid;
244 gistdoinsert(r, itup, 0, &giststate);
246 /* cleanup */
247 freeGISTstate(&giststate);
248 MemoryContextSwitchTo(oldCtx);
249 MemoryContextDelete(insertCtx);
251 PG_RETURN_BOOL(true);
256 * Workhouse routine for doing insertion into a GiST index. Note that
257 * this routine assumes it is invoked in a short-lived memory context,
258 * so it does not bother releasing palloc'd allocations.
260 static void
261 gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
263 GISTInsertState state;
265 memset(&state, 0, sizeof(GISTInsertState));
267 state.itup = (IndexTuple *) palloc(sizeof(IndexTuple));
268 state.itup[0] = (IndexTuple) palloc(IndexTupleSize(itup));
269 memcpy(state.itup[0], itup, IndexTupleSize(itup));
270 state.ituplen = 1;
271 state.freespace = freespace;
272 state.r = r;
273 state.key = itup->t_tid;
274 state.needInsertComplete = true;
276 state.stack = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
277 state.stack->blkno = GIST_ROOT_BLKNO;
279 gistfindleaf(&state, giststate);
280 gistmakedeal(&state, giststate);
283 static bool
284 gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
286 bool is_splitted = false;
287 bool is_leaf = (GistPageIsLeaf(state->stack->page)) ? true : false;
290 * if (!is_leaf) remove old key: This node's key has been modified, either
291 * because a child split occurred or because we needed to adjust our key
292 * for an insert in a child node. Therefore, remove the old version of
293 * this node's key.
295 * for WAL replay, in the non-split case we handle this by setting up a
296 * one-element todelete array; in the split case, it's handled implicitly
297 * because the tuple vector passed to gistSplit won't include this tuple.
299 * XXX: If we want to change fillfactors between node and leaf, fillfactor
300 * = (is_leaf ? state->leaf_fillfactor : state->node_fillfactor)
302 if (gistnospace(state->stack->page, state->itup, state->ituplen,
303 is_leaf ? InvalidOffsetNumber : state->stack->childoffnum,
304 state->freespace))
306 /* no space for insertion */
307 IndexTuple *itvec;
308 int tlen;
309 SplitedPageLayout *dist = NULL,
310 *ptr;
311 BlockNumber rrlink = InvalidBlockNumber;
312 GistNSN oldnsn;
314 is_splitted = true;
317 * Form index tuples vector to split: remove old tuple if t's needed
318 * and add new tuples to vector
320 itvec = gistextractpage(state->stack->page, &tlen);
321 if (!is_leaf)
323 /* on inner page we should remove old tuple */
324 int pos = state->stack->childoffnum - FirstOffsetNumber;
326 tlen--;
327 if (pos != tlen)
328 memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
330 itvec = gistjoinvector(itvec, &tlen, state->itup, state->ituplen);
331 dist = gistSplit(state->r, state->stack->page, itvec, tlen, giststate);
333 state->itup = (IndexTuple *) palloc(sizeof(IndexTuple) * tlen);
334 state->ituplen = 0;
336 if (state->stack->blkno != GIST_ROOT_BLKNO)
339 * if non-root split then we should not allocate new buffer, but
340 * we must create temporary page to operate
342 dist->buffer = state->stack->buffer;
343 dist->page = PageGetTempPageCopySpecial(BufferGetPage(dist->buffer));
345 /* clean all flags except F_LEAF */
346 GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
349 /* make new pages and fills them */
350 for (ptr = dist; ptr; ptr = ptr->next)
352 int i;
353 char *data;
355 /* get new page */
356 if (ptr->buffer == InvalidBuffer)
358 ptr->buffer = gistNewBuffer(state->r);
359 GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
360 ptr->page = BufferGetPage(ptr->buffer);
362 ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
365 * fill page, we can do it because all these pages are new (ie not
366 * linked in tree or masked by temp page
368 data = (char *) (ptr->list);
369 for (i = 0; i < ptr->block.num; i++)
371 if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
372 elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(state->r));
373 data += IndexTupleSize((IndexTuple) data);
376 /* set up ItemPointer and remember it for parent */
377 ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
378 state->itup[state->ituplen] = ptr->itup;
379 state->ituplen++;
382 /* saves old rightlink */
383 if (state->stack->blkno != GIST_ROOT_BLKNO)
384 rrlink = GistPageGetOpaque(dist->page)->rightlink;
386 START_CRIT_SECTION();
389 * must mark buffers dirty before XLogInsert, even though we'll still
390 * be changing their opaque fields below. set up right links.
392 for (ptr = dist; ptr; ptr = ptr->next)
394 MarkBufferDirty(ptr->buffer);
395 GistPageGetOpaque(ptr->page)->rightlink = (ptr->next) ?
396 ptr->next->block.blkno : rrlink;
399 /* restore splitted non-root page */
400 if (state->stack->blkno != GIST_ROOT_BLKNO)
402 PageRestoreTempPage(dist->page, BufferGetPage(dist->buffer));
403 dist->page = BufferGetPage(dist->buffer);
406 if (!state->r->rd_istemp)
408 XLogRecPtr recptr;
409 XLogRecData *rdata;
411 rdata = formSplitRdata(state->r->rd_node, state->stack->blkno,
412 is_leaf, &(state->key), dist);
414 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
416 for (ptr = dist; ptr; ptr = ptr->next)
418 PageSetLSN(ptr->page, recptr);
419 PageSetTLI(ptr->page, ThisTimeLineID);
422 else
424 for (ptr = dist; ptr; ptr = ptr->next)
426 PageSetLSN(ptr->page, XLogRecPtrForTemp);
430 /* set up NSN */
431 oldnsn = GistPageGetOpaque(dist->page)->nsn;
432 if (state->stack->blkno == GIST_ROOT_BLKNO)
433 /* if root split we should put initial value */
434 oldnsn = PageGetLSN(dist->page);
436 for (ptr = dist; ptr; ptr = ptr->next)
438 /* only for last set oldnsn */
439 GistPageGetOpaque(ptr->page)->nsn = (ptr->next) ?
440 PageGetLSN(ptr->page) : oldnsn;
444 * release buffers, if it was a root split then release all buffers
445 * because we create all buffers
447 ptr = (state->stack->blkno == GIST_ROOT_BLKNO) ? dist : dist->next;
448 for (; ptr; ptr = ptr->next)
449 UnlockReleaseBuffer(ptr->buffer);
451 if (state->stack->blkno == GIST_ROOT_BLKNO)
453 gistnewroot(state->r, state->stack->buffer, state->itup, state->ituplen, &(state->key));
454 state->needInsertComplete = false;
457 END_CRIT_SECTION();
459 else
461 /* enough space */
462 START_CRIT_SECTION();
464 if (!is_leaf)
465 PageIndexTupleDelete(state->stack->page, state->stack->childoffnum);
466 gistfillbuffer(state->stack->page, state->itup, state->ituplen, InvalidOffsetNumber);
468 MarkBufferDirty(state->stack->buffer);
470 if (!state->r->rd_istemp)
472 OffsetNumber noffs = 0,
473 offs[1];
474 XLogRecPtr recptr;
475 XLogRecData *rdata;
477 if (!is_leaf)
479 /* only on inner page we should delete previous version */
480 offs[0] = state->stack->childoffnum;
481 noffs = 1;
484 rdata = formUpdateRdata(state->r->rd_node, state->stack->buffer,
485 offs, noffs,
486 state->itup, state->ituplen,
487 &(state->key));
489 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
490 PageSetLSN(state->stack->page, recptr);
491 PageSetTLI(state->stack->page, ThisTimeLineID);
493 else
494 PageSetLSN(state->stack->page, XLogRecPtrForTemp);
496 if (state->stack->blkno == GIST_ROOT_BLKNO)
497 state->needInsertComplete = false;
499 END_CRIT_SECTION();
501 if (state->ituplen > 1)
502 { /* previous is_splitted==true */
505 * child was splited, so we must form union for insertion in
506 * parent
508 IndexTuple newtup = gistunion(state->r, state->itup, state->ituplen, giststate);
510 ItemPointerSetBlockNumber(&(newtup->t_tid), state->stack->blkno);
511 state->itup[0] = newtup;
512 state->ituplen = 1;
514 else if (is_leaf)
517 * itup[0] store key to adjust parent, we set it to valid to
518 * correct check by GistTupleIsInvalid macro in gistgetadjusted()
520 ItemPointerSetBlockNumber(&(state->itup[0]->t_tid), state->stack->blkno);
521 GistTupleSetValid(state->itup[0]);
524 return is_splitted;
528 * returns stack of pages, all pages in stack are pinned, and
529 * leaf is X-locked
532 static void
533 gistfindleaf(GISTInsertState *state, GISTSTATE *giststate)
535 ItemId iid;
536 IndexTuple idxtuple;
537 GISTPageOpaque opaque;
540 * walk down, We don't lock page for a long time, but so we should be
541 * ready to recheck path in a bad case... We remember, that page->lsn
542 * should never be invalid.
544 for (;;)
546 if (XLogRecPtrIsInvalid(state->stack->lsn))
547 state->stack->buffer = ReadBuffer(state->r, state->stack->blkno);
548 LockBuffer(state->stack->buffer, GIST_SHARE);
549 gistcheckpage(state->r, state->stack->buffer);
551 state->stack->page = (Page) BufferGetPage(state->stack->buffer);
552 opaque = GistPageGetOpaque(state->stack->page);
554 state->stack->lsn = PageGetLSN(state->stack->page);
555 Assert(state->r->rd_istemp || !XLogRecPtrIsInvalid(state->stack->lsn));
557 if (state->stack->blkno != GIST_ROOT_BLKNO &&
558 XLByteLT(state->stack->parent->lsn, opaque->nsn))
561 * caused split non-root page is detected, go up to parent to
562 * choose best child
564 UnlockReleaseBuffer(state->stack->buffer);
565 state->stack = state->stack->parent;
566 continue;
569 if (!GistPageIsLeaf(state->stack->page))
572 * This is an internal page, so continue to walk down the tree. We
573 * find the child node that has the minimum insertion penalty and
574 * recursively invoke ourselves to modify that node. Once the
575 * recursive call returns, we may need to adjust the parent node
576 * for two reasons: the child node split, or the key in this node
577 * needs to be adjusted for the newly inserted key below us.
579 GISTInsertStack *item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
581 state->stack->childoffnum = gistchoose(state->r, state->stack->page, state->itup[0], giststate);
583 iid = PageGetItemId(state->stack->page, state->stack->childoffnum);
584 idxtuple = (IndexTuple) PageGetItem(state->stack->page, iid);
585 item->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
586 LockBuffer(state->stack->buffer, GIST_UNLOCK);
588 item->parent = state->stack;
589 item->child = NULL;
590 if (state->stack)
591 state->stack->child = item;
592 state->stack = item;
594 else
596 /* be carefull, during unlock/lock page may be changed... */
597 LockBuffer(state->stack->buffer, GIST_UNLOCK);
598 LockBuffer(state->stack->buffer, GIST_EXCLUSIVE);
599 state->stack->page = (Page) BufferGetPage(state->stack->buffer);
600 opaque = GistPageGetOpaque(state->stack->page);
602 if (state->stack->blkno == GIST_ROOT_BLKNO)
605 * the only page can become inner instead of leaf is a root
606 * page, so for root we should recheck it
608 if (!GistPageIsLeaf(state->stack->page))
611 * very rarely situation: during unlock/lock index with
612 * number of pages = 1 was increased
614 LockBuffer(state->stack->buffer, GIST_UNLOCK);
615 continue;
619 * we don't need to check root split, because checking
620 * leaf/inner is enough to recognize split for root
624 else if (XLByteLT(state->stack->parent->lsn, opaque->nsn))
627 * detecting split during unlock/lock, so we should find
628 * better child on parent
631 /* forget buffer */
632 UnlockReleaseBuffer(state->stack->buffer);
634 state->stack = state->stack->parent;
635 continue;
638 state->stack->lsn = PageGetLSN(state->stack->page);
640 /* ok we found a leaf page and it X-locked */
641 break;
645 /* now state->stack->(page, buffer and blkno) points to leaf page */
649 * Traverse the tree to find path from root page to specified "child" block.
651 * returns from the beginning of closest parent;
653 * To prevent deadlocks, this should lock only one page simultaneously.
655 GISTInsertStack *
656 gistFindPath(Relation r, BlockNumber child)
658 Page page;
659 Buffer buffer;
660 OffsetNumber i,
661 maxoff;
662 ItemId iid;
663 IndexTuple idxtuple;
664 GISTInsertStack *top,
665 *tail,
666 *ptr;
667 BlockNumber blkno;
669 top = tail = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
670 top->blkno = GIST_ROOT_BLKNO;
672 while (top && top->blkno != child)
674 buffer = ReadBuffer(r, top->blkno);
675 LockBuffer(buffer, GIST_SHARE);
676 gistcheckpage(r, buffer);
677 page = (Page) BufferGetPage(buffer);
679 if (GistPageIsLeaf(page))
681 /* we can safety go away, follows only leaf pages */
682 UnlockReleaseBuffer(buffer);
683 return NULL;
686 top->lsn = PageGetLSN(page);
688 if (top->parent && XLByteLT(top->parent->lsn, GistPageGetOpaque(page)->nsn) &&
689 GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ )
691 /* page splited while we thinking of... */
692 ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
693 ptr->blkno = GistPageGetOpaque(page)->rightlink;
694 ptr->childoffnum = InvalidOffsetNumber;
695 ptr->parent = top;
696 ptr->next = NULL;
697 tail->next = ptr;
698 tail = ptr;
701 maxoff = PageGetMaxOffsetNumber(page);
703 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
705 iid = PageGetItemId(page, i);
706 idxtuple = (IndexTuple) PageGetItem(page, iid);
707 blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
708 if (blkno == child)
710 OffsetNumber poff = InvalidOffsetNumber;
712 /* make childs links */
713 ptr = top;
714 while (ptr->parent)
716 /* set child link */
717 ptr->parent->child = ptr;
718 /* move childoffnum.. */
719 if (ptr == top)
721 /* first iteration */
722 poff = ptr->parent->childoffnum;
723 ptr->parent->childoffnum = ptr->childoffnum;
725 else
727 OffsetNumber tmp = ptr->parent->childoffnum;
729 ptr->parent->childoffnum = poff;
730 poff = tmp;
732 ptr = ptr->parent;
734 top->childoffnum = i;
735 UnlockReleaseBuffer(buffer);
736 return top;
738 else
740 /* Install next inner page to the end of stack */
741 ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
742 ptr->blkno = blkno;
743 ptr->childoffnum = i; /* set offsetnumber of child to child
744 * !!! */
745 ptr->parent = top;
746 ptr->next = NULL;
747 tail->next = ptr;
748 tail = ptr;
752 UnlockReleaseBuffer(buffer);
753 top = top->next;
756 return NULL;
761 * Returns X-locked parent of stack page
764 static void
765 gistFindCorrectParent(Relation r, GISTInsertStack *child)
767 GISTInsertStack *parent = child->parent;
769 LockBuffer(parent->buffer, GIST_EXCLUSIVE);
770 gistcheckpage(r, parent->buffer);
771 parent->page = (Page) BufferGetPage(parent->buffer);
773 /* here we don't need to distinguish between split and page update */
774 if (parent->childoffnum == InvalidOffsetNumber || !XLByteEQ(parent->lsn, PageGetLSN(parent->page)))
776 /* parent is changed, look child in right links until found */
777 OffsetNumber i,
778 maxoff;
779 ItemId iid;
780 IndexTuple idxtuple;
781 GISTInsertStack *ptr;
783 while (true)
785 maxoff = PageGetMaxOffsetNumber(parent->page);
786 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
788 iid = PageGetItemId(parent->page, i);
789 idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
790 if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
792 /* yes!!, found */
793 parent->childoffnum = i;
794 return;
798 parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
799 UnlockReleaseBuffer(parent->buffer);
800 if (parent->blkno == InvalidBlockNumber)
803 * end of chain and still didn't found parent, It's very-very
804 * rare situation when root splited
806 break;
807 parent->buffer = ReadBuffer(r, parent->blkno);
808 LockBuffer(parent->buffer, GIST_EXCLUSIVE);
809 gistcheckpage(r, parent->buffer);
810 parent->page = (Page) BufferGetPage(parent->buffer);
814 * awful!!, we need search tree to find parent ... , but before we
815 * should release all old parent
818 ptr = child->parent->parent; /* child->parent already released
819 * above */
820 while (ptr)
822 ReleaseBuffer(ptr->buffer);
823 ptr = ptr->parent;
826 /* ok, find new path */
827 ptr = parent = gistFindPath(r, child->blkno);
828 Assert(ptr != NULL);
830 /* read all buffers as expected by caller */
831 /* note we don't lock them or gistcheckpage them here! */
832 while (ptr)
834 ptr->buffer = ReadBuffer(r, ptr->blkno);
835 ptr->page = (Page) BufferGetPage(ptr->buffer);
836 ptr = ptr->parent;
839 /* install new chain of parents to stack */
840 child->parent = parent;
841 parent->child = child;
843 /* make recursive call to normal processing */
844 gistFindCorrectParent(r, child);
847 return;
850 void
851 gistmakedeal(GISTInsertState *state, GISTSTATE *giststate)
853 int is_splitted;
854 ItemId iid;
855 IndexTuple oldtup,
856 newtup;
858 /* walk up */
859 while (true)
862 * After this call: 1. if child page was splited, then itup contains
863 * keys for each page 2. if child page wasn't splited, then itup
864 * contains additional for adjustment of current key
867 if (state->stack->parent)
870 * X-lock parent page before proceed child, gistFindCorrectParent
871 * should find and lock it
873 gistFindCorrectParent(state->r, state->stack);
875 is_splitted = gistplacetopage(state, giststate);
877 /* parent locked above, so release child buffer */
878 UnlockReleaseBuffer(state->stack->buffer);
880 /* pop parent page from stack */
881 state->stack = state->stack->parent;
883 /* stack is void */
884 if (!state->stack)
885 break;
888 * child did not split, so we can check is it needed to update parent
889 * tuple
891 if (!is_splitted)
893 /* parent's tuple */
894 iid = PageGetItemId(state->stack->page, state->stack->childoffnum);
895 oldtup = (IndexTuple) PageGetItem(state->stack->page, iid);
896 newtup = gistgetadjusted(state->r, oldtup, state->itup[0], giststate);
898 if (!newtup)
899 { /* not need to update key */
900 LockBuffer(state->stack->buffer, GIST_UNLOCK);
901 break;
904 state->itup[0] = newtup;
906 } /* while */
908 /* release all parent buffers */
909 while (state->stack)
911 ReleaseBuffer(state->stack->buffer);
912 state->stack = state->stack->parent;
915 /* say to xlog that insert is completed */
916 if (state->needInsertComplete && !state->r->rd_istemp)
917 gistxlogInsertCompletion(state->r->rd_node, &(state->key), 1);
921 * gistSplit -- split a page in the tree and fill struct
922 * used for XLOG and real writes buffers. Function is recursive, ie
923 * it will split page until keys will fit in every page.
925 SplitedPageLayout *
926 gistSplit(Relation r,
927 Page page,
928 IndexTuple *itup, /* contains compressed entry */
929 int len,
930 GISTSTATE *giststate)
932 IndexTuple *lvectup,
933 *rvectup;
934 GistSplitVector v;
935 GistEntryVector *entryvec;
936 int i;
937 SplitedPageLayout *res = NULL;
939 /* generate the item array */
940 entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY));
941 entryvec->n = len + 1;
943 memset(v.spl_lisnull, TRUE, sizeof(bool) * giststate->tupdesc->natts);
944 memset(v.spl_risnull, TRUE, sizeof(bool) * giststate->tupdesc->natts);
945 gistSplitByKey(r, page, itup, len, giststate,
946 &v, entryvec, 0);
948 /* form left and right vector */
949 lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
950 rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
952 for (i = 0; i < v.splitVector.spl_nleft; i++)
953 lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
955 for (i = 0; i < v.splitVector.spl_nright; i++)
956 rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
958 /* finalize splitting (may need another split) */
959 if (!gistfitpage(rvectup, v.splitVector.spl_nright))
961 res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
963 else
965 ROTATEDIST(res);
966 res->block.num = v.splitVector.spl_nright;
967 res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
968 res->itup = (v.spl_rightvalid) ? gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false)
969 : gist_form_invalid_tuple(GIST_ROOT_BLKNO);
972 if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
974 SplitedPageLayout *resptr,
975 *subres;
977 resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
979 /* install on list's tail */
980 while (resptr->next)
981 resptr = resptr->next;
983 resptr->next = res;
984 res = subres;
986 else
988 ROTATEDIST(res);
989 res->block.num = v.splitVector.spl_nleft;
990 res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
991 res->itup = (v.spl_leftvalid) ? gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false)
992 : gist_form_invalid_tuple(GIST_ROOT_BLKNO);
995 return res;
999 * buffer must be pinned and locked by caller
1001 void
1002 gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer key)
1004 Page page;
1006 Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO);
1007 page = BufferGetPage(buffer);
1009 START_CRIT_SECTION();
1011 GISTInitBuffer(buffer, 0);
1012 gistfillbuffer(page, itup, len, FirstOffsetNumber);
1014 MarkBufferDirty(buffer);
1016 if (!r->rd_istemp)
1018 XLogRecPtr recptr;
1019 XLogRecData *rdata;
1021 rdata = formUpdateRdata(r->rd_node, buffer,
1022 NULL, 0,
1023 itup, len, key);
1025 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_NEW_ROOT, rdata);
1026 PageSetLSN(page, recptr);
1027 PageSetTLI(page, ThisTimeLineID);
1029 else
1030 PageSetLSN(page, XLogRecPtrForTemp);
1032 END_CRIT_SECTION();
1035 void
1036 initGISTstate(GISTSTATE *giststate, Relation index)
1038 int i;
1040 if (index->rd_att->natts > INDEX_MAX_KEYS)
1041 elog(ERROR, "numberOfAttributes %d > %d",
1042 index->rd_att->natts, INDEX_MAX_KEYS);
1044 giststate->tupdesc = index->rd_att;
1046 for (i = 0; i < index->rd_att->natts; i++)
1048 fmgr_info_copy(&(giststate->consistentFn[i]),
1049 index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
1050 CurrentMemoryContext);
1051 fmgr_info_copy(&(giststate->unionFn[i]),
1052 index_getprocinfo(index, i + 1, GIST_UNION_PROC),
1053 CurrentMemoryContext);
1054 fmgr_info_copy(&(giststate->compressFn[i]),
1055 index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
1056 CurrentMemoryContext);
1057 fmgr_info_copy(&(giststate->decompressFn[i]),
1058 index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
1059 CurrentMemoryContext);
1060 fmgr_info_copy(&(giststate->penaltyFn[i]),
1061 index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
1062 CurrentMemoryContext);
1063 fmgr_info_copy(&(giststate->picksplitFn[i]),
1064 index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
1065 CurrentMemoryContext);
1066 fmgr_info_copy(&(giststate->equalFn[i]),
1067 index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
1068 CurrentMemoryContext);
1072 void
1073 freeGISTstate(GISTSTATE *giststate)
1075 /* no work */