1 /*-------------------------------------------------------------------------
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
13 *-------------------------------------------------------------------------
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 */
37 /* non-export function prototypes */
38 static void gistbuildCallback(Relation index
,
44 static void gistdoinsert(Relation r
,
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; \
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
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.
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
;
94 GISTBuildState buildstate
;
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
)
125 rdata
.data
= (char *) &(index
->rd_node
);
126 rdata
.len
= sizeof(RelFileNode
);
127 rdata
.buffer
= InvalidBuffer
;
130 recptr
= XLogInsert(RM_GIST_ID
, XLOG_GIST_CREATE_INDEX
, &rdata
);
131 PageSetLSN(page
, recptr
);
132 PageSetTLI(page
, ThisTimeLineID
);
135 PageSetLSN(page
, XLogRecPtrForTemp
);
137 UnlockReleaseBuffer(buffer
);
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
);
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
175 gistbuildCallback(Relation index
,
182 GISTBuildState
*buildstate
= (GISTBuildState
*) state
;
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.
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);
227 Relation heapRel
= (Relation
) PG_GETARG_POINTER(4);
228 bool checkUnique
= PG_GETARG_BOOL(5);
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
);
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.
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
));
271 state
.freespace
= freespace
;
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
);
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
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
,
306 /* no space for insertion */
309 SplitedPageLayout
*dist
= NULL
,
311 BlockNumber rrlink
= InvalidBlockNumber
;
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
);
323 /* on inner page we should remove old tuple */
324 int pos
= state
->stack
->childoffnum
- FirstOffsetNumber
;
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
);
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
)
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
;
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
)
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
);
424 for (ptr
= dist
; ptr
; ptr
= ptr
->next
)
426 PageSetLSN(ptr
->page
, XLogRecPtrForTemp
);
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;
462 START_CRIT_SECTION();
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,
479 /* only on inner page we should delete previous version */
480 offs
[0] = state
->stack
->childoffnum
;
484 rdata
= formUpdateRdata(state
->r
->rd_node
, state
->stack
->buffer
,
486 state
->itup
, state
->ituplen
,
489 recptr
= XLogInsert(RM_GIST_ID
, XLOG_GIST_PAGE_UPDATE
, rdata
);
490 PageSetLSN(state
->stack
->page
, recptr
);
491 PageSetTLI(state
->stack
->page
, ThisTimeLineID
);
494 PageSetLSN(state
->stack
->page
, XLogRecPtrForTemp
);
496 if (state
->stack
->blkno
== GIST_ROOT_BLKNO
)
497 state
->needInsertComplete
= false;
501 if (state
->ituplen
> 1)
502 { /* previous is_splitted==true */
505 * child was splited, so we must form union for insertion in
508 IndexTuple newtup
= gistunion(state
->r
, state
->itup
, state
->ituplen
, giststate
);
510 ItemPointerSetBlockNumber(&(newtup
->t_tid
), state
->stack
->blkno
);
511 state
->itup
[0] = newtup
;
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]);
528 * returns stack of pages, all pages in stack are pinned, and
533 gistfindleaf(GISTInsertState
*state
, GISTSTATE
*giststate
)
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.
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
564 UnlockReleaseBuffer(state
->stack
->buffer
);
565 state
->stack
= state
->stack
->parent
;
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
;
591 state
->stack
->child
= item
;
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
);
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
632 UnlockReleaseBuffer(state
->stack
->buffer
);
634 state
->stack
= state
->stack
->parent
;
638 state
->stack
->lsn
= PageGetLSN(state
->stack
->page
);
640 /* ok we found a leaf page and it X-locked */
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.
656 gistFindPath(Relation r
, BlockNumber child
)
664 GISTInsertStack
*top
,
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
);
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
;
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
));
710 OffsetNumber poff
= InvalidOffsetNumber
;
712 /* make childs links */
717 ptr
->parent
->child
= ptr
;
718 /* move childoffnum.. */
721 /* first iteration */
722 poff
= ptr
->parent
->childoffnum
;
723 ptr
->parent
->childoffnum
= ptr
->childoffnum
;
727 OffsetNumber tmp
= ptr
->parent
->childoffnum
;
729 ptr
->parent
->childoffnum
= poff
;
734 top
->childoffnum
= i
;
735 UnlockReleaseBuffer(buffer
);
740 /* Install next inner page to the end of stack */
741 ptr
= (GISTInsertStack
*) palloc0(sizeof(GISTInsertStack
));
743 ptr
->childoffnum
= i
; /* set offsetnumber of child to child
752 UnlockReleaseBuffer(buffer
);
761 * Returns X-locked parent of stack page
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 */
781 GISTInsertStack
*ptr
;
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
)
793 parent
->childoffnum
= i
;
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
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
822 ReleaseBuffer(ptr
->buffer
);
826 /* ok, find new path */
827 ptr
= parent
= gistFindPath(r
, child
->blkno
);
830 /* read all buffers as expected by caller */
831 /* note we don't lock them or gistcheckpage them here! */
834 ptr
->buffer
= ReadBuffer(r
, ptr
->blkno
);
835 ptr
->page
= (Page
) BufferGetPage(ptr
->buffer
);
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
);
851 gistmakedeal(GISTInsertState
*state
, GISTSTATE
*giststate
)
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
;
888 * child did not split, so we can check is it needed to update parent
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
);
899 { /* not need to update key */
900 LockBuffer(state
->stack
->buffer
, GIST_UNLOCK
);
904 state
->itup
[0] = newtup
;
908 /* release all parent buffers */
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.
926 gistSplit(Relation r
,
928 IndexTuple
*itup
, /* contains compressed entry */
930 GISTSTATE
*giststate
)
935 GistEntryVector
*entryvec
;
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
,
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
);
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
,
977 resptr
= subres
= gistSplit(r
, page
, lvectup
, v
.splitVector
.spl_nleft
, giststate
);
979 /* install on list's tail */
981 resptr
= resptr
->next
;
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
);
999 * buffer must be pinned and locked by caller
1002 gistnewroot(Relation r
, Buffer buffer
, IndexTuple
*itup
, int len
, ItemPointer key
)
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
);
1021 rdata
= formUpdateRdata(r
->rd_node
, buffer
,
1025 recptr
= XLogInsert(RM_GIST_ID
, XLOG_GIST_NEW_ROOT
, rdata
);
1026 PageSetLSN(page
, recptr
);
1027 PageSetTLI(page
, ThisTimeLineID
);
1030 PageSetLSN(page
, XLogRecPtrForTemp
);
1036 initGISTstate(GISTSTATE
*giststate
, Relation index
)
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
);
1073 freeGISTstate(GISTSTATE
*giststate
)