3 #include "access/genam.h"
4 #include "access/gin.h"
5 #if PG_VERSION_NUM >= 90100
6 #include "access/gin_private.h"
8 #include "access/gist.h"
9 #include "access/gist_private.h"
10 #include "access/gistscan.h"
11 #if PG_VERSION_NUM >= 90200
12 #include "access/spgist_private.h"
13 #include "access/spgist.h"
14 #include "utils/datum.h"
16 #include "access/heapam.h"
17 #include "catalog/index.h"
18 #include "miscadmin.h"
19 #include "storage/lmgr.h"
20 #include "catalog/namespace.h"
21 #if PG_VERSION_NUM >= 80300
22 #include <tsearch/ts_utils.h>
24 #include <utils/tqual.h>
25 #include "utils/builtins.h"
26 #include "utils/lsyscache.h"
27 #include "utils/datum.h"
28 #include "utils/fmgroids.h"
31 #include <access/heapam.h>
32 #include <catalog/pg_type.h>
33 #include <access/relscan.h>
36 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
39 #define PG_NARGS() (fcinfo->nargs)
44 char *out=palloc( VARSIZE(in) );
45 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
46 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
57 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
59 #ifdef PG_MODULE_MAGIC
65 gist_index_open(RangeVar *relvar) {
66 #if PG_VERSION_NUM < 90200
67 Oid relOid = RangeVarGetRelid(relvar, false);
69 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
71 return checkOpenedRelation(
72 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
75 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
78 gin_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, AccessShareLock), GIN_AM_OID);
88 #define gin_index_close(r) index_close((r), AccessShareLock)
93 gist_index_open(RangeVar *relvar) {
94 Relation rel = index_openrv(relvar);
96 LockRelation(rel, AccessExclusiveLock);
97 return checkOpenedRelation(rel, GIST_AM_OID);
101 gist_index_close(Relation rel) {
102 UnlockRelation(rel, AccessExclusiveLock);
107 gin_index_open(RangeVar *relvar) {
108 Relation rel = index_openrv(relvar);
110 LockRelation(rel, AccessShareLock);
111 return checkOpenedRelation(rel, GIN_AM_OID);
115 gin_index_close(Relation rel) {
116 UnlockRelation(rel, AccessShareLock);
122 #if PG_VERSION_NUM >= 80300
123 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
126 #if PG_VERSION_NUM < 80300
127 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
131 checkOpenedRelation(Relation r, Oid PgAmOid) {
132 if ( r->rd_am == NULL )
133 elog(ERROR, "Relation %s.%s is not an index",
134 get_namespace_name(RelationGetNamespace(r)),
135 RelationGetRelationName(r)
138 if ( r->rd_rel->relam != PgAmOid )
139 elog(ERROR, "Index %s.%s has wrong type",
140 get_namespace_name(RelationGetNamespace(r)),
141 RelationGetRelationName(r)
148 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
158 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
159 MemSet(pred, ' ', level*4);
160 pred[level*4] = '\0';
162 buffer = ReadBuffer(r, blk);
163 page = (Page) BufferGetPage(buffer);
165 maxoff = PageGetMaxOffsetNumber(page);
168 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
169 int dist=info->ptr-((char*)info->txt);
171 info->txt=(text*)repalloc(info->txt, info->len);
172 info->ptr = ((char*)info->txt)+dist;
175 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
181 (int) PageGetFreeSpace(page),
182 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
183 GistPageGetOpaque(page)->rightlink,
184 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
185 info->ptr=strchr(info->ptr,'\0');
187 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
188 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
189 iid = PageGetItemId(page, i);
190 which = (IndexTuple) PageGetItem(page, iid);
191 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
192 gist_dumptree(r, level + 1, cblk, i, info);
194 ReleaseBuffer(buffer);
198 PG_FUNCTION_INFO_V1(gist_tree);
199 Datum gist_tree(PG_FUNCTION_ARGS);
201 gist_tree(PG_FUNCTION_ARGS) {
202 text *name=PG_GETARG_TEXT_P(0);
203 char *relname=t2c(name);
209 relname_list = stringToQualifiedNameList(relname, "gist_tree");
210 relvar = makeRangeVarFromNameList(relname_list);
211 index = gist_index_open(relvar);
212 PG_FREE_IF_COPY(name,0);
214 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
216 info.txt=(text*)palloc( info.len );
217 info.ptr=((char*)info.txt)+VARHDRSZ;
219 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
221 gist_index_close(index);
224 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
225 PG_RETURN_POINTER(info.txt);
236 uint64 leaftuplesize;
241 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
251 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
252 MemSet(pred, ' ', level*4);
253 pred[level*4] = '\0';
255 buffer = ReadBuffer(r, blk);
256 page = (Page) BufferGetPage(buffer);
258 maxoff = PageGetMaxOffsetNumber(page);
261 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
262 info->totalsize+=BLCKSZ;
263 info->numtuple+=maxoff;
264 if ( info->level < level )
267 if (GistPageIsLeaf(page)) {
268 info->numleafpages++;
269 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
270 info->numleaftuple+=maxoff;
272 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
273 iid = PageGetItemId(page, i);
274 which = (IndexTuple) PageGetItem(page, iid);
275 if ( GistTupleIsInvalid(which) )
276 info->numinvalidtuple++;
277 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
278 gist_stattree(r, level + 1, cblk, i, info);
282 ReleaseBuffer(buffer);
286 PG_FUNCTION_INFO_V1(gist_stat);
287 Datum gist_stat(PG_FUNCTION_ARGS);
289 gist_stat(PG_FUNCTION_ARGS) {
290 text *name=PG_GETARG_TEXT_P(0);
291 char *relname=t2c(name);
296 text *out=(text*)palloc(1024);
297 char *ptr=((char*)out)+VARHDRSZ;
300 relname_list = stringToQualifiedNameList(relname, "gist_tree");
301 relvar = makeRangeVarFromNameList(relname_list);
302 index = gist_index_open(relvar);
303 PG_FREE_IF_COPY(name,0);
305 memset(&info, 0, sizeof(IdxStat));
307 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
309 gist_index_close(index);
313 "Number of levels: %d\n"
314 "Number of pages: %d\n"
315 "Number of leaf pages: %d\n"
316 "Number of tuples: %d\n"
317 "Number of invalid tuples: %d\n"
318 "Number of leaf tuples: %d\n"
319 "Total size of tuples: "INT64_FORMAT" bytes\n"
320 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
321 "Total size of index: "INT64_FORMAT" bytes\n",
326 info.numinvalidtuple,
332 ptr=strchr(ptr,'\0');
334 SET_VARSIZE(out, ptr-((char*)out));
335 PG_RETURN_POINTER(out);
338 typedef struct GPItem {
356 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
358 MemoryContext oldcontext;
359 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
361 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
362 nitem = (GPItem*)palloc( sizeof(GPItem) );
363 memset(nitem,0,sizeof(GPItem));
365 nitem->buffer = ReadBuffer(index, blk);
366 nitem->page = (Page) BufferGetPage(nitem->buffer);
367 nitem->offset=FirstOffsetNumber;
368 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
369 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
370 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
372 MemoryContextSwitchTo(oldcontext);
377 closeGPPage( FuncCallContext *funcctx ) {
378 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
380 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
382 ReleaseBuffer(oitem->buffer);
384 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
388 setup_firstcall(FuncCallContext *funcctx, text *name) {
389 MemoryContext oldcontext;
391 char *relname=t2c(name);
393 char attname[NAMEDATALEN];
396 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
398 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
399 memset(st,0,sizeof(TypeStorage));
400 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
401 st->relvar = makeRangeVarFromNameList(st->relname_list);
402 st->index = gist_index_open(st->relvar);
403 funcctx->user_fctx = (void*)st;
405 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
406 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
407 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
408 for (i = 0; i < st->index->rd_att->natts; i++) {
409 sprintf(attname, "z%d", i+2);
414 st->index->rd_att->attrs[i]->atttypid,
415 st->index->rd_att->attrs[i]->atttypmod,
416 st->index->rd_att->attrs[i]->attndims
420 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
421 st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
423 funcctx->slot = TupleDescGetSlot(tupdesc);
424 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
426 MemoryContextSwitchTo(oldcontext);
429 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
433 close_call( FuncCallContext *funcctx ) {
434 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
436 while( st->item && closeGPPage(funcctx) );
438 pfree( st->dvalues );
441 gist_index_close(st->index);
444 PG_FUNCTION_INFO_V1(gist_print);
445 Datum gist_print(PG_FUNCTION_ARGS);
447 gist_print(PG_FUNCTION_ARGS) {
448 FuncCallContext *funcctx;
450 Datum result=(Datum)0;
457 if (SRF_IS_FIRSTCALL()) {
458 text *name=PG_GETARG_TEXT_P(0);
459 funcctx = SRF_FIRSTCALL_INIT();
460 setup_firstcall(funcctx, name);
461 PG_FREE_IF_COPY(name,0);
464 funcctx = SRF_PERCALL_SETUP();
465 st = (TypeStorage*)(funcctx->user_fctx);
469 SRF_RETURN_DONE(funcctx);
472 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
473 if ( ! closeGPPage(funcctx) ) {
475 SRF_RETURN_DONE(funcctx);
479 iid = PageGetItemId( st->item->page, st->item->offset );
480 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
482 st->dvalues[0] = Int32GetDatum( st->item->level );
484 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
486 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
487 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
488 st->dvalues[i] = (Datum)0;
491 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
492 st->nulls[i] = ( isnull ) ? 'n' : ' ';
496 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
497 result = TupleGetDatum(funcctx->slot, htuple);
498 st->item->offset = OffsetNumberNext(st->item->offset);
499 if ( !GistPageIsLeaf(st->item->page) )
500 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
502 SRF_RETURN_NEXT(funcctx, result);
505 typedef struct GinStatState {
513 #if PG_VERSION_NUM >= 90100
514 GinNullCategory category;
521 moveRightIfItNeeded( GinStatState *st )
523 Page page = BufferGetPage(st->buffer);
525 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
527 * We scaned the whole page, so we should take right page
529 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
531 if ( GinPageRightMost(page) )
532 return false; /* no more page */
534 LockBuffer(st->buffer, GIN_UNLOCK);
535 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
536 LockBuffer(st->buffer, GIN_SHARE);
537 st->offset = FirstOffsetNumber;
544 * Refinds a previois position, at returns it has correctly
545 * set offset and buffer is locked
548 refindPosition(GinStatState *st)
552 /* find left if needed (it causes only for first search) */
557 LockBuffer(st->buffer, GIN_SHARE);
559 page = BufferGetPage(st->buffer);
560 if (GinPageIsLeaf(page))
563 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
564 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
566 LockBuffer(st->buffer,GIN_UNLOCK);
567 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
570 if (st->offset == InvalidOffsetNumber) {
571 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
576 #if PG_VERSION_NUM >= 90100
577 GinNullCategory category;
578 #elif PG_VERSION_NUM < 80400
584 if (moveRightIfItNeeded(st)==false)
587 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
588 #if PG_VERSION_NUM >= 90100
589 datum = gintuple_get_key(&st->ginstate, itup, &category);
590 cmp = ginCompareAttEntries(&st->ginstate,
591 st->attnum + 1, st->curval, st->category,
592 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
593 #elif PG_VERSION_NUM >= 80400
594 datum = gin_index_getattr(&st->ginstate, itup);
596 cmp = compareAttEntries(&st->ginstate,
597 st->attnum + 1, st->curval,
598 gintuple_get_attrnum(&st->ginstate, itup), datum);
600 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
604 &st->ginstate.compareFn,
611 if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
612 pfree( (void*) st->curval );
623 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
624 MemoryContext oldcontext;
626 char *relname=t2c(name);
629 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
631 st=(GinStatState*)palloc( sizeof(GinStatState) );
632 memset(st,0,sizeof(GinStatState));
633 st->index = gin_index_open(
634 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
635 initGinState( &st->ginstate, st->index );
637 #if PG_VERSION_NUM >= 80400
638 if (attnum < 0 || attnum >= st->index->rd_att->natts)
639 elog(ERROR,"Wrong column's number");
645 funcctx->user_fctx = (void*)st;
647 tupdesc = CreateTemplateTupleDesc(2, false);
648 TupleDescInitEntry(tupdesc, 1, "value",
649 st->index->rd_att->attrs[st->attnum]->atttypid,
650 st->index->rd_att->attrs[st->attnum]->atttypmod,
651 st->index->rd_att->attrs[st->attnum]->attndims);
652 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
654 memset( st->nulls, ' ', 2*sizeof(char) );
656 funcctx->slot = TupleDescGetSlot(tupdesc);
657 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
659 MemoryContextSwitchTo(oldcontext);
662 st->offset = InvalidOffsetNumber;
663 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
667 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
668 MemoryContext oldcontext;
669 #if PG_VERSION_NUM < 80400
673 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
674 st->curval = datumCopy(
675 #if PG_VERSION_NUM >= 90100
676 gintuple_get_key(&st->ginstate, itup, &st->category),
677 #elif PG_VERSION_NUM >= 80400
678 gin_index_getattr(&st->ginstate, itup),
680 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
682 st->index->rd_att->attrs[st->attnum]->attbyval,
683 st->index->rd_att->attrs[st->attnum]->attlen );
684 MemoryContextSwitchTo(oldcontext);
686 st->dvalues[0] = st->curval;
687 #if PG_VERSION_NUM >= 90100
688 /* do no distiguish various null category */
689 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n';
692 if ( GinIsPostingTree(itup) ) {
693 BlockNumber rootblkno = GinGetPostingTree(itup);
694 GinPostingTreeScan *gdi;
698 LockBuffer(st->buffer, GIN_UNLOCK);
699 #if PG_VERSION_NUM >= 90100
700 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
701 entrybuffer = ginScanBeginPostingTree(gdi);
703 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
704 entrybuffer = scanBeginPostingTree(gdi);
707 page = BufferGetPage(entrybuffer);
708 st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
710 LockBuffer(entrybuffer, GIN_UNLOCK);
711 freeGinBtreeStack(gdi->stack);
714 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
715 LockBuffer(st->buffer, GIN_UNLOCK);
719 PG_FUNCTION_INFO_V1(gin_stat);
720 Datum gin_stat(PG_FUNCTION_ARGS);
722 gin_stat(PG_FUNCTION_ARGS) {
723 FuncCallContext *funcctx;
725 Datum result=(Datum)0;
730 if (SRF_IS_FIRSTCALL()) {
731 text *name=PG_GETARG_TEXT_P(0);
732 funcctx = SRF_FIRSTCALL_INIT();
733 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
734 PG_FREE_IF_COPY(name,0);
737 funcctx = SRF_PERCALL_SETUP();
738 st = (GinStatState*)(funcctx->user_fctx);
740 if ( refindPosition(st) == false ) {
741 UnlockReleaseBuffer( st->buffer );
742 gin_index_close(st->index);
744 SRF_RETURN_DONE(funcctx);
750 if (moveRightIfItNeeded(st)==false) {
751 UnlockReleaseBuffer( st->buffer );
752 gin_index_close(st->index);
754 SRF_RETURN_DONE(funcctx);
757 page = BufferGetPage(st->buffer);
758 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
760 #if PG_VERSION_NUM >= 80400
761 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
766 processTuple( funcctx, st, ituple );
768 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
769 result = TupleGetDatum(funcctx->slot, htuple);
771 SRF_RETURN_NEXT(funcctx, result);
774 PG_FUNCTION_INFO_V1(gin_count_estimate);
775 Datum gin_count_estimate(PG_FUNCTION_ARGS);
776 #if PG_VERSION_NUM >= 80300
778 gin_count_estimate(PG_FUNCTION_ARGS) {
779 text *name=PG_GETARG_TEXT_P(0);
783 char *relname=t2c(name);
785 #if PG_VERSION_NUM >= 80400
786 TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
789 ItemPointerData tids[MAXTIDS];
794 index = gin_index_open(
795 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
797 if ( index->rd_opcintype[0] != TSVECTOROID ) {
798 gin_index_close(index);
799 elog(ERROR, "Column type is not a tsvector");
804 key.sk_strategy = TSearchStrategyNumber;
806 key.sk_argument = PG_GETARG_DATUM(1);
808 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
810 #if PG_VERSION_NUM >= 90100
811 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
812 index_rescan(scan, &key, 1, NULL, 0);
814 count = index_getbitmap(scan, bitmap);
816 #elif PG_VERSION_NUM >= 80400
817 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
819 count = index_getbitmap(scan, bitmap);
822 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
825 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
826 count += returned_tids;
830 index_endscan( scan );
831 gin_index_close(index);
833 PG_RETURN_INT64(count);
837 gin_count_estimate(PG_FUNCTION_ARGS) {
838 elog(NOTICE, "Function is not working under PgSQL < 8.3");
844 PG_FUNCTION_INFO_V1(spgist_stat);
845 Datum spgist_stat(PG_FUNCTION_ARGS);
847 spgist_stat(PG_FUNCTION_ARGS)
849 #if PG_VERSION_NUM < 90200
850 elog(NOTICE, "Function is not working under PgSQL < 9.2");
852 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
854 text *name = PG_GETARG_TEXT_P(0);
858 BlockNumber totalPages = 0,
863 double usedSpace = 0.0;
866 int64 innerTuples = 0,
869 nLeafPlaceholder = 0,
870 nInnerPlaceholder = 0,
874 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
875 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
877 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
878 index = relation_openrv(relvar, AccessExclusiveLock);
880 if (!IS_INDEX(index) || !IS_SPGIST(index))
881 elog(ERROR, "relation \"%s\" is not an SPGiST index",
882 RelationGetRelationName(index));
884 totalPages = RelationGetNumberOfBlocks(index);
886 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
892 buffer = ReadBuffer(index, blkno);
893 LockBuffer(buffer, BUFFER_LOCK_SHARE);
895 page = BufferGetPage(buffer);
897 if (PageIsNew(page) || SpGistPageIsDeleted(page))
900 UnlockReleaseBuffer(buffer);
904 if (SpGistPageIsLeaf(page))
907 leafTuples += PageGetMaxOffsetNumber(page);
908 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
909 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
917 max = PageGetMaxOffsetNumber(page);
919 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
920 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
921 for (i = FirstOffsetNumber; i <= max; i++)
925 it = (SpGistInnerTuple) PageGetItem(page,
926 PageGetItemId(page, i));
933 bufferSize = BufferGetPageSize(buffer)
934 - MAXALIGN(sizeof(SpGistPageOpaqueData))
935 - SizeOfPageHeaderData;
937 pageFree = PageGetExactFreeSpace(page);
939 usedSpace += bufferSize - pageFree;
941 if (pageFree == bufferSize)
944 UnlockReleaseBuffer(buffer);
947 index_close(index, AccessExclusiveLock);
949 totalPages--; /* discount metapage */
951 snprintf(res, sizeof(res),
957 "usedSpace: %.2f kbytes\n"
958 "freeSpace: %.2f kbytes\n"
959 "fillRatio: %.2f%%\n"
960 "leafTuples: " INT64_FORMAT "\n"
961 "innerTuples: " INT64_FORMAT "\n"
962 "innerAllTheSame: " INT64_FORMAT "\n"
963 "leafPlaceholders: " INT64_FORMAT "\n"
964 "innerPlaceholders: " INT64_FORMAT "\n"
965 "leafRedirects: " INT64_FORMAT "\n"
966 "innerRedirects: " INT64_FORMAT,
967 totalPages, deletedPages, innerPages, leafPages, emptyPages,
969 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
970 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
971 leafTuples, innerTuples, nAllTheSame,
972 nLeafPlaceholder, nInnerPlaceholder,
973 nLeafRedirect, nInnerRedirect);
975 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
979 #if PG_VERSION_NUM >= 90200
981 typedef struct SPGistPrintStackElem {
982 ItemPointerData iptr;
985 } SPGistPrintStackElem;
987 typedef struct SPGistPrint {
990 Datum dvalues[7 /* see CreateTemplateTupleDesc call */];
991 char nulls[7 /* see CreateTemplateTupleDesc call */];
996 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
997 MemoryContext oldcontext;
998 SPGistPrintStackElem *e;
1000 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1002 e = palloc(sizeof(*e));
1006 prst->stack = lcons(e, prst->stack);
1008 MemoryContextSwitchTo(oldcontext);
1012 close_spgist_print(SPGistPrint *prst) {
1013 index_close(prst->index, AccessExclusiveLock);
1017 PG_FUNCTION_INFO_V1(spgist_print);
1018 Datum spgist_print(PG_FUNCTION_ARGS);
1020 spgist_print(PG_FUNCTION_ARGS)
1022 #if PG_VERSION_NUM < 90200
1023 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1025 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1027 FuncCallContext *funcctx;
1029 SPGistPrintStackElem *s;
1032 MemoryContext oldcontext;
1034 if (SRF_IS_FIRSTCALL()) {
1035 text *name=PG_GETARG_TEXT_P(0);
1038 ItemPointerData ipd;
1041 funcctx = SRF_FIRSTCALL_INIT();
1042 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1043 index = relation_openrv(relvar, AccessExclusiveLock);
1045 if (!IS_INDEX(index) || !IS_SPGIST(index))
1046 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1047 RelationGetRelationName(index));
1049 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1051 prst = palloc(sizeof(*prst));
1053 prst->index = index;
1054 initSpGistState(&prst->state, index);
1056 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */, false);
1057 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1058 TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0);
1059 TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0);
1060 TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0);
1061 TupleDescInitEntry(tupdesc, 5, "prefix",
1062 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1063 TupleDescInitEntry(tupdesc, 6, "label",
1064 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1065 TupleDescInitEntry(tupdesc, 7, "leaf",
1066 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1068 funcctx->slot = TupleDescGetSlot(tupdesc);
1069 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1071 funcctx->user_fctx = (void*)prst;
1073 MemoryContextSwitchTo(oldcontext);
1075 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1077 pushSPGistPrint(funcctx, prst, &ipd, 1);
1079 PG_FREE_IF_COPY(name,0);
1082 funcctx = SRF_PERCALL_SETUP();
1083 prst = (SPGistPrint*)(funcctx->user_fctx);
1087 if ( prst->stack == NIL ) {
1088 close_spgist_print(prst);
1089 SRF_RETURN_DONE(funcctx);
1092 CHECK_FOR_INTERRUPTS();
1094 s = (SPGistPrintStackElem*)linitial(prst->stack);
1095 prst->stack = list_delete_first(prst->stack);
1097 if (ItemPointerIsValid(&s->iptr))
1105 SpGistDeadTuple dtuple;
1108 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1109 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1111 page = BufferGetPage(buffer);
1112 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1113 UnlockReleaseBuffer(buffer);
1118 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1120 if (dtuple->tupstate != SPGIST_LIVE) {
1121 UnlockReleaseBuffer(buffer);
1126 if (SpGistPageIsLeaf(page)) {
1127 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1129 tid = palloc(sizeof(ItemPointerData));
1131 prst->dvalues[0] = PointerGetDatum(tid);
1132 prst->nulls[0] = ' ';
1133 prst->nulls[1] = 'n';
1134 prst->dvalues[2] = s->level;
1135 prst->nulls[2] = ' ';
1136 prst->nulls[3] = 'n';
1137 prst->nulls[4] = 'n';
1138 prst->nulls[5] = 'n';
1139 prst->dvalues[6] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1140 prst->state.attType.attbyval, prst->state.attType.attlen);
1141 prst->nulls[6] = ' ';
1143 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1145 SpGistNodeTuple node;
1147 SGITITERATE(innerTuple, i, node) {
1148 if (ItemPointerIsValid(&node->t_tid)) {
1154 if (i >= innerTuple->nNodes) {
1155 UnlockReleaseBuffer(buffer);
1160 tid = palloc(sizeof(ItemPointerData));
1162 prst->dvalues[0] = PointerGetDatum(tid);
1163 prst->nulls[0] = ' ';
1164 prst->dvalues[1] = Int32GetDatum(s->nlabel);
1165 prst->nulls[1] = ' ';
1166 prst->dvalues[2] = s->level;
1167 prst->nulls[2] = ' ';
1168 tid = palloc(sizeof(ItemPointerData));
1170 prst->dvalues[3] = PointerGetDatum(tid);
1171 prst->nulls[3] = ' ';
1172 if (prst->state.attPrefixType.attbyval == false && innerTuple->prefixSize > 0) {
1173 prst->dvalues[4] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1174 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1175 prst->nulls[4] = ' ';
1177 prst->nulls[4] = 'n';
1178 if (prst->state.attLabelType.attbyval == false && !IndexTupleHasNulls(node)) {
1179 prst->dvalues[5] = datumCopy(SGNTDATUM(node, &prst->state),
1180 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1181 prst->nulls[5] = ' ';
1183 prst->nulls[5] = 'n';
1184 prst->nulls[6] = 'n';
1186 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1188 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1189 prst->stack = lcons(s, prst->stack);
1190 MemoryContextSwitchTo(oldcontext);
1194 UnlockReleaseBuffer(buffer);
1198 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1199 result = TupleGetDatum(funcctx->slot, htuple);
1201 SRF_RETURN_NEXT(funcctx, result);