📄 rtree.c
字号:
/*------------------------------------------------------------------------- * * rtree.c * interface routines for the postgres rtree indexed access method. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/access/rtree/rtree.c,v 1.92.2.1 2005/11/22 18:23:04 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "access/rtree.h"#include "access/xlogutils.h"#include "catalog/index.h"#include "commands/vacuum.h"#include "executor/executor.h"#include "miscadmin.h"/* * XXX We assume that all datatypes indexable in rtrees are pass-by-reference. * To fix this, you'd need to improve the IndexTupleGetDatum() macro, and * do something with the various datum-pfreeing code. However, it's not that * unreasonable an assumption in practice. */#define IndexTupleGetDatum(itup) \ PointerGetDatum(((char *) (itup)) + sizeof(IndexTupleData))/* * Space-allocation macros. Note we count the item's line pointer in its size. */#define RTPageAvailSpace \ (BLCKSZ - (sizeof(PageHeaderData) - sizeof(ItemIdData)) \ - MAXALIGN(sizeof(RTreePageOpaqueData)))#define IndexTupleTotalSize(itup) \ (MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData))#define IndexTupleAttSize(itup) \ (IndexTupleSize(itup) - sizeof(IndexTupleData))/* results of rtpicksplit() */typedef struct SPLITVEC{ OffsetNumber *spl_left; int spl_nleft; Datum spl_ldatum; OffsetNumber *spl_right; int spl_nright; Datum spl_rdatum;} SPLITVEC;/* for sorting tuples by cost, for picking split */typedef struct SPLITCOST{ OffsetNumber offset_number; float cost_differential; bool choose_left;} SPLITCOST;typedef struct RTSTATE{ FmgrInfo unionFn; /* union function */ FmgrInfo sizeFn; /* size function */ FmgrInfo interFn; /* intersection function */} RTSTATE;/* Working state for rtbuild and its callback */typedef struct{ RTSTATE rtState; double indtuples;} RTBuildState;/* non-export function prototypes */static void rtbuildCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state);static void rtdoinsert(Relation r, IndexTuple itup, RTSTATE *rtstate);static void rttighten(Relation r, RTSTACK *stk, Datum datum, int att_size, RTSTATE *rtstate);static void rtdosplit(Relation r, Buffer buffer, RTSTACK *stack, IndexTuple itup, RTSTATE *rtstate);static void rtintinsert(Relation r, RTSTACK *stk, IndexTuple ltup, IndexTuple rtup, RTSTATE *rtstate);static void rtnewroot(Relation r, IndexTuple lt, IndexTuple rt);static void rtpicksplit(Relation r, Page page, SPLITVEC *v, IndexTuple itup, RTSTATE *rtstate);static void RTInitBuffer(Buffer b, uint32 f);static OffsetNumber choose(Relation r, Page p, IndexTuple it, RTSTATE *rtstate);static int nospace(Page p, IndexTuple it);static void initRtstate(RTSTATE *rtstate, Relation index);static int qsort_comp_splitcost(const void *a, const void *b);/* * routine to build an index. Basically calls insert over and over */Datumrtbuild(PG_FUNCTION_ARGS){ Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); double reltuples; RTBuildState buildstate; Buffer buffer; /* no locking is needed */ initRtstate(&buildstate.rtState, index); /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. */ if (RelationGetNumberOfBlocks(index) != 0) elog(ERROR, "index \"%s\" already contains data", RelationGetRelationName(index)); /* initialize the root page */ buffer = ReadBuffer(index, P_NEW); RTInitBuffer(buffer, F_LEAF); WriteBuffer(buffer); /* build the index */ buildstate.indtuples = 0; /* do the heap scan */ reltuples = IndexBuildHeapScan(heap, index, indexInfo, rtbuildCallback, (void *) &buildstate); /* okay, all heap tuples are indexed */ /* since we just counted the # of tuples, may as well update stats */ IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); PG_RETURN_VOID();}/* * Per-tuple callback from IndexBuildHeapScan */static voidrtbuildCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state){ RTBuildState *buildstate = (RTBuildState *) state; IndexTuple itup; /* form an index tuple and point it at the heap tuple */ itup = index_form_tuple(RelationGetDescr(index), values, isnull); itup->t_tid = htup->t_self; /* rtree indexes don't index nulls, see notes in rtinsert */ if (IndexTupleHasNulls(itup)) { pfree(itup); return; } /* * Since we already have the index relation locked, we call rtdoinsert * directly. Normal access method calls dispatch through rtinsert, which * locks the relation for write. This is the right thing to do if you're * inserting single tups, but not when you're initializing the whole index * at once. */ rtdoinsert(index, itup, &buildstate->rtState); buildstate->indtuples += 1; pfree(itup);}/* * rtinsert -- wrapper for rtree tuple insertion. * * This is the public interface routine for tuple insertion in rtrees. * It doesn't do any work; just locks the relation and passes the buck. */Datumrtinsert(PG_FUNCTION_ARGS){ Relation r = (Relation) PG_GETARG_POINTER(0); Datum *values = (Datum *) PG_GETARG_POINTER(1); bool *isnull = (bool *) PG_GETARG_POINTER(2); ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);#ifdef NOT_USED Relation heapRel = (Relation) PG_GETARG_POINTER(4); bool checkUnique = PG_GETARG_BOOL(5);#endif IndexTuple itup; RTSTATE rtState; /* generate an index tuple */ itup = index_form_tuple(RelationGetDescr(r), values, isnull); itup->t_tid = *ht_ctid; /* * Currently, rtrees do not support indexing NULLs; considerable * infrastructure work would have to be done to do anything reasonable * with a NULL. */ if (IndexTupleHasNulls(itup)) { pfree(itup); PG_RETURN_BOOL(false); } initRtstate(&rtState, r); /* * Since rtree is not marked "amconcurrent" in pg_am, caller should have * acquired exclusive lock on index relation. We need no locking here. */ rtdoinsert(r, itup, &rtState); PG_RETURN_BOOL(true);}static voidrtdoinsert(Relation r, IndexTuple itup, RTSTATE *rtstate){ Page page; Buffer buffer; BlockNumber blk; IndexTuple which; OffsetNumber l; RTSTACK *stack; RTreePageOpaque opaque; Datum datum; blk = P_ROOT; buffer = InvalidBuffer; stack = NULL; do { /* release the current buffer, read in the next one */ buffer = ReleaseAndReadBuffer(buffer, r, blk); page = (Page) BufferGetPage(buffer); opaque = (RTreePageOpaque) PageGetSpecialPointer(page); if (!(opaque->flags & F_LEAF)) { RTSTACK *n; ItemId iid; n = (RTSTACK *) palloc(sizeof(RTSTACK)); n->rts_parent = stack; n->rts_blk = blk; n->rts_child = choose(r, page, itup, rtstate); stack = n; iid = PageGetItemId(page, n->rts_child); which = (IndexTuple) PageGetItem(page, iid); blk = ItemPointerGetBlockNumber(&(which->t_tid)); } } while (!(opaque->flags & F_LEAF)); if (nospace(page, itup)) { /* need to do a split */ rtdosplit(r, buffer, stack, itup, rtstate); freestack(stack); WriteBuffer(buffer); /* don't forget to release buffer! */ return; } /* add the item and write the buffer */ if (PageIsEmpty(page)) { l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), FirstOffsetNumber, LP_USED); } else { l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), OffsetNumberNext(PageGetMaxOffsetNumber(page)), LP_USED); } if (l == InvalidOffsetNumber) elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(r)); WriteBuffer(buffer); datum = IndexTupleGetDatum(itup); /* now expand the page boundary in the parent to include the new child */ rttighten(r, stack, datum, IndexTupleAttSize(itup), rtstate); freestack(stack);}static voidrttighten(Relation r, RTSTACK *stk, Datum datum, int att_size, RTSTATE *rtstate){ Datum oldud; Datum tdatum; Page p; float old_size, newd_size; Buffer b; if (stk == NULL) return; b = ReadBuffer(r, stk->rts_blk); p = BufferGetPage(b); oldud = IndexTupleGetDatum(PageGetItem(p, PageGetItemId(p, stk->rts_child))); FunctionCall2(&rtstate->sizeFn, oldud, PointerGetDatum(&old_size)); datum = FunctionCall2(&rtstate->unionFn, oldud, datum); FunctionCall2(&rtstate->sizeFn, datum, PointerGetDatum(&newd_size)); /* * If newd_size == 0 we have degenerate rectangles, so we don't know if * there was any change, so we have to assume there was. */ if ((newd_size == 0) || (newd_size != old_size)) { TupleDesc td = RelationGetDescr(r); if (td->attrs[0]->attlen < 0) { /* * This is an internal page, so 'oldud' had better be a union * (constant-length) key, too. (See comment below.) */ Assert(VARSIZE(DatumGetPointer(datum)) == VARSIZE(DatumGetPointer(oldud))); memmove(DatumGetPointer(oldud), DatumGetPointer(datum), VARSIZE(DatumGetPointer(datum))); } else { memmove(DatumGetPointer(oldud), DatumGetPointer(datum), att_size); } WriteBuffer(b); /* * The user may be defining an index on variable-sized data (like * polygons). If so, we need to get a constant-sized datum for * insertion on the internal page. We do this by calling the union * proc, which is required to return a rectangle. */ tdatum = FunctionCall2(&rtstate->unionFn, datum, datum); rttighten(r, stk->rts_parent, tdatum, att_size, rtstate); pfree(DatumGetPointer(tdatum)); } else ReleaseBuffer(b); pfree(DatumGetPointer(datum));}/* * rtdosplit -- split a page in the tree. * * rtpicksplit does the interesting work of choosing the split. * This routine just does the bit-pushing. */static voidrtdosplit(Relation r, Buffer buffer, RTSTACK *stack, IndexTuple itup, RTSTATE *rtstate){ Page p; Buffer leftbuf, rightbuf; Page left, right; ItemId itemid; IndexTuple item; IndexTuple ltup, rtup; OffsetNumber maxoff; OffsetNumber i; OffsetNumber leftoff, rightoff; BlockNumber lbknum, rbknum; BlockNumber bufblock; RTreePageOpaque opaque; bool *isnull; SPLITVEC v; OffsetNumber *spl_left, *spl_right; TupleDesc tupDesc; int n; OffsetNumber newitemoff; p = (Page) BufferGetPage(buffer); opaque = (RTreePageOpaque) PageGetSpecialPointer(p); rtpicksplit(r, p, &v, itup, rtstate); /* * The root of the tree is the first block in the relation. If we're * about to split the root, we need to do some hocus-pocus to enforce this * guarantee. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -