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 #if PG_VERSION_NUM >= 90600
22 #include <catalog/pg_am.h>
24 #include "miscadmin.h"
25 #include "storage/lmgr.h"
26 #include "catalog/namespace.h"
27 #if PG_VERSION_NUM >= 80300
28 #include <tsearch/ts_utils.h>
30 #include <utils/tqual.h>
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/datum.h"
34 #include "utils/fmgroids.h"
37 #include <access/heapam.h>
38 #include <catalog/pg_type.h>
39 #include <access/relscan.h>
42 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
45 #define PG_NARGS() (fcinfo->nargs)
48 #if PG_VERSION_NUM >= 90600
50 #define ISNOTNULL false
51 #define heap_formtuple heap_form_tuple
59 char *out=palloc(VARSIZE(in));
61 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
62 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
73 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
75 #ifdef PG_MODULE_MAGIC
81 gist_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, AccessExclusiveLock), GIST_AM_OID);
91 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
94 gin_index_open(RangeVar *relvar) {
95 #if PG_VERSION_NUM < 90200
96 Oid relOid = RangeVarGetRelid(relvar, false);
98 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
100 return checkOpenedRelation(
101 index_open(relOid, AccessShareLock), GIN_AM_OID);
104 #define gin_index_close(r) index_close((r), AccessShareLock)
109 gist_index_open(RangeVar *relvar) {
110 Relation rel = index_openrv(relvar);
112 LockRelation(rel, AccessExclusiveLock);
113 return checkOpenedRelation(rel, GIST_AM_OID);
117 gist_index_close(Relation rel) {
118 UnlockRelation(rel, AccessExclusiveLock);
123 gin_index_open(RangeVar *relvar) {
124 Relation rel = index_openrv(relvar);
126 LockRelation(rel, AccessShareLock);
127 return checkOpenedRelation(rel, GIN_AM_OID);
131 gin_index_close(Relation rel) {
132 UnlockRelation(rel, AccessShareLock);
138 #if PG_VERSION_NUM >= 80300
139 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
142 #if PG_VERSION_NUM < 80300
143 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
147 checkOpenedRelation(Relation r, Oid PgAmOid) {
148 if ( r->rd_index == NULL )
149 elog(ERROR, "Relation %s.%s is not an index",
150 get_namespace_name(RelationGetNamespace(r)),
151 RelationGetRelationName(r)
154 if ( r->rd_rel->relam != PgAmOid )
155 elog(ERROR, "Index %s.%s has wrong type",
156 get_namespace_name(RelationGetNamespace(r)),
157 RelationGetRelationName(r)
164 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
174 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
175 MemSet(pred, ' ', level*4);
176 pred[level*4] = '\0';
178 buffer = ReadBuffer(r, blk);
179 page = (Page) BufferGetPage(buffer);
181 maxoff = PageGetMaxOffsetNumber(page);
184 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
185 int dist=info->ptr-((char*)info->txt);
187 info->txt=(text*)repalloc(info->txt, info->len);
188 info->ptr = ((char*)info->txt)+dist;
191 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
197 (int) PageGetFreeSpace(page),
198 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
199 GistPageGetOpaque(page)->rightlink,
200 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
201 info->ptr=strchr(info->ptr,'\0');
203 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
204 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
205 iid = PageGetItemId(page, i);
206 which = (IndexTuple) PageGetItem(page, iid);
207 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
208 gist_dumptree(r, level + 1, cblk, i, info);
210 ReleaseBuffer(buffer);
214 PG_FUNCTION_INFO_V1(gist_tree);
215 Datum gist_tree(PG_FUNCTION_ARGS);
217 gist_tree(PG_FUNCTION_ARGS) {
218 text *name=PG_GETARG_TEXT_P(0);
219 char *relname=t2c(name);
225 relname_list = stringToQualifiedNameList(relname, "gist_tree");
226 relvar = makeRangeVarFromNameList(relname_list);
227 index = gist_index_open(relvar);
228 PG_FREE_IF_COPY(name,0);
230 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
232 info.txt=(text*)palloc( info.len );
233 info.ptr=((char*)info.txt)+VARHDRSZ;
235 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
237 gist_index_close(index);
240 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
241 PG_RETURN_POINTER(info.txt);
252 uint64 leaftuplesize;
257 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
267 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
268 MemSet(pred, ' ', level*4);
269 pred[level*4] = '\0';
271 buffer = ReadBuffer(r, blk);
272 page = (Page) BufferGetPage(buffer);
274 maxoff = PageGetMaxOffsetNumber(page);
277 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
278 info->totalsize+=BLCKSZ;
279 info->numtuple+=maxoff;
280 if ( info->level < level )
283 if (GistPageIsLeaf(page)) {
284 info->numleafpages++;
285 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
286 info->numleaftuple+=maxoff;
288 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
289 iid = PageGetItemId(page, i);
290 which = (IndexTuple) PageGetItem(page, iid);
291 if ( GistTupleIsInvalid(which) )
292 info->numinvalidtuple++;
293 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
294 gist_stattree(r, level + 1, cblk, i, info);
298 ReleaseBuffer(buffer);
302 PG_FUNCTION_INFO_V1(gist_stat);
303 Datum gist_stat(PG_FUNCTION_ARGS);
305 gist_stat(PG_FUNCTION_ARGS) {
306 text *name=PG_GETARG_TEXT_P(0);
307 char *relname=t2c(name);
312 text *out=(text*)palloc(1024);
313 char *ptr=((char*)out)+VARHDRSZ;
316 relname_list = stringToQualifiedNameList(relname, "gist_tree");
317 relvar = makeRangeVarFromNameList(relname_list);
318 index = gist_index_open(relvar);
319 PG_FREE_IF_COPY(name,0);
321 memset(&info, 0, sizeof(IdxStat));
323 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
325 gist_index_close(index);
329 "Number of levels: %d\n"
330 "Number of pages: %d\n"
331 "Number of leaf pages: %d\n"
332 "Number of tuples: %d\n"
333 "Number of invalid tuples: %d\n"
334 "Number of leaf tuples: %d\n"
335 "Total size of tuples: "INT64_FORMAT" bytes\n"
336 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
337 "Total size of index: "INT64_FORMAT" bytes\n",
342 info.numinvalidtuple,
348 ptr=strchr(ptr,'\0');
350 SET_VARSIZE(out, ptr-((char*)out));
351 PG_RETURN_POINTER(out);
354 typedef struct GPItem {
367 #if PG_VERSION_NUM >= 90600
376 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
378 MemoryContext oldcontext;
379 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
381 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
382 nitem = (GPItem*)palloc( sizeof(GPItem) );
383 memset(nitem,0,sizeof(GPItem));
385 nitem->buffer = ReadBuffer(index, blk);
386 nitem->page = (Page) BufferGetPage(nitem->buffer);
387 nitem->offset=FirstOffsetNumber;
388 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
389 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
390 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
392 MemoryContextSwitchTo(oldcontext);
397 closeGPPage( FuncCallContext *funcctx ) {
398 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
400 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
402 ReleaseBuffer(oitem->buffer);
404 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
408 setup_firstcall(FuncCallContext *funcctx, text *name) {
409 MemoryContext oldcontext;
411 char *relname=t2c(name);
413 char attname[NAMEDATALEN];
416 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
418 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
419 memset(st,0,sizeof(TypeStorage));
420 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
421 st->relvar = makeRangeVarFromNameList(st->relname_list);
422 st->index = gist_index_open(st->relvar);
423 funcctx->user_fctx = (void*)st;
425 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
426 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
427 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
428 for (i = 0; i < st->index->rd_att->natts; i++) {
429 sprintf(attname, "z%d", i+2);
434 st->index->rd_att->attrs[i]->atttypid,
435 st->index->rd_att->attrs[i]->atttypmod,
436 st->index->rd_att->attrs[i]->attndims
440 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
441 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(*st->nulls));
443 funcctx->slot = TupleDescGetSlot(tupdesc);
444 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
446 MemoryContextSwitchTo(oldcontext);
449 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
453 close_call( FuncCallContext *funcctx ) {
454 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
456 while(st->item && closeGPPage(funcctx));
461 gist_index_close(st->index);
464 PG_FUNCTION_INFO_V1(gist_print);
465 Datum gist_print(PG_FUNCTION_ARGS);
467 gist_print(PG_FUNCTION_ARGS) {
468 FuncCallContext *funcctx;
470 Datum result=(Datum)0;
477 if (SRF_IS_FIRSTCALL()) {
478 text *name=PG_GETARG_TEXT_P(0);
479 funcctx = SRF_FIRSTCALL_INIT();
480 setup_firstcall(funcctx, name);
481 PG_FREE_IF_COPY(name,0);
484 funcctx = SRF_PERCALL_SETUP();
485 st = (TypeStorage*)(funcctx->user_fctx);
489 SRF_RETURN_DONE(funcctx);
492 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
493 if ( ! closeGPPage(funcctx) ) {
495 SRF_RETURN_DONE(funcctx);
499 iid = PageGetItemId( st->item->page, st->item->offset );
500 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
502 st->dvalues[0] = Int32GetDatum( st->item->level );
503 st->nulls[0] = ISNOTNULL;
504 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
505 st->nulls[1] = ISNOTNULL;
506 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
507 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
508 st->dvalues[i] = (Datum)0;
509 st->nulls[i] = ISNULL;
511 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
512 st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
516 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
517 result = TupleGetDatum(funcctx->slot, htuple);
518 st->item->offset = OffsetNumberNext(st->item->offset);
519 if ( !GistPageIsLeaf(st->item->page) )
520 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
522 SRF_RETURN_NEXT(funcctx, result);
525 typedef struct GinStatState {
533 #if PG_VERSION_NUM >= 90100
534 GinNullCategory category;
541 moveRightIfItNeeded( GinStatState *st )
543 Page page = BufferGetPage(st->buffer);
545 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
547 * We scaned the whole page, so we should take right page
549 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
551 if ( GinPageRightMost(page) )
552 return false; /* no more page */
554 LockBuffer(st->buffer, GIN_UNLOCK);
555 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
556 LockBuffer(st->buffer, GIN_SHARE);
557 st->offset = FirstOffsetNumber;
564 * Refinds a previois position, at returns it has correctly
565 * set offset and buffer is locked
568 refindPosition(GinStatState *st)
572 /* find left if needed (it causes only for first search) */
577 LockBuffer(st->buffer, GIN_SHARE);
579 page = BufferGetPage(st->buffer);
580 if (GinPageIsLeaf(page))
583 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
584 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
586 LockBuffer(st->buffer,GIN_UNLOCK);
587 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
590 if (st->offset == InvalidOffsetNumber) {
591 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
596 #if PG_VERSION_NUM >= 90100
597 GinNullCategory category;
598 #elif PG_VERSION_NUM < 80400
604 if (moveRightIfItNeeded(st)==false)
607 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
608 #if PG_VERSION_NUM >= 90100
609 datum = gintuple_get_key(&st->ginstate, itup, &category);
610 cmp = ginCompareAttEntries(&st->ginstate,
611 st->attnum + 1, st->curval, st->category,
612 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
613 #elif PG_VERSION_NUM >= 80400
614 datum = gin_index_getattr(&st->ginstate, itup);
616 cmp = compareAttEntries(&st->ginstate,
617 st->attnum + 1, st->curval,
618 gintuple_get_attrnum(&st->ginstate, itup), datum);
620 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
624 &st->ginstate.compareFn,
631 if ( st->curval && !st->index->rd_att->attrs[st->attnum]->attbyval )
632 pfree( (void*) st->curval );
643 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
644 MemoryContext oldcontext;
646 char *relname=t2c(name);
649 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
651 st=(GinStatState*)palloc( sizeof(GinStatState) );
652 memset(st,0,sizeof(GinStatState));
653 st->index = gin_index_open(
654 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
655 initGinState( &st->ginstate, st->index );
657 #if PG_VERSION_NUM >= 80400
658 if (attnum < 0 || attnum >= st->index->rd_att->natts)
659 elog(ERROR,"Wrong column's number");
665 funcctx->user_fctx = (void*)st;
667 tupdesc = CreateTemplateTupleDesc(2, false);
668 TupleDescInitEntry(tupdesc, 1, "value",
669 st->index->rd_att->attrs[st->attnum]->atttypid,
670 st->index->rd_att->attrs[st->attnum]->atttypmod,
671 st->index->rd_att->attrs[st->attnum]->attndims);
672 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
674 memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
676 funcctx->slot = TupleDescGetSlot(tupdesc);
677 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
679 MemoryContextSwitchTo(oldcontext);
682 st->offset = InvalidOffsetNumber;
683 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
687 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
688 MemoryContext oldcontext;
689 #if PG_VERSION_NUM >= 90100
691 #elif PG_VERSION_NUM < 80400
695 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
697 #if PG_VERSION_NUM >= 90100
698 key = gintuple_get_key(&st->ginstate, itup, &st->category);
700 if (st->category != GIN_CAT_NORM_KEY)
701 st->curval = (Datum)0;
704 st->curval = datumCopy(
705 #if PG_VERSION_NUM >= 90100
707 #elif PG_VERSION_NUM >= 80400
708 gin_index_getattr(&st->ginstate, itup),
710 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
712 st->index->rd_att->attrs[st->attnum]->attbyval,
713 st->index->rd_att->attrs[st->attnum]->attlen );
714 MemoryContextSwitchTo(oldcontext);
716 st->dvalues[0] = st->curval;
717 #if PG_VERSION_NUM >= 90100
718 /* do no distiguish various null category */
719 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
722 if ( GinIsPostingTree(itup) ) {
723 BlockNumber rootblkno = GinGetPostingTree(itup);
724 #if PG_VERSION_NUM >= 90400
726 GinBtreeStack *stack;
727 ItemPointerData minItem;
731 GinPostingTreeScan *gdi;
735 uint32 predictNumber;
737 LockBuffer(st->buffer, GIN_UNLOCK);
738 #if PG_VERSION_NUM >= 90400
739 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno
740 #if PG_VERSION_NUM >= 90600
744 page = BufferGetPage(stack->buffer);
745 ItemPointerSetMin(&minItem);
746 list = GinDataLeafPageGetItems(page, &nlist, minItem);
748 predictNumber = stack->predictNumber;
749 st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
750 #elif PG_VERSION_NUM >= 90100
751 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
752 entrybuffer = ginScanBeginPostingTree(gdi);
753 page = BufferGetPage(entrybuffer);
754 predictNumber = gdi->stack->predictNumber;
755 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
757 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
758 entrybuffer = scanBeginPostingTree(gdi);
759 page = BufferGetPage(entrybuffer);
760 predictNumber = gdi->stack->predictNumber;
761 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
764 #if PG_VERSION_NUM < 90400
765 LockBuffer(entrybuffer, GIN_UNLOCK);
766 freeGinBtreeStack(gdi->stack);
769 LockBuffer(stack->buffer, GIN_UNLOCK);
770 freeGinBtreeStack(stack);
773 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
774 LockBuffer(st->buffer, GIN_UNLOCK);
778 PG_FUNCTION_INFO_V1(gin_stat);
779 Datum gin_stat(PG_FUNCTION_ARGS);
781 gin_stat(PG_FUNCTION_ARGS) {
782 FuncCallContext *funcctx;
784 Datum result=(Datum)0;
789 if (SRF_IS_FIRSTCALL()) {
790 text *name=PG_GETARG_TEXT_P(0);
791 funcctx = SRF_FIRSTCALL_INIT();
792 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
793 PG_FREE_IF_COPY(name,0);
796 funcctx = SRF_PERCALL_SETUP();
797 st = (GinStatState*)(funcctx->user_fctx);
799 if ( refindPosition(st) == false ) {
800 UnlockReleaseBuffer( st->buffer );
801 gin_index_close(st->index);
803 SRF_RETURN_DONE(funcctx);
809 if (moveRightIfItNeeded(st)==false) {
810 UnlockReleaseBuffer( st->buffer );
811 gin_index_close(st->index);
813 SRF_RETURN_DONE(funcctx);
816 page = BufferGetPage(st->buffer);
817 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
819 #if PG_VERSION_NUM >= 80400
820 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
825 processTuple( funcctx, st, ituple );
827 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
828 result = TupleGetDatum(funcctx->slot, htuple);
830 SRF_RETURN_NEXT(funcctx, result);
833 PG_FUNCTION_INFO_V1(gin_count_estimate);
834 Datum gin_count_estimate(PG_FUNCTION_ARGS);
835 #if PG_VERSION_NUM >= 80300
837 gin_count_estimate(PG_FUNCTION_ARGS) {
838 text *name=PG_GETARG_TEXT_P(0);
842 char *relname=t2c(name);
844 #if PG_VERSION_NUM >= 80400
845 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
848 ItemPointerData tids[MAXTIDS];
853 index = gin_index_open(
854 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
856 if ( index->rd_opcintype[0] != TSVECTOROID ) {
857 gin_index_close(index);
858 elog(ERROR, "Column type is not a tsvector");
863 key.sk_strategy = TSearchStrategyNumber;
865 key.sk_argument = PG_GETARG_DATUM(1);
867 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
869 #if PG_VERSION_NUM >= 90100
870 #if PG_VERSION_NUM >= 90400
871 scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
873 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
875 index_rescan(scan, &key, 1, NULL, 0);
877 count = index_getbitmap(scan, bitmap);
879 #elif PG_VERSION_NUM >= 80400
880 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
882 count = index_getbitmap(scan, bitmap);
885 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
888 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
889 count += returned_tids;
893 index_endscan( scan );
894 gin_index_close(index);
896 PG_RETURN_INT64(count);
900 gin_count_estimate(PG_FUNCTION_ARGS) {
901 elog(NOTICE, "Function is not working under PgSQL < 8.3");
907 PG_FUNCTION_INFO_V1(spgist_stat);
908 Datum spgist_stat(PG_FUNCTION_ARGS);
910 spgist_stat(PG_FUNCTION_ARGS)
912 #if PG_VERSION_NUM < 90200
913 elog(NOTICE, "Function is not working under PgSQL < 9.2");
915 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
917 text *name = PG_GETARG_TEXT_P(0);
921 BlockNumber totalPages = 0,
926 double usedSpace = 0.0,
928 usedInnerSpace = 0.0;
931 int64 innerTuples = 0,
934 nLeafPlaceholder = 0,
935 nInnerPlaceholder = 0,
939 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
940 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
942 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
943 index = relation_openrv(relvar, AccessExclusiveLock);
945 if (!IS_INDEX(index) || !IS_SPGIST(index))
946 elog(ERROR, "relation \"%s\" is not an SPGiST index",
947 RelationGetRelationName(index));
949 totalPages = RelationGetNumberOfBlocks(index);
951 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
957 buffer = ReadBuffer(index, blkno);
958 LockBuffer(buffer, BUFFER_LOCK_SHARE);
960 page = BufferGetPage(buffer);
962 if (PageIsNew(page) || SpGistPageIsDeleted(page))
965 UnlockReleaseBuffer(buffer);
969 if (SpGistPageIsLeaf(page))
972 leafTuples += PageGetMaxOffsetNumber(page);
973 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
974 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
982 max = PageGetMaxOffsetNumber(page);
984 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
985 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
986 for (i = FirstOffsetNumber; i <= max; i++)
990 it = (SpGistInnerTuple) PageGetItem(page,
991 PageGetItemId(page, i));
998 bufferSize = BufferGetPageSize(buffer)
999 - MAXALIGN(sizeof(SpGistPageOpaqueData))
1000 - SizeOfPageHeaderData;
1002 pageFree = PageGetExactFreeSpace(page);
1004 usedSpace += bufferSize - pageFree;
1005 if (SpGistPageIsLeaf(page))
1006 usedLeafSpace += bufferSize - pageFree;
1008 usedInnerSpace += bufferSize - pageFree;
1010 if (pageFree == bufferSize)
1013 UnlockReleaseBuffer(buffer);
1016 index_close(index, AccessExclusiveLock);
1018 totalPages--; /* discount metapage */
1020 snprintf(res, sizeof(res),
1022 "deletedPages: %u\n"
1026 "usedSpace: %.2f kbytes\n"
1027 "usedInnerSpace: %.2f kbytes\n"
1028 "usedLeafSpace: %.2f kbytes\n"
1029 "freeSpace: %.2f kbytes\n"
1030 "fillRatio: %.2f%%\n"
1031 "leafTuples: " INT64_FORMAT "\n"
1032 "innerTuples: " INT64_FORMAT "\n"
1033 "innerAllTheSame: " INT64_FORMAT "\n"
1034 "leafPlaceholders: " INT64_FORMAT "\n"
1035 "innerPlaceholders: " INT64_FORMAT "\n"
1036 "leafRedirects: " INT64_FORMAT "\n"
1037 "innerRedirects: " INT64_FORMAT,
1038 totalPages, deletedPages, innerPages, leafPages, emptyPages,
1040 usedInnerSpace / 1024.0,
1041 usedLeafSpace / 1024.0,
1042 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1043 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1044 leafTuples, innerTuples, nAllTheSame,
1045 nLeafPlaceholder, nInnerPlaceholder,
1046 nLeafRedirect, nInnerRedirect);
1048 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1052 #if PG_VERSION_NUM >= 90200
1054 typedef struct SPGistPrintStackElem {
1055 ItemPointerData iptr;
1058 } SPGistPrintStackElem;
1060 typedef struct SPGistPrint {
1063 Datum dvalues[8 /* see CreateTemplateTupleDesc call */];
1064 char nulls[8 /* see CreateTemplateTupleDesc call */];
1069 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1070 MemoryContext oldcontext;
1071 SPGistPrintStackElem *e;
1073 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1075 e = palloc(sizeof(*e));
1079 prst->stack = lcons(e, prst->stack);
1081 MemoryContextSwitchTo(oldcontext);
1085 close_spgist_print(SPGistPrint *prst) {
1086 index_close(prst->index, AccessExclusiveLock);
1090 PG_FUNCTION_INFO_V1(spgist_print);
1091 Datum spgist_print(PG_FUNCTION_ARGS);
1093 spgist_print(PG_FUNCTION_ARGS)
1095 #if PG_VERSION_NUM < 90200
1096 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1098 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1100 FuncCallContext *funcctx;
1102 SPGistPrintStackElem *s;
1105 MemoryContext oldcontext;
1107 if (SRF_IS_FIRSTCALL()) {
1108 text *name=PG_GETARG_TEXT_P(0);
1111 ItemPointerData ipd;
1114 funcctx = SRF_FIRSTCALL_INIT();
1115 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1116 index = relation_openrv(relvar, AccessExclusiveLock);
1118 if (!IS_INDEX(index) || !IS_SPGIST(index))
1119 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1120 RelationGetRelationName(index));
1122 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1124 prst = palloc(sizeof(*prst));
1126 prst->index = index;
1127 initSpGistState(&prst->state, index);
1129 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
1130 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1131 TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
1132 TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
1133 TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
1134 TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
1135 TupleDescInitEntry(tupdesc, 6, "prefix",
1136 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1137 TupleDescInitEntry(tupdesc, 7, "label",
1138 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1139 TupleDescInitEntry(tupdesc, 8, "leaf",
1140 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1142 funcctx->slot = TupleDescGetSlot(tupdesc);
1143 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1145 funcctx->user_fctx = (void*)prst;
1147 MemoryContextSwitchTo(oldcontext);
1149 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1151 pushSPGistPrint(funcctx, prst, &ipd, 1);
1153 PG_FREE_IF_COPY(name,0);
1156 funcctx = SRF_PERCALL_SETUP();
1157 prst = (SPGistPrint*)(funcctx->user_fctx);
1161 if ( prst->stack == NIL ) {
1162 close_spgist_print(prst);
1163 SRF_RETURN_DONE(funcctx);
1166 CHECK_FOR_INTERRUPTS();
1168 s = (SPGistPrintStackElem*)linitial(prst->stack);
1169 prst->stack = list_delete_first(prst->stack);
1171 if (ItemPointerIsValid(&s->iptr))
1179 SpGistDeadTuple dtuple;
1182 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1183 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1185 page = BufferGetPage(buffer);
1186 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1187 UnlockReleaseBuffer(buffer);
1192 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1194 if (dtuple->tupstate != SPGIST_LIVE) {
1195 UnlockReleaseBuffer(buffer);
1200 if (SpGistPageIsLeaf(page)) {
1201 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1203 tid = palloc(sizeof(ItemPointerData));
1205 prst->dvalues[0] = PointerGetDatum(tid);
1206 prst->nulls[0] = ISNOTNULL;
1207 prst->nulls[1] = ISNULL;
1208 prst->nulls[2] = ISNULL;
1209 prst->dvalues[3] = s->level;
1210 prst->nulls[3] = ISNOTNULL;
1211 prst->nulls[4] = ISNULL;
1212 prst->nulls[5] = ISNULL;
1213 prst->nulls[6] = ISNULL;
1214 prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1215 prst->state.attType.attbyval, prst->state.attType.attlen);
1216 prst->nulls[7] = ISNOTNULL;
1218 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1220 SpGistNodeTuple node;
1222 SGITITERATE(innerTuple, i, node) {
1223 if (ItemPointerIsValid(&node->t_tid)) {
1229 if (i >= innerTuple->nNodes) {
1230 UnlockReleaseBuffer(buffer);
1235 tid = palloc(sizeof(ItemPointerData));
1237 prst->dvalues[0] = PointerGetDatum(tid);
1238 prst->nulls[0] = ISNOTNULL;
1239 prst->dvalues[1] = innerTuple->allTheSame;
1240 prst->nulls[1] = ISNOTNULL;
1241 prst->dvalues[2] = Int32GetDatum(s->nlabel);
1242 prst->nulls[2] = ISNOTNULL;
1243 prst->dvalues[3] = s->level;
1244 prst->nulls[3] = ISNOTNULL;
1245 tid = palloc(sizeof(ItemPointerData));
1247 prst->dvalues[4] = PointerGetDatum(tid);
1248 prst->nulls[5] = ISNOTNULL;
1249 if (innerTuple->prefixSize > 0) {
1250 prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1251 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1252 prst->nulls[5] = ISNOTNULL;
1254 prst->nulls[5] = ISNULL;
1255 if (!IndexTupleHasNulls(node)) {
1256 prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
1257 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1258 prst->nulls[6] = ISNOTNULL;
1260 prst->nulls[6] = ISNULL;
1261 prst->nulls[7] = ISNULL;
1263 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1265 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1266 prst->stack = lcons(s, prst->stack);
1267 MemoryContextSwitchTo(oldcontext);
1271 UnlockReleaseBuffer(buffer);
1275 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1276 result = TupleGetDatum(funcctx->slot, htuple);
1278 SRF_RETURN_NEXT(funcctx, result);
1283 PG_FUNCTION_INFO_V1(gin_statpage);
1284 Datum gin_statpage(PG_FUNCTION_ARGS);
1286 gin_statpage(PG_FUNCTION_ARGS)
1288 #if PG_VERSION_NUM < 90400
1289 elog(NOTICE, "Function is not working under PgSQL < 9.4");
1291 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1293 text *name = PG_GETARG_TEXT_P(0);
1303 entryInnerPages = 0,
1306 uint64 dataInnerFreeSpace = 0,
1307 dataLeafFreeSpace = 0,
1308 dataInnerTuplesCount = 0,
1309 dataLeafIptrsCount = 0,
1310 entryInnerFreeSpace = 0,
1311 entryLeafFreeSpace = 0,
1312 entryInnerTuplesCount = 0,
1313 entryLeafTuplesCount = 0,
1314 entryPostingSize = 0,
1315 entryPostingCount = 0,
1319 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1320 index = relation_openrv(relvar, AccessExclusiveLock);
1322 if (index->rd_rel->relkind != RELKIND_INDEX ||
1323 index->rd_rel->relam != GIN_AM_OID)
1324 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1325 RelationGetRelationName(index));
1327 totalPages = RelationGetNumberOfBlocks(index);
1329 for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1335 buffer = ReadBuffer(index, blkno);
1336 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1338 page = BufferGetPage(buffer);
1339 header = (PageHeader)page;
1341 if (GinPageIsData(page))
1344 if (GinPageIsLeaf(page))
1346 ItemPointerData minItem;
1350 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1351 ItemPointerSetMin(&minItem);
1352 pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
1353 dataLeafIptrsCount += nlist;
1358 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1359 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1365 OffsetNumber i, maxoff;
1367 maxoff = PageGetMaxOffsetNumber(page);
1370 if (GinPageIsLeaf(page))
1373 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1374 entryLeafTuplesCount += maxoff;
1379 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1380 entryInnerTuplesCount += maxoff;
1383 for (i = 1; i <= maxoff; i++)
1385 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1387 if (GinPageIsLeaf(page))
1389 GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1390 entryPostingCount += GinGetNPosting(itup);
1391 entryPostingSize += SizeOfGinPostingList(list);
1392 entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1396 entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1401 UnlockReleaseBuffer(buffer);
1404 index_close(index, AccessExclusiveLock);
1407 snprintf(res, sizeof(res),
1410 "dataInnerPages: %u\n"
1411 "dataLeafPages: %u\n"
1412 "dataInnerFreeSpace: " INT64_FORMAT "\n"
1413 "dataLeafFreeSpace: " INT64_FORMAT "\n"
1414 "dataInnerTuplesCount: " INT64_FORMAT "\n"
1415 "dataLeafIptrsCount: " INT64_FORMAT "\n"
1417 "entryInnerPages: %u\n"
1418 "entryLeafPages: %u\n"
1419 "entryInnerFreeSpace: " INT64_FORMAT "\n"
1420 "entryLeafFreeSpace: " INT64_FORMAT "\n"
1421 "entryInnerTuplesCount: " INT64_FORMAT "\n"
1422 "entryLeafTuplesCount: " INT64_FORMAT "\n"
1423 "entryPostingSize: " INT64_FORMAT "\n"
1424 "entryPostingCount: " INT64_FORMAT "\n"
1425 "entryAttrSize: " INT64_FORMAT "\n"
1433 dataInnerTuplesCount,
1438 entryInnerFreeSpace,
1440 entryInnerTuplesCount,
1441 entryLeafTuplesCount,
1447 PG_RETURN_TEXT_P(CStringGetTextDatum(res));