怎样建设网站的步骤,wordpress4.2 for sae,安阳哪个公司做网站好,小程序开发源码GiST索引
PostgreSQL版本为8.4.1 #xff08;本文为《PostgreSQL数据库内核分析》一书的总结笔记#xff0c;需要电子版的可私信我#xff09;
GiST#xff08;Generalized Search Tree#xff0c;通用搜索树#xff09;是一种平衡的、树状结构的访问方法。
它在系统中…GiST索引
PostgreSQL版本为8.4.1 本文为《PostgreSQL数据库内核分析》一书的总结笔记需要电子版的可私信我
GiSTGeneralized Search Tree通用搜索树是一种平衡的、树状结构的访问方法。
它在系统中相当于一个基础的模板几乎可以使用它实现任意索引模式。B-trees、R-trees 和许多其他的索引模式都可以用GiST实现。它可以建立一种可扩展的索引结构包括数据类型和查询谓词的扩展。
GiST 允许用户并非数据库专家开发自己的数据类型并通过相应的访问方法来在该数据类型上使用GiST索引。通常实现一种新的索引访问方法意味着大量艰苦的工作。因为必须理解数据库的内部工作机制比如锁的机制和预写式日志。GiST 接口提供了一个高层的抽象只要求访问方法的实现者实现被访问数据类型的语义。GiST层本身会处理并发、日志和搜索树结构的任务。
GiST的代码分布在\src\backend\access\gist目录下包括了GiST索引的创建、查找、删除等代码。
组织结构
GiST是一棵平衡树除根节点的子树数目在2M之间外每个节点的子树数目在k*MM之间常量k称作该树的最小填充因子满足2/M≤k≤1/2M为一个节点可以容纳索引项的最大数目。 索引项形式为(pptr)其中p是搜索的谓词。在叶子节点中ptr为指向数据库中某一元组的指针而在非叶子节点中ptr为指向其子树节点的指针。 在图4-27中SP1、SP2 ( subtree predicates是指用于分隔数据的谓词。可以看出GiST 的结构和B-Tree 索引的结构有一定的相似性。 GiST内置实现了索引项查询、插入和删除等算法。
用户通过定义索引项并提供与索引项管理有关的方法便可以实现某一特定的索引结构。
这些方法包括 Consistent(Eq)对于给定的索引项E (pptr和查询谓词q判断索引项E是否与查询谓词q匹配若肯定不能匹配则返回假否则返回真。 Union(P)对于给定集合Р中索引项(p1ptr1)…(pnptrn)返回谓词r满足(p1V…Vpn)→rr代表了ptr1 到ptrn所指向的所有记录。 Same(E1E2)如果两个条目相同返回真否则返回假。 Penalty(E1E2)给定两个索引项E1 (p1ptr1)和E2(p2ptr2)返回值为将E2插入到以E1为根的子树中时的惩罚值表示插入的代价。 这用来辅助Split和Insert 算法有利于将一个记录对应的索引项插入到最适合的位置该插入引起的惩罚值最小。 PickSplit(P)对于包含M1个索引项(pptr)的集合Р而言将Р划分为两个集合P1和P2每个集合至少包含k*M个索引项。该方法确定了节点分裂的原则。
Compress(E)对于给定的索引项(pptr)返回(πptr)π为p的压缩形式。Decompress(E)对于给定的压缩表示索引项(πptr)π Compress §返回(r,ptr)r满足p→r。(可能为有损压缩并不要求p←→r)。
GiST索引的实现
由于GiST 已内置实现了索引项的创建、查找和删除等操作而这些操作都依赖于4.4.2节中介绍的7个方法因此用户只需要实现这7种方法。而索引的创建、查找等PostgreSQL自动会进行扩展。下面将对数据库如何使用这7种方法来实现对数据库的操作进行分析。
GiST索引创建
函数gistbuild
Postgres 中 GiST索引的创建由函数gistbuild完成实现流程如图4-28所示。 在索引创建过程中索引元组的插入在函数gistdoinsert中完成该函数将一个索引元组插入到索引结构中。
函数gistdoinsert
其实现过程是先从搜索树的根节点开始遍历找到插入代价最小由Penalty方法实现的叶子节点进行插入。若叶子节点已满插入新索引项会导致叶子节点的分裂也可能造成分裂的向上传播。分裂时将调用用户定义的PickSplit方法来决定新老节点中索引项的布局。而在向上更新描述谓词时将会调用Union方法来确定父节点相应索引项中的描述谓词。其执行流程如图4-29所示。 static void
gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
{GISTInsertState state;memset(state, 0, sizeof(GISTInsertState));state.itup (IndexTuple *) palloc(sizeof(IndexTuple));state.itup[0] (IndexTuple) palloc(IndexTupleSize(itup));memcpy(state.itup[0], itup, IndexTupleSize(itup));state.ituplen 1;state.freespace freespace;state.r r;state.key itup-t_tid;state.needInsertComplete true;state.stack (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));state.stack-blkno GIST_ROOT_BLKNO;gistfindleaf(state, giststate);gistmakedeal(state, giststate);
}函数gistfindleaf
从根节点往下查找到待插入的节点
static void
gistfindleaf(GISTInsertState *state, GISTSTATE *giststate)
{ItemId iid;IndexTuple idxtuple;GISTPageOpaque opaque;/** walk down, We dont lock page for a long time, but so we should be* ready to recheck path in a bad case... We remember, that page-lsn* should never be invalid.*/for (;;){if (XLogRecPtrIsInvalid(state-stack-lsn))state-stack-buffer ReadBuffer(state-r, state-stack-blkno);LockBuffer(state-stack-buffer, GIST_SHARE);gistcheckpage(state-r, state-stack-buffer);state-stack-page (Page) BufferGetPage(state-stack-buffer);opaque GistPageGetOpaque(state-stack-page);state-stack-lsn PageGetLSN(state-stack-page);Assert(state-r-rd_istemp || !XLogRecPtrIsInvalid(state-stack-lsn));if (state-stack-blkno ! GIST_ROOT_BLKNO XLByteLT(state-stack-parent-lsn, opaque-nsn)){/** caused split non-root page is detected, go up to parent to* choose best child*/UnlockReleaseBuffer(state-stack-buffer);state-stack state-stack-parent;continue;}if (!GistPageIsLeaf(state-stack-page)){/** This is an internal page, so continue to walk down the tree. We* find the child node that has the minimum insertion penalty and* recursively invoke ourselves to modify that node. Once the* recursive call returns, we may need to adjust the parent node* for two reasons: the child node split, or the key in this node* needs to be adjusted for the newly inserted key below us.*/GISTInsertStack *item (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));// gistchoose 找到插入代价最小的那个节点调用了gistpenalty方法state-stack-childoffnum gistchoose(state-r, state-stack-page, state-itup[0], giststate);iid PageGetItemId(state-stack-page, state-stack-childoffnum);idxtuple (IndexTuple) PageGetItem(state-stack-page, iid);item-blkno ItemPointerGetBlockNumber((idxtuple-t_tid));LockBuffer(state-stack-buffer, GIST_UNLOCK);item-parent state-stack;item-child NULL;if (state-stack)state-stack-child item;state-stack item;// 继续向下循环}else{/* be carefull, during unlock/lock page may be changed... */LockBuffer(state-stack-buffer, GIST_UNLOCK);LockBuffer(state-stack-buffer, GIST_EXCLUSIVE);state-stack-page (Page) BufferGetPage(state-stack-buffer);opaque GistPageGetOpaque(state-stack-page);if (state-stack-blkno GIST_ROOT_BLKNO){/** the only page can become inner instead of leaf is a root* page, so for root we should recheck it*/if (!GistPageIsLeaf(state-stack-page)){/** very rarely situation: during unlock/lock index with* number of pages 1 was increased*/LockBuffer(state-stack-buffer, GIST_UNLOCK);continue;}/** we dont need to check root split, because checking* leaf/inner is enough to recognize split for root*/}else if (XLByteLT(state-stack-parent-lsn, opaque-nsn)){/** detecting split during unlock/lock, so we should find* better child on parent*//* forget buffer */UnlockReleaseBuffer(state-stack-buffer);state-stack state-stack-parent;continue;}state-stack-lsn PageGetLSN(state-stack-page);/* ok we found a leaf page and it X-locked */break;}}/* now state-stack-(page, buffer and blkno) points to leaf page */
}GISTInsertStack结构
在插入索引元组的过程中会生成一个类型为GISTInsertStack的栈结构用于记录当前插入节点及其父节点的相关信息。通过该结构能够找到当前插入节点的父节点当插入节点发生分裂需要插入新节点到父节点中或更新父节点的信息时通过该栈保存的信息能够依次向上层进行调整。
其结构如数据结构4.12所示。 函数gistmakedeal
插入索引节点并调整索引结构
void
gistmakedeal(GISTInsertState *state, GISTSTATE *giststate)
{int is_splitted;ItemId iid;IndexTuple oldtup,newtup;/* walk up */while (true){/** After this call: 1. if child page was splited, then itup contains* keys for each page 2. if child page wasnt splited, then itup* contains additional for adjustment of current key*/if (state-stack-parent){/** X-lock parent page before proceed child, gistFindCorrectParent* should find and lock it*/gistFindCorrectParent(state-r, state-stack);}// gistplacetopage会判断待插入节点是否有足够空间// 空间不够就split通过gistSplit和unionis_splitted gistplacetopage(state, giststate);/* parent locked above, so release child buffer */UnlockReleaseBuffer(state-stack-buffer);/* pop parent page from stack */state-stack state-stack-parent;/* stack is void */if (!state-stack)break;/** child did not split, so we can check is it needed to update parent* tuple*/if (!is_splitted){/* parents tuple */iid PageGetItemId(state-stack-page, state-stack-childoffnum);oldtup (IndexTuple) PageGetItem(state-stack-page, iid);// 通过Union判断是否需要更新父节点的key需要的话就会返回新keynewtup gistgetadjusted(state-r, oldtup, state-itup[0], giststate);if (!newtup){ /* not need to update key */LockBuffer(state-stack-buffer, GIST_UNLOCK);break;}state-itup[0] newtup;}} /* while *//* release all parent buffers */while (state-stack){ReleaseBuffer(state-stack-buffer);state-stack state-stack-parent;}/* say to xlog that insert is completed */if (state-needInsertComplete !state-r-rd_istemp)gistxlogInsertCompletion(state-r-rd_node, (state-key), 1);
}GiST索引查询
GiST索引的查询流程与B-Tree索引类似从根节点开始按深度优先原则自上而下进行检索 若当前节点R是内部节点遍历检查R上每个索引项E是否与检索谓词q相符合 对于满足Consistent(E,q)的索引项递归向下检索以 E.ptr 为根的子树。 若R是叶子节点遍历检查R上每个索引项E是否满足Consistent(Eq) 对于满足Consistent(Eq)的索引项则通过 E.ptr 取得相应记录与q进行准确匹配 将匹配成功的记录放结果集中。
GiST索引查询主要通过函数gistnext来实现该函数通过从上往下搜索索引结构使用上面的方法找到结果。
GISTSearchStack栈结构
在扫描的过程中会生成一个类型为GISTSearchStack栈结构用于保存扫描过程中内部节点里面满足前述的Consistent方法的节点如果扫描的是内部节点且找到满足Consistent方法的元组则将该元组指向的下一层的节点入栈。
其数据结构见数据结构4.13所示 GistNSN结构
typedef XLogRecPtr GistNSN;typedef struct XLogRecPtr
{uint32 xlogid; /* log file #, 0 based */uint32 xrecoff; /* byte offset of location in log file */
} XLogRecPtr;
/*
结构定义描述了一个名为XLogRecPtr的结构体它用于表示在XLOGWALWrite-Ahead Logging中的位置指针。XLOG是PostgreSQL中用于持久化数据更改的一种方法它确保在数据库崩溃时数据的持久性和一致性。以下是对结构体成员的解释uint32 xlogid: 这个成员表示日志文件的编号从0开始计数。在XLOG中有一系列日志文件每个文件都有一个唯一的编号这个编号在XLogRecPtr结构中表示。每个XLOG文件实际上是XLogSegSize一般是16MB大小的一个“段”。uint32 xrecoff: 这个成员表示在日志文件中的字节偏移量。它指示了指定XLOG文件中的具体位置。当组合起来与xlogid时它标识了一个唯一的物理XLOG文件。分段号和文件内偏移量可以通过将xrecoff除以XLogSegSize来计算,结果整数部分是分段号余数是段内偏移量。这个结构中的两个成员组合起来提供了对XLOG中特定位置的唯一标识。当数据库系统需要记录某个事务提交的位置时就会使用这种结构来表示。通常这些指针被用来跟踪数据库的事务日志以支持持久性和故障恢复。
*/函数gistnext
执行完 gistnext 函数后即得到结果集。 GISTScanOpaqueData结构跟踪索引扫描时的父节点堆栈记录当前页面和被标记了的页面的信息
/** When were doing a scan, we need to keep track of the parent stack* for the marked and current items.*/
typedef struct GISTScanOpaqueData
{GISTSearchStack *stack;GISTSearchStack *markstk;bool qual_ok; /* false if qual can never be satisfied */GISTSTATE *giststate;MemoryContext tempCxt;Buffer curbuf;ItemPointerData curpos;ItemResult pageData[BLCKSZ / sizeof(IndexTupleData)];OffsetNumber nPageData;OffsetNumber curPageData;
} GISTScanOpaqueData;typedef GISTScanOpaqueData *GISTScanOpaque;static int64
gistnext(IndexScanDesc scan, TIDBitmap *tbm)
{Page p;OffsetNumber n;GISTScanOpaque so;GISTSearchStack *stk;IndexTuple it;GISTPageOpaque opaque;int64 ntids 0;so (GISTScanOpaque) scan-opaque;// opaque是void*类型的是一种通用的表示索引的特定方法if (so-qual_ok false)return 0;if (so-curbuf InvalidBuffer){if (ItemPointerIsValid(so-curpos) false){/* Being asked to fetch the first entry, so start at the root */Assert(so-curbuf InvalidBuffer);Assert(so-stack NULL);so-curbuf ReadBuffer(scan-indexRelation, GIST_ROOT_BLKNO);stk so-stack (GISTSearchStack *) palloc0(sizeof(GISTSearchStack));stk-next NULL;stk-block GIST_ROOT_BLKNO;pgstat_count_index_scan(scan-indexRelation);}else{/* scan is finished */return 0;}}/** check stored pointers from last visit*/if (so-nPageData 0){/** gistgetmulti never should go here*/Assert(tbm NULL);if (so-curPageData so-nPageData){scan-xs_ctup.t_self so-pageData[so-curPageData].heapPtr;scan-xs_recheck so-pageData[so-curPageData].recheck;ItemPointerSet(so-curpos,BufferGetBlockNumber(so-curbuf),so-pageData[so-curPageData].pageOffset);so-curPageData;return 1;}else{/** Go to the next page*/stk so-stack-next;pfree(so-stack);so-stack stk;/* If were out of stack entries, were done */if (so-stack NULL){ReleaseBuffer(so-curbuf);so-curbuf InvalidBuffer;return 0;}so-curbuf ReleaseAndReadBuffer(so-curbuf,scan-indexRelation,stk-block);}}for (;;)// 外层循环遍历每个节点通过so-stack的next指针一直往后{CHECK_FOR_INTERRUPTS();/* First of all, we need lock buffer */Assert(so-curbuf ! InvalidBuffer);LockBuffer(so-curbuf, GIST_SHARE);gistcheckpage(scan-indexRelation, so-curbuf);p BufferGetPage(so-curbuf);opaque GistPageGetOpaque(p);/* remember lsn to identify page changed for tuples killing */so-stack-lsn PageGetLSN(p);/* check page split, occured since visit to parent */if (!XLogRecPtrIsInvalid(so-stack-parentlsn) XLByteLT(so-stack-parentlsn, opaque-nsn) opaque-rightlink ! InvalidBlockNumber /* sanity check */ (so-stack-next NULL || so-stack-next-block ! opaque-rightlink) /* check if alreadyadded */ ){/* detect page split, follow right link to add pages */stk (GISTSearchStack *) palloc(sizeof(GISTSearchStack));stk-next so-stack-next;stk-block opaque-rightlink;stk-parentlsn so-stack-parentlsn;memset((stk-lsn), 0, sizeof(GistNSN));so-stack-next stk;}/* if page is empty, then just skip it */if (PageIsEmpty(p)){LockBuffer(so-curbuf, GIST_UNLOCK);stk so-stack-next;pfree(so-stack);so-stack stk;if (so-stack NULL){ReleaseBuffer(so-curbuf);so-curbuf InvalidBuffer;return ntids;}so-curbuf ReleaseAndReadBuffer(so-curbuf, scan-indexRelation,stk-block);continue;}n FirstOffsetNumber;/* wonderful, we can look at page */so-nPageData so-curPageData 0;for (;;)// 内层循环遍历节点内的索引项{n gistfindnext(scan, n);if (!OffsetNumberIsValid(n)){/** If we was called from gistgettuple and current buffer* contains something matched then make a recursive call - it* will return ItemPointer from so-pageData. But we save* buffer pinned to support tuples killing*/if (!tbm so-nPageData 0){LockBuffer(so-curbuf, GIST_UNLOCK);return gistnext(scan, NULL);}/** We ran out of matching index entries on the current page,* so pop the top stack entry and use it to continue the* search.*/LockBuffer(so-curbuf, GIST_UNLOCK);stk so-stack-next;pfree(so-stack);so-stack stk;/* If were out of stack entries, were done */if (so-stack NULL){ReleaseBuffer(so-curbuf);so-curbuf InvalidBuffer;return ntids;}so-curbuf ReleaseAndReadBuffer(so-curbuf,scan-indexRelation,stk-block);/* XXX go up */break;}if (GistPageIsLeaf(p)){/** Weve found a matching index entry in a leaf page, so* return success. Note that we keep curbuf pinned so that* we can efficiently resume the index scan later.*/if (!(scan-ignore_killed_tuples ItemIdIsDead(PageGetItemId(p, n)))){it (IndexTuple) PageGetItem(p, PageGetItemId(p, n));ntids;if (tbm ! NULL)// 将匹配的元组放入结果集tbm_add_tuples(tbm, it-t_tid, 1, scan-xs_recheck);else{so-pageData[so-nPageData].heapPtr it-t_tid;so-pageData[so-nPageData].pageOffset n;so-pageData[so-nPageData].recheck scan-xs_recheck;so-nPageData;}}}else{/** Weve found an entry in an internal node whose key is* consistent with the search key, so push it to stack*/stk (GISTSearchStack *) palloc(sizeof(GISTSearchStack));it (IndexTuple) PageGetItem(p, PageGetItemId(p, n));stk-block ItemPointerGetBlockNumber((it-t_tid));memset((stk-lsn), 0, sizeof(GistNSN));stk-parentlsn so-stack-lsn;stk-next so-stack-next;so-stack-next stk;}n OffsetNumberNext(n);}}return ntids;
}函数gistfindnext
返回与当前页中偏移量’n’后的搜索key一致的第一个索引项的偏移量。gistnext函数会循环调用它 如果没有一致的条目则返回InvalidoffsetNumber。 如果执行成功则scan→xs_recheck的设置也正确。
/** Return the offset of the first index entry that is consistent with* the search key after offset n in the current page. If there are* no more consistent entries, return InvalidOffsetNumber.* On success, scan-xs_recheck is set correctly, too.* Page should be locked....*/
static OffsetNumber
gistfindnext(IndexScanDesc scan, OffsetNumber n)
{OffsetNumber maxoff;IndexTuple it;GISTScanOpaque so;MemoryContext oldcxt;Page p;so (GISTScanOpaque) scan-opaque;p BufferGetPage(so-curbuf);maxoff PageGetMaxOffsetNumber(p);/** Make sure were in a short-lived memory context when we invoke a* user-supplied GiST method in gistindex_keytest(), so we dont leak* memory*/oldcxt MemoryContextSwitchTo(so-tempCxt);while (n FirstOffsetNumber n maxoff){it (IndexTuple) PageGetItem(p, PageGetItemId(p, n));if (gistindex_keytest(it, scan, n))break;n OffsetNumberNext(n);}MemoryContextSwitchTo(oldcxt);MemoryContextReset(so-tempCxt);/** If we found a matching entry, return its offset; otherwise return* InvalidOffsetNumber to inform the caller to go to the next page.*/if (n FirstOffsetNumber n maxoff)return n;elsereturn InvalidOffsetNumber;
}/* gistindex_keytest()——判断这个索引元组是否满足扫描键
成功返回叶子元组时scan-xs_recheck被设置为是否需要重新检查。
我们重新检查是否有Consistent()函数请求它。
在将key传递给scan-keyData-sk_func之前我们必须解压IndexTuple中的键(我们之前已经覆盖了sk func以使用用户定义的一致方法因此实际上我们正在调用它)。
请注意该函数总是在短期内存上下文中调用因此我们不需要担心清理已分配的内存无论是在这里还是在任何一致的方法的实现中。
*/GiST索引删除
与B-Tree索引类似当表元组被删除时GiST索引中与之对应的索引元组不是立即被删除而是由VACUUM操作来批量完成无效索引元组的清除。
若是普通的VACUUMLazy Vacuum则只更新FSM (“空闲空间映射表”然后后续可被新元组覆盖实际并不删除索引结构中索引元组如果是Full VACUUM则需要修改索引页面中的元组信息。
函数gistvacuumcleanup
在删除过程中首先找到叶子节点中需删除的索引项然后从叶子节点往上回溯更新索引若删除索引项后存在空节点则删除该节点。
Datum
gistvacuumcleanup(PG_FUNCTION_ARGS)
{IndexVacuumInfo *info (IndexVacuumInfo *) PG_GETARG_POINTER(0);GistBulkDeleteResult *stats (GistBulkDeleteResult *) PG_GETARG_POINTER(1);Relation rel info-index;BlockNumber npages,blkno;BlockNumber totFreePages;BlockNumber lastBlock GIST_ROOT_BLKNO,lastFilledBlock GIST_ROOT_BLKNO;bool needLock;/* No-op in ANALYZE ONLY mode */if (info-analyze_only)PG_RETURN_POINTER(stats);/* Set up all-zero stats if gistbulkdelete wasnt called */if (stats NULL){stats (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));/* use heaps tuple count */stats-std.num_index_tuples info-num_heap_tuples;stats-std.estimated_count info-estimated_count;/** XXX the above is wrong if index is partial. Would it be OK to just* return NULL, or is there work we must do below?*/}/* gistVacuumUpdate may cause hard work */if (info-vacuum_full){GistVacuum gv;ArrayTuple res;/* note: vacuum.c already acquired AccessExclusiveLock on index */gv.index rel;initGISTstate((gv.giststate), rel);gv.opCtx createTempGistContext();gv.result stats;gv.strategy info-strategy;/* walk through the entire index for update tuples */res gistVacuumUpdate(gv, GIST_ROOT_BLKNO, false);/* cleanup */if (res.itup){int i;for (i 0; i res.ituplen; i)pfree(res.itup[i]);pfree(res.itup);}freeGISTstate((gv.giststate));MemoryContextDelete(gv.opCtx);}else if (stats-needFullVacuum)ereport(NOTICE,(errmsg(index \%s\ needs VACUUM FULL or REINDEX to finish crash recovery,RelationGetRelationName(rel))));/** If vacuum full, we already have exclusive lock on the index. Otherwise,* need lock unless its local to this backend.*/if (info-vacuum_full)needLock false;elseneedLock !RELATION_IS_LOCAL(rel);/* try to find deleted pages */if (needLock)LockRelationForExtension(rel, ExclusiveLock);npages RelationGetNumberOfBlocks(rel);if (needLock)UnlockRelationForExtension(rel, ExclusiveLock);totFreePages 0;// 遍历索引节点记录空闲的节点for (blkno GIST_ROOT_BLKNO 1; blkno npages; blkno){Buffer buffer;Page page;vacuum_delay_point();buffer ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,info-strategy);LockBuffer(buffer, GIST_SHARE);page (Page) BufferGetPage(buffer);if (PageIsNew(page) || GistPageIsDeleted(page))// 说明这个索引节点是新的或者是已删除的空闲{totFreePages;RecordFreeIndexPage(rel, blkno);// mark a page as free in the FSM}elselastFilledBlock blkno;UnlockReleaseBuffer(buffer);}lastBlock npages - 1;if (info-vacuum_full lastFilledBlock lastBlock){ /* try to truncate index */RelationTruncate(rel, lastFilledBlock 1);stats-std.pages_removed lastBlock - lastFilledBlock;totFreePages totFreePages - stats-std.pages_removed;}/* Finally, vacuum the FSM 以深度优先顺序遍历索引树。*/IndexFreeSpaceMapVacuum(info-index);/* return statistics */stats-std.pages_free totFreePages;if (needLock)LockRelationForExtension(rel, ExclusiveLock);stats-std.num_pages RelationGetNumberOfBlocks(rel);if (needLock)UnlockRelationForExtension(rel, ExclusiveLock);PG_RETURN_POINTER(stats);
}扩展
PostgreSQL 8.4.1内建了对box (矩形)、polygon (多边形)和circle (圆形) 等数据类型的GiST索引的支持
可以在postgresql-8.4.1\src\backend\access\gist\gistproc.c文件中查看到相关代码。这些数据类型可以直接创建GiST索引其他数据类型如果需要创建GiST索引则需要用户手动添加。
在postgresql-8.4.1\contrib文件夹下提供了很多扩展的模块其中也有关于GiST的模块如 cube文件夹下是用于多维立方体的GiST索引 ltree文件夹下是用于树状结构的GiST索引等。编译这些模块就可以使数据库支持该种数据类型同时可以在该类型上创建GiST索引。
下面就以多维立方体cube文件夹下的扩展模块为例讲解如何将其添加到数据库系统中。
查看cube文件夹下的文件可以看出这些文件已经编写好了增加一种数据类型、相应的操作符、该数据类型的支持函数、以及GiST索引对该数据类型的支持。那么只需要将这些信息编译进数据库即可。主要包括以下几个步骤 执行make编译C代码得到cube. so链接库。 这一步主要完成的工作是将该种数据类型所需要的支持函数、GiST索引需要实现的7种函数方法编译到链接库里供数据库调用。 执行make install 这时系统就会自动将刚才得到的链接库添加到数据库中然后执行新生成cube. sql添加相关命令到数据库系统中。 当执行完成make命令之后可以看到在当前目录下新生成了一个文件cube.sql该文件是修改了cube.sql.in得到的将make得到的cube.so链接库的目录写入了。若读者感兴趣也可以自己手动将cube.sql.in 中的命令添加到数据库系统中只需在执行make之后执行以下命令 修改cube.sql.in文件。 该文件的主要功能是向数据库系统中添加数据类型pg.type 系统表、添加该数据类型及索引的操作符pg_opelass、pg_amop、pg_operator系统表以及支持函数pa_amproc系统表由于这些函数都要链接到刚才编译得到的cube.so所以需要修改cube.sql.in文件里cube. so文件的路径。 登录一个数据库然后执行cube.sql.in语句PostgreSQL会自动执行cube.sql.in 里所有的语句将相关信息添加到数据库系统中。
通过以上四个步骤就完成了向数据库系统里添加cube数据类型、操作符、相关函数及其对GiST索引类型的支持下面就可以像使用数据库自带的数据类型一样使用cube类型。如首先创建一个表
CREATE TABLE test_cube (
name varchar,
cub cube);插入一些数据后即可在该表的cub字段上创建GiST索引并使用该索引进行查找
CREATE INDEX test_cube_ix ON test_cube USING gist (cub);
SELECT * FROM test_cube WHERE cub (3000,1000),(0,0) ;执行make install 这时系统就会自动将刚才得到的链接库添加到数据库中然后执行新生成cube. sql添加相关命令到数据库系统中。 当执行完成make命令之后可以看到在当前目录下新生成了一个文件cube.sql该文件是修改了cube.sql.in得到的将make得到的cube.so链接库的目录写入了。若读者感兴趣也可以自己手动将cube.sql.in 中的命令添加到数据库系统中只需在执行make之后执行以下命令 修改cube.sql.in文件。 该文件的主要功能是向数据库系统中添加数据类型pg.type 系统表、添加该数据类型及索引的操作符pg_opelass、pg_amop、pg_operator系统表以及支持函数pa_amproc系统表由于这些函数都要链接到刚才编译得到的cube.so所以需要修改cube.sql.in文件里cube. so文件的路径。 登录一个数据库然后执行cube.sql.in语句PostgreSQL会自动执行cube.sql.in 里所有的语句将相关信息添加到数据库系统中。
通过以上四个步骤就完成了向数据库系统里添加cube数据类型、操作符、相关函数及其对GiST索引类型的支持下面就可以像使用数据库自带的数据类型一样使用cube类型。如首先创建一个表
CREATE TABLE test_cube (
name varchar,
cub cube);插入一些数据后即可在该表的cub字段上创建GiST索引并使用该索引进行查找
CREATE INDEX test_cube_ix ON test_cube USING gist (cub);
SELECT * FROM test_cube WHERE cub (3000,1000),(0,0) ;如果想了解具体添加了哪些信息到数据库中可以查看cube\cube.sql.in文件。