3 #include "access/genam.h"
4 #include "access/gin.h"
5 #if PG_VERSION_NUM >= 90400
6 #include "utils/snapmgr.h"
8 #if PG_VERSION_NUM >= 90100
9 #include "access/gin_private.h"
11 #include "access/gist.h"
12 #include "access/gist_private.h"
13 #include "access/gistscan.h"
14 #if PG_VERSION_NUM >= 90200
15 #include "access/spgist_private.h"
16 #include "access/spgist.h"
17 #include "utils/datum.h"
19 #include "access/heapam.h"
20 #include "catalog/index.h"
21 #include "miscadmin.h"
22 #include "storage/lmgr.h"
23 #include "catalog/namespace.h"
24 #if PG_VERSION_NUM >= 80300
25 #include <tsearch/ts_utils.h>
27 #include <utils/tqual.h>
28 #include "utils/builtins.h"
29 #include "utils/lsyscache.h"
30 #include "utils/datum.h"
31 #include "utils/fmgroids.h"
34 #include <access/heapam.h>
35 #include <catalog/pg_type.h>
36 #include <access/relscan.h>
39 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
42 #define PG_NARGS() (fcinfo->nargs)
45 #if PG_VERSION_NUM >= 90600
47 #define ISNOTNULL false
48 #define heap_formtuple heap_form_tuple
56 char *out=palloc(VARSIZE(in));
58 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
59 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
70 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
72 #ifdef PG_MODULE_MAGIC
78 gist_index_open(RangeVar *relvar) {
79 #if PG_VERSION_NUM < 90200
80 Oid relOid = RangeVarGetRelid(relvar, false);
82 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
84 return checkOpenedRelation(
85 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
88 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
91 gin_index_open(RangeVar *relvar) {
92 #if PG_VERSION_NUM < 90200
93 Oid relOid = RangeVarGetRelid(relvar, false);
95 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
97 return checkOpenedRelation(
98 index_open(relOid, AccessShareLock), GIN_AM_OID);
101 #define gin_index_close(r) index_close((r), AccessShareLock)
106 gist_index_open(RangeVar *relvar) {
107 Relation rel = index_openrv(relvar);
109 LockRelation(rel, AccessExclusiveLock);
110 return checkOpenedRelation(rel, GIST_AM_OID);
114 gist_index_close(Relation rel) {
115 UnlockRelation(rel, AccessExclusiveLock);
120 gin_index_open(RangeVar *relvar) {
121 Relation rel = index_openrv(relvar);
123 LockRelation(rel, AccessShareLock);
124 return checkOpenedRelation(rel, GIN_AM_OID);
128 gin_index_close(Relation rel) {
129 UnlockRelation(rel, AccessShareLock);
135 #if PG_VERSION_NUM >= 80300
136 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
139 #if PG_VERSION_NUM < 80300
140 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
144 checkOpenedRelation(Relation r, Oid PgAmOid) {
145 if ( r->rd_am == NULL )
146 elog(ERROR, "Relation %s.%s is not an index",
147 get_namespace_name(RelationGetNamespace(r)),
148 RelationGetRelationName(r)
151 if ( r->rd_rel->relam != PgAmOid )
152 elog(ERROR, "Index %s.%s has wrong type",
153 get_namespace_name(RelationGetNamespace(r)),
154 RelationGetRelationName(r)
161 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
171 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
172 MemSet(pred, ' ', level*4);
173 pred[level*4] = '\0';
175 buffer = ReadBuffer(r, blk);
176 page = (Page) BufferGetPage(buffer);
178 maxoff = PageGetMaxOffsetNumber(page);
181 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
182 int dist=info->ptr-((char*)info->txt);
184 info->txt=(text*)repalloc(info->txt, info->len);
185 info->ptr = ((char*)info->txt)+dist;
188 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
194 (int) PageGetFreeSpace(page),
195 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
196 GistPageGetOpaque(page)->rightlink,
197 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
198 info->ptr=strchr(info->ptr,'\0');
200 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
201 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
202 iid = PageGetItemId(page, i);
203 which = (IndexTuple) PageGetItem(page, iid);
204 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
205 gist_dumptree(r, level + 1, cblk, i, info);
207 ReleaseBuffer(buffer);
211 PG_FUNCTION_INFO_V1(gist_tree);
212 Datum gist_tree(PG_FUNCTION_ARGS);
214 gist_tree(PG_FUNCTION_ARGS) {
215 text *name=PG_GETARG_TEXT_P(0);
216 char *relname=t2c(name);
222 relname_list = stringToQualifiedNameList(relname, "gist_tree");
223 relvar = makeRangeVarFromNameList(relname_list);
224 index = gist_index_open(relvar);
225 PG_FREE_IF_COPY(name,0);
227 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
229 info.txt=(text*)palloc( info.len );
230 info.ptr=((char*)info.txt)+VARHDRSZ;
232 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
234 gist_index_close(index);
237 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
238 PG_RETURN_POINTER(info.txt);
249 uint64 leaftuplesize;
254 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
264 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
265 MemSet(pred, ' ', level*4);
266 pred[level*4] = '\0';
268 buffer = ReadBuffer(r, blk);
269 page = (Page) BufferGetPage(buffer);
271 maxoff = PageGetMaxOffsetNumber(page);
274 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
275 info->totalsize+=BLCKSZ;
276 info->numtuple+=maxoff;
277 if ( info->level < level )
280 if (GistPageIsLeaf(page)) {
281 info->numleafpages++;
282 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
283 info->numleaftuple+=maxoff;
285 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
286 iid = PageGetItemId(page, i);
287 which = (IndexTuple) PageGetItem(page, iid);
288 if ( GistTupleIsInvalid(which) )
289 info->numinvalidtuple++;
290 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
291 gist_stattree(r, level + 1, cblk, i, info);
295 ReleaseBuffer(buffer);
299 PG_FUNCTION_INFO_V1(gist_stat);
300 Datum gist_stat(PG_FUNCTION_ARGS);
302 gist_stat(PG_FUNCTION_ARGS) {
303 text *name=PG_GETARG_TEXT_P(0);
304 char *relname=t2c(name);
309 text *out=(text*)palloc(1024);
310 char *ptr=((char*)out)+VARHDRSZ;
313 relname_list = stringToQualifiedNameList(relname, "gist_tree");
314 relvar = makeRangeVarFromNameList(relname_list);
315 index = gist_index_open(relvar);
316 PG_FREE_IF_COPY(name,0);
318 memset(&info, 0, sizeof(IdxStat));
320 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
322 gist_index_close(index);
326 "Number of levels: %d\n"
327 "Number of pages: %d\n"
328 "Number of leaf pages: %d\n"
329 "Number of tuples: %d\n"
330 "Number of invalid tuples: %d\n"
331 "Number of leaf tuples: %d\n"
332 "Total size of tuples: "INT64_FORMAT" bytes\n"
333 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
334 "Total size of index: "INT64_FORMAT" bytes\n",
339 info.numinvalidtuple,
345 ptr=strchr(ptr,'\0');
347 SET_VARSIZE(out, ptr-((char*)out));
348 PG_RETURN_POINTER(out);
351 typedef struct GPItem {
364 #if PG_VERSION_NUM >= 90600
373 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
375 MemoryContext oldcontext;
376 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
378 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
379 nitem = (GPItem*)palloc( sizeof(GPItem) );
380 memset(nitem,0,sizeof(GPItem));
382 nitem->buffer = ReadBuffer(index, blk);
383 nitem->page = (Page) BufferGetPage(nitem->buffer);
384 nitem->offset=FirstOffsetNumber;
385 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
386 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
387 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
389 MemoryContextSwitchTo(oldcontext);
394 closeGPPage( FuncCallContext *funcctx ) {
395 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
397 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
399 ReleaseBuffer(oitem->buffer);
401 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
405 setup_firstcall(FuncCallContext *funcctx, text *name) {
406 MemoryContext oldcontext;
408 char *relname=t2c(name);
410 char attname[NAMEDATALEN];
413 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
415 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
416 memset(st,0,sizeof(TypeStorage));
417 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
418 st->relvar = makeRangeVarFromNameList(st->relname_list);
419 st->index = gist_index_open(st->relvar);
420 funcctx->user_fctx = (void*)st;
422 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
423 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
424 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
425 for (i = 0; i < st->index->rd_att->natts; i++) {
426 sprintf(attname, "z%d", i+2);
431 st->index->rd_att->attrs[i]->atttypid,
432 st->index->rd_att->attrs[i]->atttypmod,
433 st->index->rd_att->attrs[i]->attndims
437 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
438 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(*st->nulls));
440 funcctx->slot = TupleDescGetSlot(tupdesc);
441 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
443 MemoryContextSwitchTo(oldcontext);
446 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
450 close_call( FuncCallContext *funcctx ) {
451 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
453 while(st->item && closeGPPage(funcctx));
458 gist_index_close(st->index);
461 PG_FUNCTION_INFO_V1(gist_print);
462 Datum gist_print(PG_FUNCTION_ARGS);
464 gist_print(PG_FUNCTION_ARGS) {
465 FuncCallContext *funcctx;
467 Datum result=(Datum)0;
474 if (SRF_IS_FIRSTCALL()) {
475 text *name=PG_GETARG_TEXT_P(0);
476 funcctx = SRF_FIRSTCALL_INIT();
477 setup_firstcall(funcctx, name);
478 PG_FREE_IF_COPY(name,0);
481 funcctx = SRF_PERCALL_SETUP();
482 st = (TypeStorage*)(funcctx->user_fctx);
486 SRF_RETURN_DONE(funcctx);
489 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
490 if ( ! closeGPPage(funcctx) ) {
492 SRF_RETURN_DONE(funcctx);
496 iid = PageGetItemId( st->item->page, st->item->offset );
497 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
499 st->dvalues[0] = Int32GetDatum( st->item->level );
500 st->nulls[0] = ISNOTNULL;
501 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
502 st->nulls[1] = ISNOTNULL;
503 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
504 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
505 st->dvalues[i] = (Datum)0;
506 st->nulls[i] = ISNULL;
508 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
509 st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
513 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
514 result = TupleGetDatum(funcctx->slot, htuple);
515 st->item->offset = OffsetNumberNext(st->item->offset);
516 if ( !GistPageIsLeaf(st->item->page) )
517 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
519 SRF_RETURN_NEXT(funcctx, result);
522 typedef struct GinStatState {
530 #if PG_VERSION_NUM >= 90100
531 GinNullCategory category;
538 moveRightIfItNeeded( GinStatState *st )
540 Page page = BufferGetPage(st->buffer);
542 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
544 * We scaned the whole page, so we should take right page
546 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
548 if ( GinPageRightMost(page) )
549 return false; /* no more page */
551 LockBuffer(st->buffer, GIN_UNLOCK);
552 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
553 LockBuffer(st->buffer, GIN_SHARE);
554 st->offset = FirstOffsetNumber;
561 * Refinds a previois position, at returns it has correctly
562 * set offset and buffer is locked
565 refindPosition(GinStatState *st)
569 /* find left if needed (it causes only for first search) */
574 LockBuffer(st->buffer, GIN_SHARE);
576 page = BufferGetPage(st->buffer);
577 if (GinPageIsLeaf(page))
580 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
581 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
583 LockBuffer(st->buffer,GIN_UNLOCK);
584 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
587 if (st->offset == InvalidOffsetNumber) {
588 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
593 #if PG_VERSION_NUM >= 90100
594 GinNullCategory category;
595 #elif PG_VERSION_NUM < 80400
601 if (moveRightIfItNeeded(st)==false)
604 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
605 #if PG_VERSION_NUM >= 90100
606 datum = gintuple_get_key(&st->ginstate, itup, &category);
607 cmp = ginCompareAttEntries(&st->ginstate,
608 st->attnum + 1, st->curval, st->category,
609 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
610 #elif PG_VERSION_NUM >= 80400
611 datum = gin_index_getattr(&st->ginstate, itup);
613 cmp = compareAttEntries(&st->ginstate,
614 st->attnum + 1, st->curval,
615 gintuple_get_attrnum(&st->ginstate, itup), datum);
617 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
621 &st->ginstate.compareFn,
628 if ( st->curval && !st->index->rd_att->attrs[st->attnum]->attbyval )
629 pfree( (void*) st->curval );
640 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
641 MemoryContext oldcontext;
643 char *relname=t2c(name);
646 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
648 st=(GinStatState*)palloc( sizeof(GinStatState) );
649 memset(st,0,sizeof(GinStatState));
650 st->index = gin_index_open(
651 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
652 initGinState( &st->ginstate, st->index );
654 #if PG_VERSION_NUM >= 80400
655 if (attnum < 0 || attnum >= st->index->rd_att->natts)
656 elog(ERROR,"Wrong column's number");
662 funcctx->user_fctx = (void*)st;
664 tupdesc = CreateTemplateTupleDesc(2, false);
665 TupleDescInitEntry(tupdesc, 1, "value",
666 st->index->rd_att->attrs[st->attnum]->atttypid,
667 st->index->rd_att->attrs[st->attnum]->atttypmod,
668 st->index->rd_att->attrs[st->attnum]->attndims);
669 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
671 memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
673 funcctx->slot = TupleDescGetSlot(tupdesc);
674 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
676 MemoryContextSwitchTo(oldcontext);
679 st->offset = InvalidOffsetNumber;
680 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
684 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
685 MemoryContext oldcontext;
686 #if PG_VERSION_NUM >= 90100
688 #elif PG_VERSION_NUM < 80400
692 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
694 #if PG_VERSION_NUM >= 90100
695 key = gintuple_get_key(&st->ginstate, itup, &st->category);
697 if (st->category != GIN_CAT_NORM_KEY)
698 st->curval = (Datum)0;
701 st->curval = datumCopy(
702 #if PG_VERSION_NUM >= 90100
704 #elif PG_VERSION_NUM >= 80400
705 gin_index_getattr(&st->ginstate, itup),
707 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
709 st->index->rd_att->attrs[st->attnum]->attbyval,
710 st->index->rd_att->attrs[st->attnum]->attlen );
711 MemoryContextSwitchTo(oldcontext);
713 st->dvalues[0] = st->curval;
714 #if PG_VERSION_NUM >= 90100
715 /* do no distiguish various null category */
716 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
719 if ( GinIsPostingTree(itup) ) {
720 BlockNumber rootblkno = GinGetPostingTree(itup);
721 #if PG_VERSION_NUM >= 90400
723 GinBtreeStack *stack;
724 ItemPointerData minItem;
728 GinPostingTreeScan *gdi;
732 uint32 predictNumber;
734 LockBuffer(st->buffer, GIN_UNLOCK);
735 #if PG_VERSION_NUM >= 90400
736 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
737 page = BufferGetPage(stack->buffer);
738 ItemPointerSetMin(&minItem);
739 list = GinDataLeafPageGetItems(page, &nlist, minItem);
741 predictNumber = stack->predictNumber;
742 st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
743 #elif PG_VERSION_NUM >= 90100
744 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
745 entrybuffer = ginScanBeginPostingTree(gdi);
746 page = BufferGetPage(entrybuffer);
747 predictNumber = gdi->stack->predictNumber;
748 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
750 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
751 entrybuffer = scanBeginPostingTree(gdi);
752 page = BufferGetPage(entrybuffer);
753 predictNumber = gdi->stack->predictNumber;
754 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
757 #if PG_VERSION_NUM < 90400
758 LockBuffer(entrybuffer, GIN_UNLOCK);
759 freeGinBtreeStack(gdi->stack);
762 LockBuffer(stack->buffer, GIN_UNLOCK);
763 freeGinBtreeStack(stack);
766 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
767 LockBuffer(st->buffer, GIN_UNLOCK);
771 PG_FUNCTION_INFO_V1(gin_stat);
772 Datum gin_stat(PG_FUNCTION_ARGS);
774 gin_stat(PG_FUNCTION_ARGS) {
775 FuncCallContext *funcctx;
777 Datum result=(Datum)0;
782 if (SRF_IS_FIRSTCALL()) {
783 text *name=PG_GETARG_TEXT_P(0);
784 funcctx = SRF_FIRSTCALL_INIT();
785 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
786 PG_FREE_IF_COPY(name,0);
789 funcctx = SRF_PERCALL_SETUP();
790 st = (GinStatState*)(funcctx->user_fctx);
792 if ( refindPosition(st) == false ) {
793 UnlockReleaseBuffer( st->buffer );
794 gin_index_close(st->index);
796 SRF_RETURN_DONE(funcctx);
802 if (moveRightIfItNeeded(st)==false) {
803 UnlockReleaseBuffer( st->buffer );
804 gin_index_close(st->index);
806 SRF_RETURN_DONE(funcctx);
809 page = BufferGetPage(st->buffer);
810 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
812 #if PG_VERSION_NUM >= 80400
813 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
818 processTuple( funcctx, st, ituple );
820 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
821 result = TupleGetDatum(funcctx->slot, htuple);
823 SRF_RETURN_NEXT(funcctx, result);
826 PG_FUNCTION_INFO_V1(gin_count_estimate);
827 Datum gin_count_estimate(PG_FUNCTION_ARGS);
828 #if PG_VERSION_NUM >= 80300
830 gin_count_estimate(PG_FUNCTION_ARGS) {
831 text *name=PG_GETARG_TEXT_P(0);
835 char *relname=t2c(name);
837 #if PG_VERSION_NUM >= 80400
838 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
841 ItemPointerData tids[MAXTIDS];
846 index = gin_index_open(
847 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
849 if ( index->rd_opcintype[0] != TSVECTOROID ) {
850 gin_index_close(index);
851 elog(ERROR, "Column type is not a tsvector");
856 key.sk_strategy = TSearchStrategyNumber;
858 key.sk_argument = PG_GETARG_DATUM(1);
860 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
862 #if PG_VERSION_NUM >= 90100
863 #if PG_VERSION_NUM >= 90400
864 scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
866 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
868 index_rescan(scan, &key, 1, NULL, 0);
870 count = index_getbitmap(scan, bitmap);
872 #elif PG_VERSION_NUM >= 80400
873 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
875 count = index_getbitmap(scan, bitmap);
878 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
881 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
882 count += returned_tids;
886 index_endscan( scan );
887 gin_index_close(index);
889 PG_RETURN_INT64(count);
893 gin_count_estimate(PG_FUNCTION_ARGS) {
894 elog(NOTICE, "Function is not working under PgSQL < 8.3");
900 PG_FUNCTION_INFO_V1(spgist_stat);
901 Datum spgist_stat(PG_FUNCTION_ARGS);
903 spgist_stat(PG_FUNCTION_ARGS)
905 #if PG_VERSION_NUM < 90200
906 elog(NOTICE, "Function is not working under PgSQL < 9.2");
908 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
910 text *name = PG_GETARG_TEXT_P(0);
914 BlockNumber totalPages = 0,
919 double usedSpace = 0.0,
921 usedInnerSpace = 0.0;
924 int64 innerTuples = 0,
927 nLeafPlaceholder = 0,
928 nInnerPlaceholder = 0,
932 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
933 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
935 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
936 index = relation_openrv(relvar, AccessExclusiveLock);
938 if (!IS_INDEX(index) || !IS_SPGIST(index))
939 elog(ERROR, "relation \"%s\" is not an SPGiST index",
940 RelationGetRelationName(index));
942 totalPages = RelationGetNumberOfBlocks(index);
944 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
950 buffer = ReadBuffer(index, blkno);
951 LockBuffer(buffer, BUFFER_LOCK_SHARE);
953 page = BufferGetPage(buffer);
955 if (PageIsNew(page) || SpGistPageIsDeleted(page))
958 UnlockReleaseBuffer(buffer);
962 if (SpGistPageIsLeaf(page))
965 leafTuples += PageGetMaxOffsetNumber(page);
966 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
967 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
975 max = PageGetMaxOffsetNumber(page);
977 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
978 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
979 for (i = FirstOffsetNumber; i <= max; i++)
983 it = (SpGistInnerTuple) PageGetItem(page,
984 PageGetItemId(page, i));
991 bufferSize = BufferGetPageSize(buffer)
992 - MAXALIGN(sizeof(SpGistPageOpaqueData))
993 - SizeOfPageHeaderData;
995 pageFree = PageGetExactFreeSpace(page);
997 usedSpace += bufferSize - pageFree;
998 if (SpGistPageIsLeaf(page))
999 usedLeafSpace += bufferSize - pageFree;
1001 usedInnerSpace += bufferSize - pageFree;
1003 if (pageFree == bufferSize)
1006 UnlockReleaseBuffer(buffer);
1009 index_close(index, AccessExclusiveLock);
1011 totalPages--; /* discount metapage */
1013 snprintf(res, sizeof(res),
1015 "deletedPages: %u\n"
1019 "usedSpace: %.2f kbytes\n"
1020 "usedInnerSpace: %.2f kbytes\n"
1021 "usedLeafSpace: %.2f kbytes\n"
1022 "freeSpace: %.2f kbytes\n"
1023 "fillRatio: %.2f%%\n"
1024 "leafTuples: " INT64_FORMAT "\n"
1025 "innerTuples: " INT64_FORMAT "\n"
1026 "innerAllTheSame: " INT64_FORMAT "\n"
1027 "leafPlaceholders: " INT64_FORMAT "\n"
1028 "innerPlaceholders: " INT64_FORMAT "\n"
1029 "leafRedirects: " INT64_FORMAT "\n"
1030 "innerRedirects: " INT64_FORMAT,
1031 totalPages, deletedPages, innerPages, leafPages, emptyPages,
1033 usedInnerSpace / 1024.0,
1034 usedLeafSpace / 1024.0,
1035 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1036 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1037 leafTuples, innerTuples, nAllTheSame,
1038 nLeafPlaceholder, nInnerPlaceholder,
1039 nLeafRedirect, nInnerRedirect);
1041 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1045 #if PG_VERSION_NUM >= 90200
1047 typedef struct SPGistPrintStackElem {
1048 ItemPointerData iptr;
1051 } SPGistPrintStackElem;
1053 typedef struct SPGistPrint {
1056 Datum dvalues[8 /* see CreateTemplateTupleDesc call */];
1057 char nulls[8 /* see CreateTemplateTupleDesc call */];
1062 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1063 MemoryContext oldcontext;
1064 SPGistPrintStackElem *e;
1066 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1068 e = palloc(sizeof(*e));
1072 prst->stack = lcons(e, prst->stack);
1074 MemoryContextSwitchTo(oldcontext);
1078 close_spgist_print(SPGistPrint *prst) {
1079 index_close(prst->index, AccessExclusiveLock);
1083 PG_FUNCTION_INFO_V1(spgist_print);
1084 Datum spgist_print(PG_FUNCTION_ARGS);
1086 spgist_print(PG_FUNCTION_ARGS)
1088 #if PG_VERSION_NUM < 90200
1089 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1091 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1093 FuncCallContext *funcctx;
1095 SPGistPrintStackElem *s;
1098 MemoryContext oldcontext;
1100 if (SRF_IS_FIRSTCALL()) {
1101 text *name=PG_GETARG_TEXT_P(0);
1104 ItemPointerData ipd;
1107 funcctx = SRF_FIRSTCALL_INIT();
1108 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1109 index = relation_openrv(relvar, AccessExclusiveLock);
1111 if (!IS_INDEX(index) || !IS_SPGIST(index))
1112 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1113 RelationGetRelationName(index));
1115 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1117 prst = palloc(sizeof(*prst));
1119 prst->index = index;
1120 initSpGistState(&prst->state, index);
1122 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
1123 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1124 TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
1125 TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
1126 TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
1127 TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
1128 TupleDescInitEntry(tupdesc, 6, "prefix",
1129 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1130 TupleDescInitEntry(tupdesc, 7, "label",
1131 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1132 TupleDescInitEntry(tupdesc, 8, "leaf",
1133 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1135 funcctx->slot = TupleDescGetSlot(tupdesc);
1136 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1138 funcctx->user_fctx = (void*)prst;
1140 MemoryContextSwitchTo(oldcontext);
1142 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1144 pushSPGistPrint(funcctx, prst, &ipd, 1);
1146 PG_FREE_IF_COPY(name,0);
1149 funcctx = SRF_PERCALL_SETUP();
1150 prst = (SPGistPrint*)(funcctx->user_fctx);
1154 if ( prst->stack == NIL ) {
1155 close_spgist_print(prst);
1156 SRF_RETURN_DONE(funcctx);
1159 CHECK_FOR_INTERRUPTS();
1161 s = (SPGistPrintStackElem*)linitial(prst->stack);
1162 prst->stack = list_delete_first(prst->stack);
1164 if (ItemPointerIsValid(&s->iptr))
1172 SpGistDeadTuple dtuple;
1175 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1176 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1178 page = BufferGetPage(buffer);
1179 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1180 UnlockReleaseBuffer(buffer);
1185 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1187 if (dtuple->tupstate != SPGIST_LIVE) {
1188 UnlockReleaseBuffer(buffer);
1193 if (SpGistPageIsLeaf(page)) {
1194 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1196 tid = palloc(sizeof(ItemPointerData));
1198 prst->dvalues[0] = PointerGetDatum(tid);
1199 prst->nulls[0] = ISNOTNULL;
1200 prst->nulls[1] = ISNULL;
1201 prst->nulls[2] = ISNULL;
1202 prst->dvalues[3] = s->level;
1203 prst->nulls[3] = ISNOTNULL;
1204 prst->nulls[4] = ISNULL;
1205 prst->nulls[5] = ISNULL;
1206 prst->nulls[6] = ISNULL;
1207 prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1208 prst->state.attType.attbyval, prst->state.attType.attlen);
1209 prst->nulls[7] = ISNOTNULL;
1211 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1213 SpGistNodeTuple node;
1215 SGITITERATE(innerTuple, i, node) {
1216 if (ItemPointerIsValid(&node->t_tid)) {
1222 if (i >= innerTuple->nNodes) {
1223 UnlockReleaseBuffer(buffer);
1228 tid = palloc(sizeof(ItemPointerData));
1230 prst->dvalues[0] = PointerGetDatum(tid);
1231 prst->nulls[0] = ISNOTNULL;
1232 prst->dvalues[1] = innerTuple->allTheSame;
1233 prst->nulls[1] = ISNOTNULL;
1234 prst->dvalues[2] = Int32GetDatum(s->nlabel);
1235 prst->nulls[2] = ISNOTNULL;
1236 prst->dvalues[3] = s->level;
1237 prst->nulls[3] = ISNOTNULL;
1238 tid = palloc(sizeof(ItemPointerData));
1240 prst->dvalues[4] = PointerGetDatum(tid);
1241 prst->nulls[5] = ISNOTNULL;
1242 if (innerTuple->prefixSize > 0) {
1243 prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1244 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1245 prst->nulls[5] = ISNOTNULL;
1247 prst->nulls[5] = ISNULL;
1248 if (!IndexTupleHasNulls(node)) {
1249 prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
1250 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1251 prst->nulls[6] = ISNOTNULL;
1253 prst->nulls[6] = ISNULL;
1254 prst->nulls[7] = ISNULL;
1256 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1258 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1259 prst->stack = lcons(s, prst->stack);
1260 MemoryContextSwitchTo(oldcontext);
1264 UnlockReleaseBuffer(buffer);
1268 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1269 result = TupleGetDatum(funcctx->slot, htuple);
1271 SRF_RETURN_NEXT(funcctx, result);
1276 PG_FUNCTION_INFO_V1(gin_statpage);
1277 Datum gin_statpage(PG_FUNCTION_ARGS);
1279 gin_statpage(PG_FUNCTION_ARGS)
1281 #if PG_VERSION_NUM < 90400
1282 elog(NOTICE, "Function is not working under PgSQL < 9.4");
1284 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1286 text *name = PG_GETARG_TEXT_P(0);
1296 entryInnerPages = 0,
1299 uint64 dataInnerFreeSpace = 0,
1300 dataLeafFreeSpace = 0,
1301 dataInnerTuplesCount = 0,
1302 dataLeafIptrsCount = 0,
1303 entryInnerFreeSpace = 0,
1304 entryLeafFreeSpace = 0,
1305 entryInnerTuplesCount = 0,
1306 entryLeafTuplesCount = 0,
1307 entryPostingSize = 0,
1308 entryPostingCount = 0,
1312 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1313 index = relation_openrv(relvar, AccessExclusiveLock);
1315 if (index->rd_rel->relkind != RELKIND_INDEX ||
1316 index->rd_rel->relam != GIN_AM_OID)
1317 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1318 RelationGetRelationName(index));
1320 totalPages = RelationGetNumberOfBlocks(index);
1322 for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1328 buffer = ReadBuffer(index, blkno);
1329 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1331 page = BufferGetPage(buffer);
1332 header = (PageHeader)page;
1334 if (GinPageIsData(page))
1337 if (GinPageIsLeaf(page))
1339 ItemPointerData minItem;
1343 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1344 ItemPointerSetMin(&minItem);
1345 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1346 dataLeafIptrsCount += nlist;
1351 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1352 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1358 OffsetNumber i, maxoff;
1360 maxoff = PageGetMaxOffsetNumber(page);
1363 if (GinPageIsLeaf(page))
1366 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1367 entryLeafTuplesCount += maxoff;
1372 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1373 entryInnerTuplesCount += maxoff;
1376 for (i = 1; i <= maxoff; i++)
1378 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1380 if (GinPageIsLeaf(page))
1382 GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1383 entryPostingCount += GinGetNPosting(itup);
1384 entryPostingSize += SizeOfGinPostingList(list);
1385 entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1389 entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1394 UnlockReleaseBuffer(buffer);
1397 index_close(index, AccessExclusiveLock);
1400 snprintf(res, sizeof(res),
1403 "dataInnerPages: %u\n"
1404 "dataLeafPages: %u\n"
1405 "dataInnerFreeSpace: " INT64_FORMAT "\n"
1406 "dataLeafFreeSpace: " INT64_FORMAT "\n"
1407 "dataInnerTuplesCount: " INT64_FORMAT "\n"
1408 "dataLeafIptrsCount: " INT64_FORMAT "\n"
1410 "entryInnerPages: %u\n"
1411 "entryLeafPages: %u\n"
1412 "entryInnerFreeSpace: " INT64_FORMAT "\n"
1413 "entryLeafFreeSpace: " INT64_FORMAT "\n"
1414 "entryInnerTuplesCount: " INT64_FORMAT "\n"
1415 "entryLeafTuplesCount: " INT64_FORMAT "\n"
1416 "entryPostingSize: " INT64_FORMAT "\n"
1417 "entryPostingCount: " INT64_FORMAT "\n"
1418 "entryAttrSize: " INT64_FORMAT "\n"
1426 dataInnerTuplesCount,
1431 entryInnerFreeSpace,
1433 entryInnerTuplesCount,
1434 entryLeafTuplesCount,
1440 PG_RETURN_TEXT_P(CStringGetTextDatum(res));