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)
47 char *out=palloc( VARSIZE(in) );
48 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
49 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
60 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
62 #ifdef PG_MODULE_MAGIC
68 gist_index_open(RangeVar *relvar) {
69 #if PG_VERSION_NUM < 90200
70 Oid relOid = RangeVarGetRelid(relvar, false);
72 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
74 return checkOpenedRelation(
75 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
78 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
81 gin_index_open(RangeVar *relvar) {
82 #if PG_VERSION_NUM < 90200
83 Oid relOid = RangeVarGetRelid(relvar, false);
85 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
87 return checkOpenedRelation(
88 index_open(relOid, AccessShareLock), GIN_AM_OID);
91 #define gin_index_close(r) index_close((r), AccessShareLock)
96 gist_index_open(RangeVar *relvar) {
97 Relation rel = index_openrv(relvar);
99 LockRelation(rel, AccessExclusiveLock);
100 return checkOpenedRelation(rel, GIST_AM_OID);
104 gist_index_close(Relation rel) {
105 UnlockRelation(rel, AccessExclusiveLock);
110 gin_index_open(RangeVar *relvar) {
111 Relation rel = index_openrv(relvar);
113 LockRelation(rel, AccessShareLock);
114 return checkOpenedRelation(rel, GIN_AM_OID);
118 gin_index_close(Relation rel) {
119 UnlockRelation(rel, AccessShareLock);
125 #if PG_VERSION_NUM >= 80300
126 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
129 #if PG_VERSION_NUM < 80300
130 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
134 checkOpenedRelation(Relation r, Oid PgAmOid) {
135 if ( r->rd_am == NULL )
136 elog(ERROR, "Relation %s.%s is not an index",
137 get_namespace_name(RelationGetNamespace(r)),
138 RelationGetRelationName(r)
141 if ( r->rd_rel->relam != PgAmOid )
142 elog(ERROR, "Index %s.%s has wrong type",
143 get_namespace_name(RelationGetNamespace(r)),
144 RelationGetRelationName(r)
151 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
161 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
162 MemSet(pred, ' ', level*4);
163 pred[level*4] = '\0';
165 buffer = ReadBuffer(r, blk);
166 page = (Page) BufferGetPage(buffer);
168 maxoff = PageGetMaxOffsetNumber(page);
171 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
172 int dist=info->ptr-((char*)info->txt);
174 info->txt=(text*)repalloc(info->txt, info->len);
175 info->ptr = ((char*)info->txt)+dist;
178 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
184 (int) PageGetFreeSpace(page),
185 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
186 GistPageGetOpaque(page)->rightlink,
187 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
188 info->ptr=strchr(info->ptr,'\0');
190 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
191 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
192 iid = PageGetItemId(page, i);
193 which = (IndexTuple) PageGetItem(page, iid);
194 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
195 gist_dumptree(r, level + 1, cblk, i, info);
197 ReleaseBuffer(buffer);
201 PG_FUNCTION_INFO_V1(gist_tree);
202 Datum gist_tree(PG_FUNCTION_ARGS);
204 gist_tree(PG_FUNCTION_ARGS) {
205 text *name=PG_GETARG_TEXT_P(0);
206 char *relname=t2c(name);
212 relname_list = stringToQualifiedNameList(relname, "gist_tree");
213 relvar = makeRangeVarFromNameList(relname_list);
214 index = gist_index_open(relvar);
215 PG_FREE_IF_COPY(name,0);
217 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
219 info.txt=(text*)palloc( info.len );
220 info.ptr=((char*)info.txt)+VARHDRSZ;
222 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
224 gist_index_close(index);
227 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
228 PG_RETURN_POINTER(info.txt);
239 uint64 leaftuplesize;
244 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
254 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
255 MemSet(pred, ' ', level*4);
256 pred[level*4] = '\0';
258 buffer = ReadBuffer(r, blk);
259 page = (Page) BufferGetPage(buffer);
261 maxoff = PageGetMaxOffsetNumber(page);
264 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
265 info->totalsize+=BLCKSZ;
266 info->numtuple+=maxoff;
267 if ( info->level < level )
270 if (GistPageIsLeaf(page)) {
271 info->numleafpages++;
272 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
273 info->numleaftuple+=maxoff;
275 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
276 iid = PageGetItemId(page, i);
277 which = (IndexTuple) PageGetItem(page, iid);
278 if ( GistTupleIsInvalid(which) )
279 info->numinvalidtuple++;
280 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
281 gist_stattree(r, level + 1, cblk, i, info);
285 ReleaseBuffer(buffer);
289 PG_FUNCTION_INFO_V1(gist_stat);
290 Datum gist_stat(PG_FUNCTION_ARGS);
292 gist_stat(PG_FUNCTION_ARGS) {
293 text *name=PG_GETARG_TEXT_P(0);
294 char *relname=t2c(name);
299 text *out=(text*)palloc(1024);
300 char *ptr=((char*)out)+VARHDRSZ;
303 relname_list = stringToQualifiedNameList(relname, "gist_tree");
304 relvar = makeRangeVarFromNameList(relname_list);
305 index = gist_index_open(relvar);
306 PG_FREE_IF_COPY(name,0);
308 memset(&info, 0, sizeof(IdxStat));
310 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
312 gist_index_close(index);
316 "Number of levels: %d\n"
317 "Number of pages: %d\n"
318 "Number of leaf pages: %d\n"
319 "Number of tuples: %d\n"
320 "Number of invalid tuples: %d\n"
321 "Number of leaf tuples: %d\n"
322 "Total size of tuples: "INT64_FORMAT" bytes\n"
323 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
324 "Total size of index: "INT64_FORMAT" bytes\n",
329 info.numinvalidtuple,
335 ptr=strchr(ptr,'\0');
337 SET_VARSIZE(out, ptr-((char*)out));
338 PG_RETURN_POINTER(out);
341 typedef struct GPItem {
359 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
361 MemoryContext oldcontext;
362 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
364 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
365 nitem = (GPItem*)palloc( sizeof(GPItem) );
366 memset(nitem,0,sizeof(GPItem));
368 nitem->buffer = ReadBuffer(index, blk);
369 nitem->page = (Page) BufferGetPage(nitem->buffer);
370 nitem->offset=FirstOffsetNumber;
371 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
372 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
373 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
375 MemoryContextSwitchTo(oldcontext);
380 closeGPPage( FuncCallContext *funcctx ) {
381 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
383 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
385 ReleaseBuffer(oitem->buffer);
387 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
391 setup_firstcall(FuncCallContext *funcctx, text *name) {
392 MemoryContext oldcontext;
394 char *relname=t2c(name);
396 char attname[NAMEDATALEN];
399 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
401 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
402 memset(st,0,sizeof(TypeStorage));
403 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
404 st->relvar = makeRangeVarFromNameList(st->relname_list);
405 st->index = gist_index_open(st->relvar);
406 funcctx->user_fctx = (void*)st;
408 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
409 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
410 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
411 for (i = 0; i < st->index->rd_att->natts; i++) {
412 sprintf(attname, "z%d", i+2);
417 st->index->rd_att->attrs[i]->atttypid,
418 st->index->rd_att->attrs[i]->atttypmod,
419 st->index->rd_att->attrs[i]->attndims
423 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
424 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
426 funcctx->slot = TupleDescGetSlot(tupdesc);
427 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
429 MemoryContextSwitchTo(oldcontext);
432 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
436 close_call( FuncCallContext *funcctx ) {
437 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
439 while( st->item && closeGPPage(funcctx) );
441 pfree( st->dvalues );
444 gist_index_close(st->index);
447 PG_FUNCTION_INFO_V1(gist_print);
448 Datum gist_print(PG_FUNCTION_ARGS);
450 gist_print(PG_FUNCTION_ARGS) {
451 FuncCallContext *funcctx;
453 Datum result=(Datum)0;
460 if (SRF_IS_FIRSTCALL()) {
461 text *name=PG_GETARG_TEXT_P(0);
462 funcctx = SRF_FIRSTCALL_INIT();
463 setup_firstcall(funcctx, name);
464 PG_FREE_IF_COPY(name,0);
467 funcctx = SRF_PERCALL_SETUP();
468 st = (TypeStorage*)(funcctx->user_fctx);
472 SRF_RETURN_DONE(funcctx);
475 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
476 if ( ! closeGPPage(funcctx) ) {
478 SRF_RETURN_DONE(funcctx);
482 iid = PageGetItemId( st->item->page, st->item->offset );
483 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
485 st->dvalues[0] = Int32GetDatum( st->item->level );
487 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
489 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
490 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
491 st->dvalues[i] = (Datum)0;
494 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
495 st->nulls[i] = ( isnull ) ? 'n' : ' ';
499 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
500 result = TupleGetDatum(funcctx->slot, htuple);
501 st->item->offset = OffsetNumberNext(st->item->offset);
502 if ( !GistPageIsLeaf(st->item->page) )
503 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
505 SRF_RETURN_NEXT(funcctx, result);
508 typedef struct GinStatState {
516 #if PG_VERSION_NUM >= 90100
517 GinNullCategory category;
524 moveRightIfItNeeded( GinStatState *st )
526 Page page = BufferGetPage(st->buffer);
528 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
530 * We scaned the whole page, so we should take right page
532 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
534 if ( GinPageRightMost(page) )
535 return false; /* no more page */
537 LockBuffer(st->buffer, GIN_UNLOCK);
538 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
539 LockBuffer(st->buffer, GIN_SHARE);
540 st->offset = FirstOffsetNumber;
547 * Refinds a previois position, at returns it has correctly
548 * set offset and buffer is locked
551 refindPosition(GinStatState *st)
555 /* find left if needed (it causes only for first search) */
560 LockBuffer(st->buffer, GIN_SHARE);
562 page = BufferGetPage(st->buffer);
563 if (GinPageIsLeaf(page))
566 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
567 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
569 LockBuffer(st->buffer,GIN_UNLOCK);
570 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
573 if (st->offset == InvalidOffsetNumber) {
574 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
579 #if PG_VERSION_NUM >= 90100
580 GinNullCategory category;
581 #elif PG_VERSION_NUM < 80400
587 if (moveRightIfItNeeded(st)==false)
590 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
591 #if PG_VERSION_NUM >= 90100
592 datum = gintuple_get_key(&st->ginstate, itup, &category);
593 cmp = ginCompareAttEntries(&st->ginstate,
594 st->attnum + 1, st->curval, st->category,
595 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
596 #elif PG_VERSION_NUM >= 80400
597 datum = gin_index_getattr(&st->ginstate, itup);
599 cmp = compareAttEntries(&st->ginstate,
600 st->attnum + 1, st->curval,
601 gintuple_get_attrnum(&st->ginstate, itup), datum);
603 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
607 &st->ginstate.compareFn,
614 if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
615 pfree( (void*) st->curval );
626 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
627 MemoryContext oldcontext;
629 char *relname=t2c(name);
632 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
634 st=(GinStatState*)palloc( sizeof(GinStatState) );
635 memset(st,0,sizeof(GinStatState));
636 st->index = gin_index_open(
637 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
638 initGinState( &st->ginstate, st->index );
640 #if PG_VERSION_NUM >= 80400
641 if (attnum < 0 || attnum >= st->index->rd_att->natts)
642 elog(ERROR,"Wrong column's number");
648 funcctx->user_fctx = (void*)st;
650 tupdesc = CreateTemplateTupleDesc(2, false);
651 TupleDescInitEntry(tupdesc, 1, "value",
652 st->index->rd_att->attrs[st->attnum]->atttypid,
653 st->index->rd_att->attrs[st->attnum]->atttypmod,
654 st->index->rd_att->attrs[st->attnum]->attndims);
655 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
657 memset( st->nulls, ' ', 2*sizeof(char) );
659 funcctx->slot = TupleDescGetSlot(tupdesc);
660 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
662 MemoryContextSwitchTo(oldcontext);
665 st->offset = InvalidOffsetNumber;
666 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
670 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
671 MemoryContext oldcontext;
672 #if PG_VERSION_NUM < 80400
676 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
677 st->curval = datumCopy(
678 #if PG_VERSION_NUM >= 90100
679 gintuple_get_key(&st->ginstate, itup, &st->category),
680 #elif PG_VERSION_NUM >= 80400
681 gin_index_getattr(&st->ginstate, itup),
683 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
685 st->index->rd_att->attrs[st->attnum]->attbyval,
686 st->index->rd_att->attrs[st->attnum]->attlen );
687 MemoryContextSwitchTo(oldcontext);
689 st->dvalues[0] = st->curval;
690 #if PG_VERSION_NUM >= 90100
691 /* do no distiguish various null category */
692 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n';
695 if ( GinIsPostingTree(itup) ) {
696 BlockNumber rootblkno = GinGetPostingTree(itup);
697 #if PG_VERSION_NUM >= 90400
699 GinBtreeStack *stack;
700 ItemPointerData minItem;
704 GinPostingTreeScan *gdi;
708 uint32 predictNumber;
710 LockBuffer(st->buffer, GIN_UNLOCK);
711 #if PG_VERSION_NUM >= 90400
712 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
713 page = BufferGetPage(stack->buffer);
714 ItemPointerSetMin(&minItem);
715 list = GinDataLeafPageGetItems(page, &nlist, minItem);
717 predictNumber = stack->predictNumber;
718 #elif PG_VERSION_NUM >= 90100
719 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
720 entrybuffer = ginScanBeginPostingTree(gdi);
721 page = BufferGetPage(entrybuffer);
722 predictNumber = gdi->stack->predictNumber;
724 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
725 entrybuffer = scanBeginPostingTree(gdi);
726 page = BufferGetPage(entrybuffer);
727 predictNumber = gdi->stack->predictNumber;
730 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
732 #if PG_VERSION_NUM < 90400
733 LockBuffer(entrybuffer, GIN_UNLOCK);
734 freeGinBtreeStack(gdi->stack);
737 LockBuffer(stack->buffer, GIN_UNLOCK);
738 freeGinBtreeStack(stack);
741 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
742 LockBuffer(st->buffer, GIN_UNLOCK);
746 PG_FUNCTION_INFO_V1(gin_stat);
747 Datum gin_stat(PG_FUNCTION_ARGS);
749 gin_stat(PG_FUNCTION_ARGS) {
750 FuncCallContext *funcctx;
752 Datum result=(Datum)0;
757 if (SRF_IS_FIRSTCALL()) {
758 text *name=PG_GETARG_TEXT_P(0);
759 funcctx = SRF_FIRSTCALL_INIT();
760 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
761 PG_FREE_IF_COPY(name,0);
764 funcctx = SRF_PERCALL_SETUP();
765 st = (GinStatState*)(funcctx->user_fctx);
767 if ( refindPosition(st) == false ) {
768 UnlockReleaseBuffer( st->buffer );
769 gin_index_close(st->index);
771 SRF_RETURN_DONE(funcctx);
777 if (moveRightIfItNeeded(st)==false) {
778 UnlockReleaseBuffer( st->buffer );
779 gin_index_close(st->index);
781 SRF_RETURN_DONE(funcctx);
784 page = BufferGetPage(st->buffer);
785 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
787 #if PG_VERSION_NUM >= 80400
788 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
793 processTuple( funcctx, st, ituple );
795 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
796 result = TupleGetDatum(funcctx->slot, htuple);
798 SRF_RETURN_NEXT(funcctx, result);
801 PG_FUNCTION_INFO_V1(gin_count_estimate);
802 Datum gin_count_estimate(PG_FUNCTION_ARGS);
803 #if PG_VERSION_NUM >= 80300
805 gin_count_estimate(PG_FUNCTION_ARGS) {
806 text *name=PG_GETARG_TEXT_P(0);
810 char *relname=t2c(name);
812 #if PG_VERSION_NUM >= 80400
813 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
816 ItemPointerData tids[MAXTIDS];
821 index = gin_index_open(
822 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
824 if ( index->rd_opcintype[0] != TSVECTOROID ) {
825 gin_index_close(index);
826 elog(ERROR, "Column type is not a tsvector");
831 key.sk_strategy = TSearchStrategyNumber;
833 key.sk_argument = PG_GETARG_DATUM(1);
835 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
837 #if PG_VERSION_NUM >= 90100
838 #if PG_VERSION_NUM >= 90400
839 scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
841 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
843 index_rescan(scan, &key, 1, NULL, 0);
845 count = index_getbitmap(scan, bitmap);
847 #elif PG_VERSION_NUM >= 80400
848 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
850 count = index_getbitmap(scan, bitmap);
853 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
856 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
857 count += returned_tids;
861 index_endscan( scan );
862 gin_index_close(index);
864 PG_RETURN_INT64(count);
868 gin_count_estimate(PG_FUNCTION_ARGS) {
869 elog(NOTICE, "Function is not working under PgSQL < 8.3");
875 PG_FUNCTION_INFO_V1(spgist_stat);
876 Datum spgist_stat(PG_FUNCTION_ARGS);
878 spgist_stat(PG_FUNCTION_ARGS)
880 #if PG_VERSION_NUM < 90200
881 elog(NOTICE, "Function is not working under PgSQL < 9.2");
883 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
885 text *name = PG_GETARG_TEXT_P(0);
889 BlockNumber totalPages = 0,
894 double usedSpace = 0.0,
896 usedInnerSpace = 0.0;
899 int64 innerTuples = 0,
902 nLeafPlaceholder = 0,
903 nInnerPlaceholder = 0,
907 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
908 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
910 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
911 index = relation_openrv(relvar, AccessExclusiveLock);
913 if (!IS_INDEX(index) || !IS_SPGIST(index))
914 elog(ERROR, "relation \"%s\" is not an SPGiST index",
915 RelationGetRelationName(index));
917 totalPages = RelationGetNumberOfBlocks(index);
919 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
925 buffer = ReadBuffer(index, blkno);
926 LockBuffer(buffer, BUFFER_LOCK_SHARE);
928 page = BufferGetPage(buffer);
930 if (PageIsNew(page) || SpGistPageIsDeleted(page))
933 UnlockReleaseBuffer(buffer);
937 if (SpGistPageIsLeaf(page))
940 leafTuples += PageGetMaxOffsetNumber(page);
941 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
942 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
950 max = PageGetMaxOffsetNumber(page);
952 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
953 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
954 for (i = FirstOffsetNumber; i <= max; i++)
958 it = (SpGistInnerTuple) PageGetItem(page,
959 PageGetItemId(page, i));
966 bufferSize = BufferGetPageSize(buffer)
967 - MAXALIGN(sizeof(SpGistPageOpaqueData))
968 - SizeOfPageHeaderData;
970 pageFree = PageGetExactFreeSpace(page);
972 usedSpace += bufferSize - pageFree;
973 if (SpGistPageIsLeaf(page))
974 usedLeafSpace += bufferSize - pageFree;
976 usedInnerSpace += bufferSize - pageFree;
978 if (pageFree == bufferSize)
981 UnlockReleaseBuffer(buffer);
984 index_close(index, AccessExclusiveLock);
986 totalPages--; /* discount metapage */
988 snprintf(res, sizeof(res),
994 "usedSpace: %.2f kbytes\n"
995 "usedInnerSpace: %.2f kbytes\n"
996 "usedLeafSpace: %.2f kbytes\n"
997 "freeSpace: %.2f kbytes\n"
998 "fillRatio: %.2f%%\n"
999 "leafTuples: " INT64_FORMAT "\n"
1000 "innerTuples: " INT64_FORMAT "\n"
1001 "innerAllTheSame: " INT64_FORMAT "\n"
1002 "leafPlaceholders: " INT64_FORMAT "\n"
1003 "innerPlaceholders: " INT64_FORMAT "\n"
1004 "leafRedirects: " INT64_FORMAT "\n"
1005 "innerRedirects: " INT64_FORMAT,
1006 totalPages, deletedPages, innerPages, leafPages, emptyPages,
1008 usedInnerSpace / 1024.0,
1009 usedLeafSpace / 1024.0,
1010 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1011 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1012 leafTuples, innerTuples, nAllTheSame,
1013 nLeafPlaceholder, nInnerPlaceholder,
1014 nLeafRedirect, nInnerRedirect);
1016 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1020 #if PG_VERSION_NUM >= 90200
1022 typedef struct SPGistPrintStackElem {
1023 ItemPointerData iptr;
1026 } SPGistPrintStackElem;
1028 typedef struct SPGistPrint {
1031 Datum dvalues[7 /* see CreateTemplateTupleDesc call */];
1032 char nulls[7 /* see CreateTemplateTupleDesc call */];
1037 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1038 MemoryContext oldcontext;
1039 SPGistPrintStackElem *e;
1041 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1043 e = palloc(sizeof(*e));
1047 prst->stack = lcons(e, prst->stack);
1049 MemoryContextSwitchTo(oldcontext);
1053 close_spgist_print(SPGistPrint *prst) {
1054 index_close(prst->index, AccessExclusiveLock);
1058 PG_FUNCTION_INFO_V1(spgist_print);
1059 Datum spgist_print(PG_FUNCTION_ARGS);
1061 spgist_print(PG_FUNCTION_ARGS)
1063 #if PG_VERSION_NUM < 90200
1064 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1066 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1068 FuncCallContext *funcctx;
1070 SPGistPrintStackElem *s;
1073 MemoryContext oldcontext;
1075 if (SRF_IS_FIRSTCALL()) {
1076 text *name=PG_GETARG_TEXT_P(0);
1079 ItemPointerData ipd;
1082 funcctx = SRF_FIRSTCALL_INIT();
1083 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1084 index = relation_openrv(relvar, AccessExclusiveLock);
1086 if (!IS_INDEX(index) || !IS_SPGIST(index))
1087 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1088 RelationGetRelationName(index));
1090 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1092 prst = palloc(sizeof(*prst));
1094 prst->index = index;
1095 initSpGistState(&prst->state, index);
1097 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */, false);
1098 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1099 TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0);
1100 TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0);
1101 TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0);
1102 TupleDescInitEntry(tupdesc, 5, "prefix",
1103 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1104 TupleDescInitEntry(tupdesc, 6, "label",
1105 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1106 TupleDescInitEntry(tupdesc, 7, "leaf",
1107 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1109 funcctx->slot = TupleDescGetSlot(tupdesc);
1110 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1112 funcctx->user_fctx = (void*)prst;
1114 MemoryContextSwitchTo(oldcontext);
1116 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1118 pushSPGistPrint(funcctx, prst, &ipd, 1);
1120 PG_FREE_IF_COPY(name,0);
1123 funcctx = SRF_PERCALL_SETUP();
1124 prst = (SPGistPrint*)(funcctx->user_fctx);
1128 if ( prst->stack == NIL ) {
1129 close_spgist_print(prst);
1130 SRF_RETURN_DONE(funcctx);
1133 CHECK_FOR_INTERRUPTS();
1135 s = (SPGistPrintStackElem*)linitial(prst->stack);
1136 prst->stack = list_delete_first(prst->stack);
1138 if (ItemPointerIsValid(&s->iptr))
1146 SpGistDeadTuple dtuple;
1149 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1150 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1152 page = BufferGetPage(buffer);
1153 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1154 UnlockReleaseBuffer(buffer);
1159 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1161 if (dtuple->tupstate != SPGIST_LIVE) {
1162 UnlockReleaseBuffer(buffer);
1167 if (SpGistPageIsLeaf(page)) {
1168 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1170 tid = palloc(sizeof(ItemPointerData));
1172 prst->dvalues[0] = PointerGetDatum(tid);
1173 prst->nulls[0] = ' ';
1174 prst->nulls[1] = 'n';
1175 prst->dvalues[2] = s->level;
1176 prst->nulls[2] = ' ';
1177 prst->nulls[3] = 'n';
1178 prst->nulls[4] = 'n';
1179 prst->nulls[5] = 'n';
1180 prst->dvalues[6] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1181 prst->state.attType.attbyval, prst->state.attType.attlen);
1182 prst->nulls[6] = ' ';
1184 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1186 SpGistNodeTuple node;
1188 SGITITERATE(innerTuple, i, node) {
1189 if (ItemPointerIsValid(&node->t_tid)) {
1195 if (i >= innerTuple->nNodes) {
1196 UnlockReleaseBuffer(buffer);
1201 tid = palloc(sizeof(ItemPointerData));
1203 prst->dvalues[0] = PointerGetDatum(tid);
1204 prst->nulls[0] = ' ';
1205 prst->dvalues[1] = Int32GetDatum(s->nlabel);
1206 prst->nulls[1] = ' ';
1207 prst->dvalues[2] = s->level;
1208 prst->nulls[2] = ' ';
1209 tid = palloc(sizeof(ItemPointerData));
1211 prst->dvalues[3] = PointerGetDatum(tid);
1212 prst->nulls[3] = ' ';
1213 if (innerTuple->prefixSize > 0) {
1214 prst->dvalues[4] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1215 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1216 prst->nulls[4] = ' ';
1218 prst->nulls[4] = 'n';
1219 if (!IndexTupleHasNulls(node)) {
1220 prst->dvalues[5] = datumCopy(SGNTDATUM(node, &prst->state),
1221 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1222 prst->nulls[5] = ' ';
1224 prst->nulls[5] = 'n';
1225 prst->nulls[6] = 'n';
1227 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1229 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1230 prst->stack = lcons(s, prst->stack);
1231 MemoryContextSwitchTo(oldcontext);
1235 UnlockReleaseBuffer(buffer);
1239 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1240 result = TupleGetDatum(funcctx->slot, htuple);
1242 SRF_RETURN_NEXT(funcctx, result);
1247 PG_FUNCTION_INFO_V1(gin_statpage);
1248 Datum gin_statpage(PG_FUNCTION_ARGS);
1250 gin_statpage(PG_FUNCTION_ARGS)
1252 #if PG_VERSION_NUM < 90400
1253 elog(NOTICE, "Function is not working under PgSQL < 9.4");
1255 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1257 text *name = PG_GETARG_TEXT_P(0);
1267 entryInnerPages = 0,
1270 uint64 dataInnerFreeSpace = 0,
1271 dataLeafFreeSpace = 0,
1272 dataInnerTuplesCount = 0,
1273 dataLeafIptrsCount = 0,
1274 entryInnerFreeSpace = 0,
1275 entryLeafFreeSpace = 0,
1276 entryInnerTuplesCount = 0,
1277 entryLeafTuplesCount = 0,
1278 entryPostingSize = 0,
1279 entryPostingCount = 0,
1283 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1284 index = relation_openrv(relvar, AccessExclusiveLock);
1286 if (index->rd_rel->relkind != RELKIND_INDEX ||
1287 index->rd_rel->relam != GIN_AM_OID)
1288 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1289 RelationGetRelationName(index));
1291 totalPages = RelationGetNumberOfBlocks(index);
1293 for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1299 buffer = ReadBuffer(index, blkno);
1300 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1302 page = BufferGetPage(buffer);
1303 header = (PageHeader)page;
1305 if (GinPageIsData(page))
1308 if (GinPageIsLeaf(page))
1310 ItemPointerData minItem;
1314 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1315 ItemPointerSetMin(&minItem);
1316 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1317 dataLeafIptrsCount += nlist;
1322 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1323 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1329 OffsetNumber i, maxoff;
1331 maxoff = PageGetMaxOffsetNumber(page);
1334 if (GinPageIsLeaf(page))
1337 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1338 entryLeafTuplesCount += maxoff;
1343 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1344 entryInnerTuplesCount += maxoff;
1347 for (i = 1; i <= maxoff; i++)
1349 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1351 if (GinPageIsLeaf(page))
1353 GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1354 entryPostingCount += GinGetNPosting(itup);
1355 entryPostingSize += SizeOfGinPostingList(list);
1356 entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1360 entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1365 UnlockReleaseBuffer(buffer);
1368 index_close(index, AccessExclusiveLock);
1371 snprintf(res, sizeof(res),
1374 "dataInnerPages: %u\n"
1375 "dataLeafPages: %u\n"
1376 "dataInnerFreeSpace: " INT64_FORMAT "\n"
1377 "dataLeafFreeSpace: " INT64_FORMAT "\n"
1378 "dataInnerTuplesCount: " INT64_FORMAT "\n"
1379 "dataLeafIptrsCount: " INT64_FORMAT "\n"
1381 "entryInnerPages: %u\n"
1382 "entryLeafPages: %u\n"
1383 "entryInnerFreeSpace: " INT64_FORMAT "\n"
1384 "entryLeafFreeSpace: " INT64_FORMAT "\n"
1385 "entryInnerTuplesCount: " INT64_FORMAT "\n"
1386 "entryLeafTuplesCount: " INT64_FORMAT "\n"
1387 "entryPostingSize: " INT64_FORMAT "\n"
1388 "entryPostingCount: " INT64_FORMAT "\n"
1389 "entryAttrSize: " INT64_FORMAT "\n"
1397 dataInnerTuplesCount,
1402 entryInnerFreeSpace,
1404 entryInnerTuplesCount,
1405 entryLeafTuplesCount,
1411 PG_RETURN_TEXT_P(CStringGetTextDatum(res));