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 #if PG_VERSION_NUM >= 100000
31 #include <utils/regproc.h>
32 #include <utils/varlena.h>
34 #include <utils/tqual.h>
35 #include "utils/builtins.h"
36 #include "utils/lsyscache.h"
37 #include "utils/datum.h"
38 #include "utils/fmgroids.h"
41 #include <access/heapam.h>
42 #include <catalog/pg_type.h>
43 #include <access/relscan.h>
46 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
49 #define PG_NARGS() (fcinfo->nargs)
52 #if PG_VERSION_NUM >= 90600
54 #define ISNOTNULL false
55 #define heap_formtuple heap_form_tuple
63 char *out=palloc(VARSIZE(in));
65 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
66 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
77 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
79 #ifdef PG_MODULE_MAGIC
85 gist_index_open(RangeVar *relvar) {
86 #if PG_VERSION_NUM < 90200
87 Oid relOid = RangeVarGetRelid(relvar, false);
89 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
91 return checkOpenedRelation(
92 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
95 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
98 gin_index_open(RangeVar *relvar) {
99 #if PG_VERSION_NUM < 90200
100 Oid relOid = RangeVarGetRelid(relvar, false);
102 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
104 return checkOpenedRelation(
105 index_open(relOid, AccessShareLock), GIN_AM_OID);
108 #define gin_index_close(r) index_close((r), AccessShareLock)
113 gist_index_open(RangeVar *relvar) {
114 Relation rel = index_openrv(relvar);
116 LockRelation(rel, AccessExclusiveLock);
117 return checkOpenedRelation(rel, GIST_AM_OID);
121 gist_index_close(Relation rel) {
122 UnlockRelation(rel, AccessExclusiveLock);
127 gin_index_open(RangeVar *relvar) {
128 Relation rel = index_openrv(relvar);
130 LockRelation(rel, AccessShareLock);
131 return checkOpenedRelation(rel, GIN_AM_OID);
135 gin_index_close(Relation rel) {
136 UnlockRelation(rel, AccessShareLock);
142 #if PG_VERSION_NUM >= 80300
143 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
146 #if PG_VERSION_NUM < 80300
147 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
151 checkOpenedRelation(Relation r, Oid PgAmOid) {
152 if ( r->rd_index == NULL )
153 elog(ERROR, "Relation %s.%s is not an index",
154 get_namespace_name(RelationGetNamespace(r)),
155 RelationGetRelationName(r)
158 if ( r->rd_rel->relam != PgAmOid )
159 elog(ERROR, "Index %s.%s has wrong type",
160 get_namespace_name(RelationGetNamespace(r)),
161 RelationGetRelationName(r)
168 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
178 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
179 MemSet(pred, ' ', level*4);
180 pred[level*4] = '\0';
182 buffer = ReadBuffer(r, blk);
183 page = (Page) BufferGetPage(buffer);
185 maxoff = PageGetMaxOffsetNumber(page);
188 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
189 int dist=info->ptr-((char*)info->txt);
191 info->txt=(text*)repalloc(info->txt, info->len);
192 info->ptr = ((char*)info->txt)+dist;
195 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
201 (int) PageGetFreeSpace(page),
202 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
203 GistPageGetOpaque(page)->rightlink,
204 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
205 info->ptr=strchr(info->ptr,'\0');
207 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
208 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
209 iid = PageGetItemId(page, i);
210 which = (IndexTuple) PageGetItem(page, iid);
211 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
212 gist_dumptree(r, level + 1, cblk, i, info);
214 ReleaseBuffer(buffer);
218 PG_FUNCTION_INFO_V1(gist_tree);
219 Datum gist_tree(PG_FUNCTION_ARGS);
221 gist_tree(PG_FUNCTION_ARGS) {
222 text *name=PG_GETARG_TEXT_P(0);
223 char *relname=t2c(name);
229 relname_list = stringToQualifiedNameList(relname, "gist_tree");
230 relvar = makeRangeVarFromNameList(relname_list);
231 index = gist_index_open(relvar);
232 PG_FREE_IF_COPY(name,0);
234 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
236 info.txt=(text*)palloc( info.len );
237 info.ptr=((char*)info.txt)+VARHDRSZ;
239 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
241 gist_index_close(index);
244 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
245 PG_RETURN_POINTER(info.txt);
256 uint64 leaftuplesize;
261 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
271 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
272 MemSet(pred, ' ', level*4);
273 pred[level*4] = '\0';
275 buffer = ReadBuffer(r, blk);
276 page = (Page) BufferGetPage(buffer);
278 maxoff = PageGetMaxOffsetNumber(page);
281 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
282 info->totalsize+=BLCKSZ;
283 info->numtuple+=maxoff;
284 if ( info->level < level )
287 if (GistPageIsLeaf(page)) {
288 info->numleafpages++;
289 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
290 info->numleaftuple+=maxoff;
292 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
293 iid = PageGetItemId(page, i);
294 which = (IndexTuple) PageGetItem(page, iid);
295 if ( GistTupleIsInvalid(which) )
296 info->numinvalidtuple++;
297 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
298 gist_stattree(r, level + 1, cblk, i, info);
302 ReleaseBuffer(buffer);
306 PG_FUNCTION_INFO_V1(gist_stat);
307 Datum gist_stat(PG_FUNCTION_ARGS);
309 gist_stat(PG_FUNCTION_ARGS) {
310 text *name=PG_GETARG_TEXT_P(0);
311 char *relname=t2c(name);
316 text *out=(text*)palloc(1024);
317 char *ptr=((char*)out)+VARHDRSZ;
320 relname_list = stringToQualifiedNameList(relname, "gist_tree");
321 relvar = makeRangeVarFromNameList(relname_list);
322 index = gist_index_open(relvar);
323 PG_FREE_IF_COPY(name,0);
325 memset(&info, 0, sizeof(IdxStat));
327 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
329 gist_index_close(index);
333 "Number of levels: %d\n"
334 "Number of pages: %d\n"
335 "Number of leaf pages: %d\n"
336 "Number of tuples: %d\n"
337 "Number of invalid tuples: %d\n"
338 "Number of leaf tuples: %d\n"
339 "Total size of tuples: "INT64_FORMAT" bytes\n"
340 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
341 "Total size of index: "INT64_FORMAT" bytes\n",
346 info.numinvalidtuple,
352 ptr=strchr(ptr,'\0');
354 SET_VARSIZE(out, ptr-((char*)out));
355 PG_RETURN_POINTER(out);
358 typedef struct GPItem {
371 #if PG_VERSION_NUM >= 90600
380 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
382 MemoryContext oldcontext;
383 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
385 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
386 nitem = (GPItem*)palloc( sizeof(GPItem) );
387 memset(nitem,0,sizeof(GPItem));
389 nitem->buffer = ReadBuffer(index, blk);
390 nitem->page = (Page) BufferGetPage(nitem->buffer);
391 nitem->offset=FirstOffsetNumber;
392 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
393 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
394 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
396 MemoryContextSwitchTo(oldcontext);
401 closeGPPage( FuncCallContext *funcctx ) {
402 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
404 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
406 ReleaseBuffer(oitem->buffer);
408 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
411 #if PG_VERSION_NUM >= 110000
412 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)].v
414 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)]->v
418 setup_firstcall(FuncCallContext *funcctx, text *name) {
419 MemoryContext oldcontext;
421 char *relname=t2c(name);
423 char attname[NAMEDATALEN];
426 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
428 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
429 memset(st,0,sizeof(TypeStorage));
430 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
431 st->relvar = makeRangeVarFromNameList(st->relname_list);
432 st->index = gist_index_open(st->relvar);
433 funcctx->user_fctx = (void*)st;
435 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
436 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
437 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
438 for (i = 0; i < st->index->rd_att->natts; i++) {
439 sprintf(attname, "z%d", i+2);
444 TS_GET_TYPEVAL(st, i, atttypid),
445 TS_GET_TYPEVAL(st, i, atttypmod),
446 TS_GET_TYPEVAL(st, i, attndims)
450 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
451 st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
453 funcctx->slot = TupleDescGetSlot(tupdesc);
454 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
456 MemoryContextSwitchTo(oldcontext);
459 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
463 close_call( FuncCallContext *funcctx ) {
464 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
466 while(st->item && closeGPPage(funcctx));
471 gist_index_close(st->index);
474 PG_FUNCTION_INFO_V1(gist_print);
475 Datum gist_print(PG_FUNCTION_ARGS);
477 gist_print(PG_FUNCTION_ARGS) {
478 FuncCallContext *funcctx;
480 Datum result=(Datum)0;
487 if (SRF_IS_FIRSTCALL()) {
488 text *name=PG_GETARG_TEXT_P(0);
489 funcctx = SRF_FIRSTCALL_INIT();
490 setup_firstcall(funcctx, name);
491 PG_FREE_IF_COPY(name,0);
494 funcctx = SRF_PERCALL_SETUP();
495 st = (TypeStorage*)(funcctx->user_fctx);
499 SRF_RETURN_DONE(funcctx);
502 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
503 if ( ! closeGPPage(funcctx) ) {
505 SRF_RETURN_DONE(funcctx);
509 iid = PageGetItemId( st->item->page, st->item->offset );
510 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
512 st->dvalues[0] = Int32GetDatum( st->item->level );
513 st->nulls[0] = ISNOTNULL;
514 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
515 st->nulls[1] = ISNOTNULL;
516 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
517 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
518 st->dvalues[i] = (Datum)0;
519 st->nulls[i] = ISNULL;
521 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
522 st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
526 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
527 result = TupleGetDatum(funcctx->slot, htuple);
528 st->item->offset = OffsetNumberNext(st->item->offset);
529 if ( !GistPageIsLeaf(st->item->page) )
530 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
532 SRF_RETURN_NEXT(funcctx, result);
535 typedef struct GinStatState {
543 #if PG_VERSION_NUM >= 90100
544 GinNullCategory category;
547 #if PG_VERSION_NUM >= 110000
555 moveRightIfItNeeded( GinStatState *st )
557 Page page = BufferGetPage(st->buffer);
559 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
561 * We scaned the whole page, so we should take right page
563 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
565 if ( GinPageRightMost(page) )
566 return false; /* no more page */
568 LockBuffer(st->buffer, GIN_UNLOCK);
569 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
570 LockBuffer(st->buffer, GIN_SHARE);
571 st->offset = FirstOffsetNumber;
578 * Refinds a previois position, at returns it has correctly
579 * set offset and buffer is locked
582 refindPosition(GinStatState *st)
586 /* find left if needed (it causes only for first search) */
591 LockBuffer(st->buffer, GIN_SHARE);
593 page = BufferGetPage(st->buffer);
594 if (GinPageIsLeaf(page))
597 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
598 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
600 LockBuffer(st->buffer,GIN_UNLOCK);
601 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
604 if (st->offset == InvalidOffsetNumber) {
605 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
610 #if PG_VERSION_NUM >= 90100
611 GinNullCategory category;
612 #elif PG_VERSION_NUM < 80400
618 if (moveRightIfItNeeded(st)==false)
621 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
622 #if PG_VERSION_NUM >= 90100
623 datum = gintuple_get_key(&st->ginstate, itup, &category);
624 cmp = ginCompareAttEntries(&st->ginstate,
625 st->attnum + 1, st->curval, st->category,
626 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
627 #elif PG_VERSION_NUM >= 80400
628 datum = gin_index_getattr(&st->ginstate, itup);
630 cmp = compareAttEntries(&st->ginstate,
631 st->attnum + 1, st->curval,
632 gintuple_get_attrnum(&st->ginstate, itup), datum);
634 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
638 &st->ginstate.compareFn,
645 if ( st->curval && !TS_GET_TYPEVAL(st, st->attnum, attbyval) )
646 pfree( (void*) st->curval );
657 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
658 MemoryContext oldcontext;
660 char *relname=t2c(name);
663 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
665 st=(GinStatState*)palloc( sizeof(GinStatState) );
666 memset(st,0,sizeof(GinStatState));
667 st->index = gin_index_open(
668 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
669 initGinState( &st->ginstate, st->index );
671 #if PG_VERSION_NUM >= 80400
672 if (attnum < 0 || attnum >= st->index->rd_att->natts)
673 elog(ERROR,"Wrong column's number");
679 funcctx->user_fctx = (void*)st;
681 tupdesc = CreateTemplateTupleDesc(2, false);
682 TupleDescInitEntry(tupdesc, 1, "value",
683 TS_GET_TYPEVAL(st, st->attnum, atttypid),
684 TS_GET_TYPEVAL(st, st->attnum, atttypmod),
685 TS_GET_TYPEVAL(st, st->attnum, attndims));
686 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
688 memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
690 funcctx->slot = TupleDescGetSlot(tupdesc);
691 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
693 MemoryContextSwitchTo(oldcontext);
696 st->offset = InvalidOffsetNumber;
697 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
701 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
702 MemoryContext oldcontext;
703 #if PG_VERSION_NUM >= 90100
705 #elif PG_VERSION_NUM < 80400
709 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
711 #if PG_VERSION_NUM >= 90100
712 key = gintuple_get_key(&st->ginstate, itup, &st->category);
714 if (st->category != GIN_CAT_NORM_KEY)
715 st->curval = (Datum)0;
718 st->curval = datumCopy(
719 #if PG_VERSION_NUM >= 90100
721 #elif PG_VERSION_NUM >= 80400
722 gin_index_getattr(&st->ginstate, itup),
724 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
726 TS_GET_TYPEVAL(st, st->attnum, attbyval),
727 TS_GET_TYPEVAL(st, st->attnum, attlen));
728 MemoryContextSwitchTo(oldcontext);
730 st->dvalues[0] = st->curval;
731 #if PG_VERSION_NUM >= 90100
732 /* do no distiguish various null category */
733 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
736 if ( GinIsPostingTree(itup) ) {
737 BlockNumber rootblkno = GinGetPostingTree(itup);
738 #if PG_VERSION_NUM >= 90400
740 GinBtreeStack *stack;
741 ItemPointerData minItem;
745 GinPostingTreeScan *gdi;
749 uint32 predictNumber;
751 LockBuffer(st->buffer, GIN_UNLOCK);
752 #if PG_VERSION_NUM >= 90400
753 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno
754 #if PG_VERSION_NUM >= 90600
758 page = BufferGetPage(stack->buffer);
759 ItemPointerSetMin(&minItem);
760 list = GinDataLeafPageGetItems(page, &nlist, minItem);
762 predictNumber = stack->predictNumber;
763 st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
764 #elif PG_VERSION_NUM >= 90100
765 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
766 entrybuffer = ginScanBeginPostingTree(gdi);
767 page = BufferGetPage(entrybuffer);
768 predictNumber = gdi->stack->predictNumber;
769 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
771 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
772 entrybuffer = scanBeginPostingTree(gdi);
773 page = BufferGetPage(entrybuffer);
774 predictNumber = gdi->stack->predictNumber;
775 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
778 #if PG_VERSION_NUM < 90400
779 LockBuffer(entrybuffer, GIN_UNLOCK);
780 freeGinBtreeStack(gdi->stack);
783 LockBuffer(stack->buffer, GIN_UNLOCK);
784 freeGinBtreeStack(stack);
787 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
788 LockBuffer(st->buffer, GIN_UNLOCK);
792 PG_FUNCTION_INFO_V1(gin_stat);
793 Datum gin_stat(PG_FUNCTION_ARGS);
795 gin_stat(PG_FUNCTION_ARGS) {
796 FuncCallContext *funcctx;
798 Datum result=(Datum)0;
803 if (SRF_IS_FIRSTCALL()) {
804 text *name=PG_GETARG_TEXT_P(0);
805 funcctx = SRF_FIRSTCALL_INIT();
806 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
807 PG_FREE_IF_COPY(name,0);
810 funcctx = SRF_PERCALL_SETUP();
811 st = (GinStatState*)(funcctx->user_fctx);
813 if ( refindPosition(st) == false ) {
814 UnlockReleaseBuffer( st->buffer );
815 gin_index_close(st->index);
817 SRF_RETURN_DONE(funcctx);
823 if (moveRightIfItNeeded(st)==false) {
824 UnlockReleaseBuffer( st->buffer );
825 gin_index_close(st->index);
827 SRF_RETURN_DONE(funcctx);
830 page = BufferGetPage(st->buffer);
831 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
833 #if PG_VERSION_NUM >= 80400
834 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
839 processTuple( funcctx, st, ituple );
841 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
842 result = TupleGetDatum(funcctx->slot, htuple);
844 SRF_RETURN_NEXT(funcctx, result);
847 PG_FUNCTION_INFO_V1(gin_count_estimate);
848 Datum gin_count_estimate(PG_FUNCTION_ARGS);
849 #if PG_VERSION_NUM >= 80300
851 gin_count_estimate(PG_FUNCTION_ARGS) {
852 text *name=PG_GETARG_TEXT_P(0);
856 char *relname=t2c(name);
858 #if PG_VERSION_NUM >= 80400
859 TIDBitmap *bitmap = tbm_create(work_mem * 1024L
860 #if PG_VERSION_NUM >= 100000
866 ItemPointerData tids[MAXTIDS];
871 index = gin_index_open(
872 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
874 if ( index->rd_opcintype[0] != TSVECTOROID ) {
875 gin_index_close(index);
876 elog(ERROR, "Column type is not a tsvector");
881 key.sk_strategy = TSearchStrategyNumber;
883 key.sk_argument = PG_GETARG_DATUM(1);
885 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
887 #if PG_VERSION_NUM >= 90100
888 #if PG_VERSION_NUM >= 90400
889 scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
891 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
893 index_rescan(scan, &key, 1, NULL, 0);
895 count = index_getbitmap(scan, bitmap);
897 #elif PG_VERSION_NUM >= 80400
898 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
900 count = index_getbitmap(scan, bitmap);
903 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
906 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
907 count += returned_tids;
911 index_endscan( scan );
912 gin_index_close(index);
914 PG_RETURN_INT64(count);
918 gin_count_estimate(PG_FUNCTION_ARGS) {
919 elog(NOTICE, "Function is not working under PgSQL < 8.3");
925 PG_FUNCTION_INFO_V1(spgist_stat);
926 Datum spgist_stat(PG_FUNCTION_ARGS);
928 spgist_stat(PG_FUNCTION_ARGS)
930 #if PG_VERSION_NUM < 90200
931 elog(NOTICE, "Function is not working under PgSQL < 9.2");
933 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
935 text *name = PG_GETARG_TEXT_P(0);
939 BlockNumber totalPages = 0,
944 double usedSpace = 0.0,
946 usedInnerSpace = 0.0;
949 int64 innerTuples = 0,
952 nLeafPlaceholder = 0,
953 nInnerPlaceholder = 0,
957 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
958 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
960 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
961 index = relation_openrv(relvar, AccessExclusiveLock);
963 if (!IS_INDEX(index) || !IS_SPGIST(index))
964 elog(ERROR, "relation \"%s\" is not an SPGiST index",
965 RelationGetRelationName(index));
967 totalPages = RelationGetNumberOfBlocks(index);
969 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
975 buffer = ReadBuffer(index, blkno);
976 LockBuffer(buffer, BUFFER_LOCK_SHARE);
978 page = BufferGetPage(buffer);
980 if (PageIsNew(page) || SpGistPageIsDeleted(page))
983 UnlockReleaseBuffer(buffer);
987 if (SpGistPageIsLeaf(page))
990 leafTuples += PageGetMaxOffsetNumber(page);
991 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
992 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
1000 max = PageGetMaxOffsetNumber(page);
1002 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
1003 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
1004 for (i = FirstOffsetNumber; i <= max; i++)
1006 SpGistInnerTuple it;
1008 it = (SpGistInnerTuple) PageGetItem(page,
1009 PageGetItemId(page, i));
1016 bufferSize = BufferGetPageSize(buffer)
1017 - MAXALIGN(sizeof(SpGistPageOpaqueData))
1018 - SizeOfPageHeaderData;
1020 pageFree = PageGetExactFreeSpace(page);
1022 usedSpace += bufferSize - pageFree;
1023 if (SpGistPageIsLeaf(page))
1024 usedLeafSpace += bufferSize - pageFree;
1026 usedInnerSpace += bufferSize - pageFree;
1028 if (pageFree == bufferSize)
1031 UnlockReleaseBuffer(buffer);
1034 index_close(index, AccessExclusiveLock);
1036 totalPages--; /* discount metapage */
1038 snprintf(res, sizeof(res),
1040 "deletedPages: %u\n"
1044 "usedSpace: %.2f kbytes\n"
1045 "usedInnerSpace: %.2f kbytes\n"
1046 "usedLeafSpace: %.2f kbytes\n"
1047 "freeSpace: %.2f kbytes\n"
1048 "fillRatio: %.2f%%\n"
1049 "leafTuples: " INT64_FORMAT "\n"
1050 "innerTuples: " INT64_FORMAT "\n"
1051 "innerAllTheSame: " INT64_FORMAT "\n"
1052 "leafPlaceholders: " INT64_FORMAT "\n"
1053 "innerPlaceholders: " INT64_FORMAT "\n"
1054 "leafRedirects: " INT64_FORMAT "\n"
1055 "innerRedirects: " INT64_FORMAT,
1056 totalPages, deletedPages, innerPages, leafPages, emptyPages,
1058 usedInnerSpace / 1024.0,
1059 usedLeafSpace / 1024.0,
1060 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1061 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1062 leafTuples, innerTuples, nAllTheSame,
1063 nLeafPlaceholder, nInnerPlaceholder,
1064 nLeafRedirect, nInnerRedirect);
1066 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1070 #if PG_VERSION_NUM >= 90200
1072 typedef struct SPGistPrintStackElem {
1073 ItemPointerData iptr;
1076 } SPGistPrintStackElem;
1078 typedef struct SPGistPrint {
1081 Datum dvalues[8 /* see CreateTemplateTupleDesc call */];
1082 #if PG_VERSION_NUM >= 110000
1091 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1092 MemoryContext oldcontext;
1093 SPGistPrintStackElem *e;
1095 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1097 e = palloc(sizeof(*e));
1101 prst->stack = lcons(e, prst->stack);
1103 MemoryContextSwitchTo(oldcontext);
1107 close_spgist_print(SPGistPrint *prst) {
1108 index_close(prst->index, AccessExclusiveLock);
1112 PG_FUNCTION_INFO_V1(spgist_print);
1113 Datum spgist_print(PG_FUNCTION_ARGS);
1115 spgist_print(PG_FUNCTION_ARGS)
1117 #if PG_VERSION_NUM < 90200
1118 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1120 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1122 FuncCallContext *funcctx;
1124 SPGistPrintStackElem *s;
1127 MemoryContext oldcontext;
1129 if (SRF_IS_FIRSTCALL()) {
1130 text *name=PG_GETARG_TEXT_P(0);
1133 ItemPointerData ipd;
1136 funcctx = SRF_FIRSTCALL_INIT();
1137 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1138 index = relation_openrv(relvar, AccessExclusiveLock);
1140 if (!IS_INDEX(index) || !IS_SPGIST(index))
1141 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1142 RelationGetRelationName(index));
1144 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1146 prst = palloc(sizeof(*prst));
1148 prst->index = index;
1149 initSpGistState(&prst->state, index);
1151 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
1152 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1153 TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
1154 TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
1155 TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
1156 TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
1157 TupleDescInitEntry(tupdesc, 6, "prefix",
1158 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1159 TupleDescInitEntry(tupdesc, 7, "label",
1160 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1161 TupleDescInitEntry(tupdesc, 8, "leaf",
1162 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1164 funcctx->slot = TupleDescGetSlot(tupdesc);
1165 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1167 funcctx->user_fctx = (void*)prst;
1169 MemoryContextSwitchTo(oldcontext);
1171 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1173 pushSPGistPrint(funcctx, prst, &ipd, 1);
1175 PG_FREE_IF_COPY(name,0);
1178 funcctx = SRF_PERCALL_SETUP();
1179 prst = (SPGistPrint*)(funcctx->user_fctx);
1183 if ( prst->stack == NIL ) {
1184 close_spgist_print(prst);
1185 SRF_RETURN_DONE(funcctx);
1188 CHECK_FOR_INTERRUPTS();
1190 s = (SPGistPrintStackElem*)linitial(prst->stack);
1191 prst->stack = list_delete_first(prst->stack);
1193 if (ItemPointerIsValid(&s->iptr))
1201 SpGistDeadTuple dtuple;
1204 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1205 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1207 page = BufferGetPage(buffer);
1208 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1209 UnlockReleaseBuffer(buffer);
1214 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1216 if (dtuple->tupstate != SPGIST_LIVE) {
1217 UnlockReleaseBuffer(buffer);
1222 if (SpGistPageIsLeaf(page)) {
1223 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1225 tid = palloc(sizeof(ItemPointerData));
1227 prst->dvalues[0] = PointerGetDatum(tid);
1228 prst->nulls[0] = ISNOTNULL;
1229 prst->nulls[1] = ISNULL;
1230 prst->nulls[2] = ISNULL;
1231 prst->dvalues[3] = s->level;
1232 prst->nulls[3] = ISNOTNULL;
1233 prst->nulls[4] = ISNULL;
1234 prst->nulls[5] = ISNULL;
1235 prst->nulls[6] = ISNULL;
1236 prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1237 prst->state.attType.attbyval, prst->state.attType.attlen);
1238 prst->nulls[7] = ISNOTNULL;
1240 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1242 SpGistNodeTuple node;
1244 SGITITERATE(innerTuple, i, node) {
1245 if (ItemPointerIsValid(&node->t_tid)) {
1251 if (i >= innerTuple->nNodes) {
1252 UnlockReleaseBuffer(buffer);
1257 tid = palloc(sizeof(ItemPointerData));
1259 prst->dvalues[0] = PointerGetDatum(tid);
1260 prst->nulls[0] = ISNOTNULL;
1261 prst->dvalues[1] = innerTuple->allTheSame;
1262 prst->nulls[1] = ISNOTNULL;
1263 prst->dvalues[2] = Int32GetDatum(s->nlabel);
1264 prst->nulls[2] = ISNOTNULL;
1265 prst->dvalues[3] = s->level;
1266 prst->nulls[3] = ISNOTNULL;
1267 tid = palloc(sizeof(ItemPointerData));
1269 prst->dvalues[4] = PointerGetDatum(tid);
1270 prst->nulls[5] = ISNOTNULL;
1271 if (innerTuple->prefixSize > 0) {
1272 prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1273 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1274 prst->nulls[5] = ISNOTNULL;
1276 prst->nulls[5] = ISNULL;
1277 if (!IndexTupleHasNulls(node)) {
1278 prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
1279 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1280 prst->nulls[6] = ISNOTNULL;
1282 prst->nulls[6] = ISNULL;
1283 prst->nulls[7] = ISNULL;
1285 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1287 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1288 prst->stack = lcons(s, prst->stack);
1289 MemoryContextSwitchTo(oldcontext);
1293 UnlockReleaseBuffer(buffer);
1297 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1298 result = TupleGetDatum(funcctx->slot, htuple);
1300 SRF_RETURN_NEXT(funcctx, result);
1305 PG_FUNCTION_INFO_V1(gin_statpage);
1306 Datum gin_statpage(PG_FUNCTION_ARGS);
1308 gin_statpage(PG_FUNCTION_ARGS)
1310 #if PG_VERSION_NUM < 90400
1311 elog(NOTICE, "Function is not working under PgSQL < 9.4");
1313 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1315 text *name = PG_GETARG_TEXT_P(0);
1321 #if PG_VERSION_NUM >= 100000
1329 entryInnerPages = 0,
1332 uint64 dataInnerFreeSpace = 0,
1333 dataLeafFreeSpace = 0,
1334 dataInnerTuplesCount = 0,
1335 dataLeafIptrsCount = 0,
1336 entryInnerFreeSpace = 0,
1337 entryLeafFreeSpace = 0,
1338 entryInnerTuplesCount = 0,
1339 entryLeafTuplesCount = 0,
1340 entryPostingSize = 0,
1341 entryPostingCount = 0,
1345 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1346 index = relation_openrv(relvar, AccessExclusiveLock);
1348 if (index->rd_rel->relkind != RELKIND_INDEX ||
1349 index->rd_rel->relam != GIN_AM_OID)
1350 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1351 RelationGetRelationName(index));
1353 totalPages = RelationGetNumberOfBlocks(index);
1355 for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1361 buffer = ReadBuffer(index, blkno);
1362 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1364 page = BufferGetPage(buffer);
1365 header = (PageHeader)page;
1367 #if PG_VERSION_NUM >= 100000
1368 if (GinPageIsDeleted(page))
1374 if (GinPageIsData(page))
1377 if (GinPageIsLeaf(page))
1379 ItemPointerData minItem, *ptr;
1384 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1385 ItemPointerSetMin(&minItem);
1387 ptr = GinDataLeafPageGetItems(page, &nlist, minItem);
1392 dataLeafIptrsCount += nlist;
1400 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1401 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1407 OffsetNumber i, maxoff;
1409 maxoff = PageGetMaxOffsetNumber(page);
1412 if (GinPageIsLeaf(page))
1415 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1416 entryLeafTuplesCount += maxoff;
1421 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1422 entryInnerTuplesCount += maxoff;
1425 for (i = 1; i <= maxoff; i++)
1427 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1429 if (GinPageIsLeaf(page))
1431 GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1432 entryPostingCount += GinGetNPosting(itup);
1433 entryPostingSize += SizeOfGinPostingList(list);
1434 entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1438 entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1443 UnlockReleaseBuffer(buffer);
1446 index_close(index, AccessExclusiveLock);
1449 snprintf(res, sizeof(res),
1451 #if PG_VERSION_NUM >= 100000
1452 "deletedPages: %u\n"
1453 "emptyDataPages: %u\n"
1456 "dataInnerPages: %u\n"
1457 "dataLeafPages: %u\n"
1458 "dataInnerFreeSpace: " INT64_FORMAT "\n"
1459 "dataLeafFreeSpace: " INT64_FORMAT "\n"
1460 "dataInnerTuplesCount: " INT64_FORMAT "\n"
1461 "dataLeafIptrsCount: " INT64_FORMAT "\n"
1463 "entryInnerPages: %u\n"
1464 "entryLeafPages: %u\n"
1465 "entryInnerFreeSpace: " INT64_FORMAT "\n"
1466 "entryLeafFreeSpace: " INT64_FORMAT "\n"
1467 "entryInnerTuplesCount: " INT64_FORMAT "\n"
1468 "entryLeafTuplesCount: " INT64_FORMAT "\n"
1469 "entryPostingSize: " INT64_FORMAT "\n"
1470 "entryPostingCount: " INT64_FORMAT "\n"
1471 "entryAttrSize: " INT64_FORMAT "\n"
1474 #if PG_VERSION_NUM >= 100000
1483 dataInnerTuplesCount,
1488 entryInnerFreeSpace,
1490 entryInnerTuplesCount,
1491 entryLeafTuplesCount,
1497 PG_RETURN_TEXT_P(CStringGetTextDatum(res));