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/builtins.h"
35 #include "utils/lsyscache.h"
36 #include "utils/datum.h"
37 #include "utils/fmgroids.h"
40 #include <access/heapam.h>
41 #include <catalog/pg_type.h>
42 #include <access/relscan.h>
43 #if PG_VERSION_NUM >= 120000
44 #include <access/nbtree.h>
45 #include <access/brin.h>
46 #include <access/brin_revmap.h>
47 #include <access/brin_page.h>
48 #include <access/brin_tuple.h>
52 #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
55 #define PG_NARGS() (fcinfo->nargs)
58 #if PG_VERSION_NUM >= 90600
60 #define ISNOTNULL false
61 #define heap_formtuple heap_form_tuple
69 char *out=palloc(VARSIZE(in));
71 memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
72 out[ VARSIZE(in)-VARHDRSZ ] ='\0';
83 static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
85 #ifdef PG_MODULE_MAGIC
91 gist_index_open(RangeVar *relvar) {
92 #if PG_VERSION_NUM < 90200
93 Oid relOid = RangeVarGetRelid(relvar, false);
95 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
97 return checkOpenedRelation(
98 index_open(relOid, AccessExclusiveLock), GIST_AM_OID);
101 #define gist_index_close(r) index_close((r), AccessExclusiveLock)
104 gin_index_open(RangeVar *relvar) {
105 #if PG_VERSION_NUM < 90200
106 Oid relOid = RangeVarGetRelid(relvar, false);
108 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
110 return checkOpenedRelation(
111 index_open(relOid, AccessShareLock), GIN_AM_OID);
114 #define gin_index_close(r) index_close((r), AccessShareLock)
116 #if PG_VERSION_NUM >= 120000
118 btree_index_open(RangeVar *relvar) {
119 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
120 return checkOpenedRelation(
121 index_open(relOid, AccessExclusiveLock), BTREE_AM_OID);
124 #define btree_index_close(r) index_close((r), AccessExclusiveLock)
127 brin_index_open(RangeVar *relvar)
129 Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
130 return checkOpenedRelation(
131 index_open(relOid, AccessExclusiveLock), BRIN_AM_OID);
134 #define brin_index_close(r) index_close((r), AccessExclusiveLock)
140 gist_index_open(RangeVar *relvar) {
141 Relation rel = index_openrv(relvar);
143 LockRelation(rel, AccessExclusiveLock);
144 return checkOpenedRelation(rel, GIST_AM_OID);
148 gist_index_close(Relation rel) {
149 UnlockRelation(rel, AccessExclusiveLock);
154 gin_index_open(RangeVar *relvar) {
155 Relation rel = index_openrv(relvar);
157 LockRelation(rel, AccessShareLock);
158 return checkOpenedRelation(rel, GIN_AM_OID);
162 gin_index_close(Relation rel) {
163 UnlockRelation(rel, AccessShareLock);
168 btree_index_open(RangeVar *relvar) {
169 Relation rel = index_openrv(relvar);
171 LockRelation(rel, AccessExclusiveLock);
172 return checkOpenedRelation(rel, BTREE_AM_OID);
176 btree_index_close(Relation rel) {
177 UnlockRelation(rel, AccessExclusiveLock);
183 #if PG_VERSION_NUM >= 80300
184 #define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x)
187 #if PG_VERSION_NUM < 80300
188 #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
192 checkOpenedRelation(Relation r, Oid PgAmOid) {
193 if ( r->rd_index == NULL )
194 elog(ERROR, "Relation %s.%s is not an index",
195 get_namespace_name(RelationGetNamespace(r)),
196 RelationGetRelationName(r)
199 if ( r->rd_rel->relam != PgAmOid )
200 elog(ERROR, "Index %s.%s has wrong type",
201 get_namespace_name(RelationGetNamespace(r)),
202 RelationGetRelationName(r)
209 gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) {
219 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
220 MemSet(pred, ' ', level*4);
221 pred[level*4] = '\0';
223 buffer = ReadBuffer(r, blk);
224 page = (Page) BufferGetPage(buffer);
226 maxoff = PageGetMaxOffsetNumber(page);
229 while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) {
230 int dist=info->ptr-((char*)info->txt);
232 info->txt=(text*)repalloc(info->txt, info->len);
233 info->ptr = ((char*)info->txt)+dist;
236 sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n",
242 (int) PageGetFreeSpace(page),
243 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
244 GistPageGetOpaque(page)->rightlink,
245 ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
246 info->ptr=strchr(info->ptr,'\0');
248 if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || level<info->maxlevel ) )
249 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
250 iid = PageGetItemId(page, i);
251 which = (IndexTuple) PageGetItem(page, iid);
252 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
253 gist_dumptree(r, level + 1, cblk, i, info);
255 ReleaseBuffer(buffer);
259 PG_FUNCTION_INFO_V1(gist_tree);
260 Datum gist_tree(PG_FUNCTION_ARGS);
262 gist_tree(PG_FUNCTION_ARGS) {
263 text *name=PG_GETARG_TEXT_P(0);
264 char *relname=t2c(name);
270 relname_list = stringToQualifiedNameList(relname, "gist_tree");
271 relvar = makeRangeVarFromNameList(relname_list);
272 index = gist_index_open(relvar);
273 PG_FREE_IF_COPY(name,0);
275 info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
277 info.txt=(text*)palloc( info.len );
278 info.ptr=((char*)info.txt)+VARHDRSZ;
280 gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info);
282 gist_index_close(index);
285 SET_VARSIZE(info.txt, info.ptr-((char*)info.txt));
286 PG_RETURN_POINTER(info.txt);
297 uint64 leaftuplesize;
302 gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) {
312 pred = (char *) palloc(sizeof(char) * level * 4 + 1);
313 MemSet(pred, ' ', level*4);
314 pred[level*4] = '\0';
316 buffer = ReadBuffer(r, blk);
317 page = (Page) BufferGetPage(buffer);
319 maxoff = PageGetMaxOffsetNumber(page);
322 info->tuplesize+=PAGESIZE-PageGetFreeSpace(page);
323 info->totalsize+=BLCKSZ;
324 info->numtuple+=maxoff;
325 if ( info->level < level )
328 if (GistPageIsLeaf(page)) {
329 info->numleafpages++;
330 info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page);
331 info->numleaftuple+=maxoff;
333 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
334 iid = PageGetItemId(page, i);
335 which = (IndexTuple) PageGetItem(page, iid);
336 if ( GistTupleIsInvalid(which) )
337 info->numinvalidtuple++;
338 cblk = ItemPointerGetBlockNumber(&(which->t_tid));
339 gist_stattree(r, level + 1, cblk, i, info);
343 ReleaseBuffer(buffer);
347 PG_FUNCTION_INFO_V1(gist_stat);
348 Datum gist_stat(PG_FUNCTION_ARGS);
350 gist_stat(PG_FUNCTION_ARGS) {
351 text *name=PG_GETARG_TEXT_P(0);
352 char *relname=t2c(name);
357 text *out=(text*)palloc(1024);
358 char *ptr=((char*)out)+VARHDRSZ;
361 relname_list = stringToQualifiedNameList(relname, "gist_tree");
362 relvar = makeRangeVarFromNameList(relname_list);
363 index = gist_index_open(relvar);
364 PG_FREE_IF_COPY(name,0);
366 memset(&info, 0, sizeof(IdxStat));
368 gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info);
370 gist_index_close(index);
374 "Number of levels: %d\n"
375 "Number of pages: %d\n"
376 "Number of leaf pages: %d\n"
377 "Number of tuples: %d\n"
378 "Number of invalid tuples: %d\n"
379 "Number of leaf tuples: %d\n"
380 "Total size of tuples: "INT64_FORMAT" bytes\n"
381 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
382 "Total size of index: "INT64_FORMAT" bytes\n",
387 info.numinvalidtuple,
393 ptr=strchr(ptr,'\0');
395 SET_VARSIZE(out, ptr-((char*)out));
396 PG_RETURN_POINTER(out);
399 typedef struct GPItem {
412 #if PG_VERSION_NUM >= 90600
421 openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
423 MemoryContext oldcontext;
424 Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
426 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
427 nitem = (GPItem*)palloc( sizeof(GPItem) );
428 memset(nitem,0,sizeof(GPItem));
430 nitem->buffer = ReadBuffer(index, blk);
431 nitem->page = (Page) BufferGetPage(nitem->buffer);
432 nitem->offset=FirstOffsetNumber;
433 nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
434 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
435 ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
437 MemoryContextSwitchTo(oldcontext);
442 closeGPPage( FuncCallContext *funcctx ) {
443 GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
445 ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
447 ReleaseBuffer(oitem->buffer);
449 return ( (TypeStorage*)(funcctx->user_fctx) )->item;
452 #if PG_VERSION_NUM >= 110000
453 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)].v
455 #define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)]->v
459 setup_firstcall(FuncCallContext *funcctx, text *name) {
460 MemoryContext oldcontext;
462 char *relname=t2c(name);
464 char attname[NAMEDATALEN];
467 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
469 st=(TypeStorage*)palloc( sizeof(TypeStorage) );
470 memset(st,0,sizeof(TypeStorage));
471 st->relname_list = stringToQualifiedNameList(relname, "gist_tree");
472 st->relvar = makeRangeVarFromNameList(st->relname_list);
473 st->index = gist_index_open(st->relvar);
474 funcctx->user_fctx = (void*)st;
476 #if PG_VERSION_NUM >= 120000
477 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
479 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
481 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
482 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
483 for (i = 0; i < st->index->rd_att->natts; i++) {
484 sprintf(attname, "z%d", i+2);
489 TS_GET_TYPEVAL(st, i, atttypid),
490 TS_GET_TYPEVAL(st, i, atttypmod),
491 TS_GET_TYPEVAL(st, i, attndims)
495 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
496 st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
498 #if PG_VERSION_NUM >= 120000
499 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
501 funcctx->slot = TupleDescGetSlot(tupdesc);
504 MemoryContextSwitchTo(oldcontext);
507 st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
511 close_call( FuncCallContext *funcctx ) {
512 TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
514 while(st->item && closeGPPage(funcctx));
519 gist_index_close(st->index);
522 PG_FUNCTION_INFO_V1(gist_print);
523 Datum gist_print(PG_FUNCTION_ARGS);
525 gist_print(PG_FUNCTION_ARGS) {
526 FuncCallContext *funcctx;
528 Datum result=(Datum)0;
535 if (SRF_IS_FIRSTCALL()) {
536 text *name=PG_GETARG_TEXT_P(0);
537 funcctx = SRF_FIRSTCALL_INIT();
538 setup_firstcall(funcctx, name);
539 PG_FREE_IF_COPY(name,0);
542 funcctx = SRF_PERCALL_SETUP();
543 st = (TypeStorage*)(funcctx->user_fctx);
547 SRF_RETURN_DONE(funcctx);
550 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) {
551 if ( ! closeGPPage(funcctx) ) {
553 SRF_RETURN_DONE(funcctx);
557 iid = PageGetItemId( st->item->page, st->item->offset );
558 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
560 st->dvalues[0] = Int32GetDatum( st->item->level );
561 st->nulls[0] = ISNOTNULL;
562 st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
563 st->nulls[1] = ISNOTNULL;
564 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++)
566 if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) )
568 st->dvalues[i] = (Datum)0;
569 st->nulls[i] = ISNULL;
571 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
572 st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
576 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
577 #if PG_VERSION_NUM >= 120000
578 result = HeapTupleGetDatum(htuple);
580 result = TupleGetDatum(funcctx->slot, htuple);
582 st->item->offset = OffsetNumberNext(st->item->offset);
583 if ( !GistPageIsLeaf(st->item->page) )
584 openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
586 SRF_RETURN_NEXT(funcctx, result);
589 typedef struct GinStatState {
597 #if PG_VERSION_NUM >= 90100
598 GinNullCategory category;
601 #if PG_VERSION_NUM >= 110000
609 moveRightIfItNeeded( GinStatState *st )
611 Page page = BufferGetPage(st->buffer);
613 if ( st->offset > PageGetMaxOffsetNumber(page) ) {
615 * We scaned the whole page, so we should take right page
617 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
619 if ( GinPageRightMost(page) )
620 return false; /* no more page */
622 LockBuffer(st->buffer, GIN_UNLOCK);
623 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
624 LockBuffer(st->buffer, GIN_SHARE);
625 st->offset = FirstOffsetNumber;
632 * Refinds a previois position, at returns it has correctly
633 * set offset and buffer is locked
636 refindPosition(GinStatState *st)
640 /* find left if needed (it causes only for first search) */
645 LockBuffer(st->buffer, GIN_SHARE);
647 page = BufferGetPage(st->buffer);
648 if (GinPageIsLeaf(page))
651 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
652 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
654 LockBuffer(st->buffer,GIN_UNLOCK);
655 st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno);
658 if (st->offset == InvalidOffsetNumber) {
659 return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */
664 #if PG_VERSION_NUM >= 90100
665 GinNullCategory category;
666 #elif PG_VERSION_NUM < 80400
672 if (moveRightIfItNeeded(st)==false)
675 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
676 #if PG_VERSION_NUM >= 90100
677 datum = gintuple_get_key(&st->ginstate, itup, &category);
678 cmp = ginCompareAttEntries(&st->ginstate,
679 st->attnum + 1, st->curval, st->category,
680 gintuple_get_attrnum(&st->ginstate, itup), datum, category);
681 #elif PG_VERSION_NUM >= 80400
682 datum = gin_index_getattr(&st->ginstate, itup);
684 cmp = compareAttEntries(&st->ginstate,
685 st->attnum + 1, st->curval,
686 gintuple_get_attrnum(&st->ginstate, itup), datum);
688 datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
692 &st->ginstate.compareFn,
699 if ( st->curval && !TS_GET_TYPEVAL(st, st->attnum, attbyval) )
700 pfree( (void*) st->curval );
711 gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
712 MemoryContext oldcontext;
714 char *relname=t2c(name);
717 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
719 st=(GinStatState*)palloc( sizeof(GinStatState) );
720 memset(st,0,sizeof(GinStatState));
721 st->index = gin_index_open(
722 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_stat")));
723 initGinState( &st->ginstate, st->index );
725 #if PG_VERSION_NUM >= 80400
726 if (attnum < 0 || attnum >= st->index->rd_att->natts)
727 elog(ERROR,"Wrong column's number");
733 funcctx->user_fctx = (void*)st;
735 #if PG_VERSION_NUM >= 120000
736 tupdesc = CreateTemplateTupleDesc(2);
738 tupdesc = CreateTemplateTupleDesc(2, false);
740 TupleDescInitEntry(tupdesc, 1, "value",
741 TS_GET_TYPEVAL(st, st->attnum, atttypid),
742 TS_GET_TYPEVAL(st, st->attnum, atttypmod),
743 TS_GET_TYPEVAL(st, st->attnum, attndims));
744 TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0);
746 memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
748 #if PG_VERSION_NUM >= 120000
749 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
751 funcctx->slot = TupleDescGetSlot(tupdesc);
754 MemoryContextSwitchTo(oldcontext);
757 st->offset = InvalidOffsetNumber;
758 st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO );
762 processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
763 MemoryContext oldcontext;
764 #if PG_VERSION_NUM >= 90100
766 #elif PG_VERSION_NUM < 80400
770 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
772 #if PG_VERSION_NUM >= 90100
773 key = gintuple_get_key(&st->ginstate, itup, &st->category);
775 if (st->category != GIN_CAT_NORM_KEY)
776 st->curval = (Datum)0;
779 st->curval = datumCopy(
780 #if PG_VERSION_NUM >= 90100
782 #elif PG_VERSION_NUM >= 80400
783 gin_index_getattr(&st->ginstate, itup),
785 index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
787 TS_GET_TYPEVAL(st, st->attnum, attbyval),
788 TS_GET_TYPEVAL(st, st->attnum, attlen));
789 MemoryContextSwitchTo(oldcontext);
791 st->dvalues[0] = st->curval;
792 #if PG_VERSION_NUM >= 90100
793 /* do no distiguish various null category */
794 st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
797 if ( GinIsPostingTree(itup) ) {
798 BlockNumber rootblkno = GinGetPostingTree(itup);
799 #if PG_VERSION_NUM >= 90400
801 GinBtreeStack *stack;
802 ItemPointerData minItem;
806 GinPostingTreeScan *gdi;
810 uint32 predictNumber;
812 LockBuffer(st->buffer, GIN_UNLOCK);
813 #if PG_VERSION_NUM >= 90400
814 stack = ginScanBeginPostingTree(&btree, st->index, rootblkno
815 #if PG_VERSION_NUM >= 90600
819 page = BufferGetPage(stack->buffer);
820 ItemPointerSetMin(&minItem);
821 list = GinDataLeafPageGetItems(page, &nlist, minItem);
823 predictNumber = stack->predictNumber;
824 st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
825 #elif PG_VERSION_NUM >= 90100
826 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
827 entrybuffer = ginScanBeginPostingTree(gdi);
828 page = BufferGetPage(entrybuffer);
829 predictNumber = gdi->stack->predictNumber;
830 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
832 gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
833 entrybuffer = scanBeginPostingTree(gdi);
834 page = BufferGetPage(entrybuffer);
835 predictNumber = gdi->stack->predictNumber;
836 st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
839 #if PG_VERSION_NUM < 90400
840 LockBuffer(entrybuffer, GIN_UNLOCK);
841 freeGinBtreeStack(gdi->stack);
844 LockBuffer(stack->buffer, GIN_UNLOCK);
845 freeGinBtreeStack(stack);
848 st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
849 LockBuffer(st->buffer, GIN_UNLOCK);
853 PG_FUNCTION_INFO_V1(gin_stat);
854 Datum gin_stat(PG_FUNCTION_ARGS);
856 gin_stat(PG_FUNCTION_ARGS) {
857 FuncCallContext *funcctx;
859 Datum result=(Datum)0;
864 if (SRF_IS_FIRSTCALL()) {
865 text *name=PG_GETARG_TEXT_P(0);
866 funcctx = SRF_FIRSTCALL_INIT();
867 gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
868 PG_FREE_IF_COPY(name,0);
871 funcctx = SRF_PERCALL_SETUP();
872 st = (GinStatState*)(funcctx->user_fctx);
874 if ( refindPosition(st) == false ) {
875 UnlockReleaseBuffer( st->buffer );
876 gin_index_close(st->index);
878 SRF_RETURN_DONE(funcctx);
884 if (moveRightIfItNeeded(st)==false) {
885 UnlockReleaseBuffer( st->buffer );
886 gin_index_close(st->index);
888 SRF_RETURN_DONE(funcctx);
891 page = BufferGetPage(st->buffer);
892 ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
894 #if PG_VERSION_NUM >= 80400
895 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
900 processTuple( funcctx, st, ituple );
902 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
903 #if PG_VERSION_NUM >= 120000
904 result = HeapTupleGetDatum(htuple);
906 result = TupleGetDatum(funcctx->slot, htuple);
909 SRF_RETURN_NEXT(funcctx, result);
912 PG_FUNCTION_INFO_V1(gin_count_estimate);
913 Datum gin_count_estimate(PG_FUNCTION_ARGS);
914 #if PG_VERSION_NUM >= 80300
916 gin_count_estimate(PG_FUNCTION_ARGS) {
917 text *name=PG_GETARG_TEXT_P(0);
921 char *relname=t2c(name);
923 #if PG_VERSION_NUM >= 80400
924 TIDBitmap *bitmap = tbm_create(work_mem * 1024L
925 #if PG_VERSION_NUM >= 100000
931 ItemPointerData tids[MAXTIDS];
936 index = gin_index_open(
937 makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate")));
939 if ( index->rd_opcintype[0] != TSVECTOROID ) {
940 gin_index_close(index);
941 elog(ERROR, "Column type is not a tsvector");
946 key.sk_strategy = TSearchStrategyNumber;
948 key.sk_argument = PG_GETARG_DATUM(1);
950 fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
952 #if PG_VERSION_NUM >= 90100
953 #if PG_VERSION_NUM >= 90400
954 scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
956 scan = index_beginscan_bitmap(index, SnapshotNow, 1);
958 index_rescan(scan, &key, 1, NULL, 0);
960 count = index_getbitmap(scan, bitmap);
962 #elif PG_VERSION_NUM >= 80400
963 scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
965 count = index_getbitmap(scan, bitmap);
968 scan = index_beginscan_multi(index, SnapshotNow, 1, &key);
971 more = index_getmulti(scan, tids, MAXTIDS, &returned_tids);
972 count += returned_tids;
976 index_endscan( scan );
977 gin_index_close(index);
979 PG_RETURN_INT64(count);
983 gin_count_estimate(PG_FUNCTION_ARGS) {
984 elog(NOTICE, "Function is not working under PgSQL < 8.3");
990 PG_FUNCTION_INFO_V1(spgist_stat);
991 Datum spgist_stat(PG_FUNCTION_ARGS);
993 spgist_stat(PG_FUNCTION_ARGS)
995 #if PG_VERSION_NUM < 90200
996 elog(NOTICE, "Function is not working under PgSQL < 9.2");
998 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1000 text *name = PG_GETARG_TEXT_P(0);
1004 BlockNumber totalPages = 0,
1009 double usedSpace = 0.0,
1010 usedLeafSpace = 0.0,
1011 usedInnerSpace = 0.0;
1013 int bufferSize = -1;
1014 int64 innerTuples = 0,
1017 nLeafPlaceholder = 0,
1018 nInnerPlaceholder = 0,
1022 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
1023 #define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID)
1025 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1026 index = relation_openrv(relvar, AccessExclusiveLock);
1028 if (!IS_INDEX(index) || !IS_SPGIST(index))
1029 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1030 RelationGetRelationName(index));
1032 totalPages = RelationGetNumberOfBlocks(index);
1034 for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
1040 buffer = ReadBuffer(index, blkno);
1041 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1043 page = BufferGetPage(buffer);
1045 if (PageIsNew(page) || SpGistPageIsDeleted(page))
1048 UnlockReleaseBuffer(buffer);
1052 if (SpGistPageIsLeaf(page))
1055 leafTuples += PageGetMaxOffsetNumber(page);
1056 nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
1057 nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection;
1065 max = PageGetMaxOffsetNumber(page);
1067 nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder;
1068 nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection;
1069 for (i = FirstOffsetNumber; i <= max; i++)
1071 SpGistInnerTuple it;
1073 it = (SpGistInnerTuple) PageGetItem(page,
1074 PageGetItemId(page, i));
1081 bufferSize = BufferGetPageSize(buffer)
1082 - MAXALIGN(sizeof(SpGistPageOpaqueData))
1083 - SizeOfPageHeaderData;
1085 pageFree = PageGetExactFreeSpace(page);
1087 usedSpace += bufferSize - pageFree;
1088 if (SpGistPageIsLeaf(page))
1089 usedLeafSpace += bufferSize - pageFree;
1091 usedInnerSpace += bufferSize - pageFree;
1093 if (pageFree == bufferSize)
1096 UnlockReleaseBuffer(buffer);
1099 index_close(index, AccessExclusiveLock);
1101 totalPages--; /* discount metapage */
1103 snprintf(res, sizeof(res),
1105 "deletedPages: %u\n"
1109 "usedSpace: %.2f kbytes\n"
1110 "usedInnerSpace: %.2f kbytes\n"
1111 "usedLeafSpace: %.2f kbytes\n"
1112 "freeSpace: %.2f kbytes\n"
1113 "fillRatio: %.2f%%\n"
1114 "leafTuples: " INT64_FORMAT "\n"
1115 "innerTuples: " INT64_FORMAT "\n"
1116 "innerAllTheSame: " INT64_FORMAT "\n"
1117 "leafPlaceholders: " INT64_FORMAT "\n"
1118 "innerPlaceholders: " INT64_FORMAT "\n"
1119 "leafRedirects: " INT64_FORMAT "\n"
1120 "innerRedirects: " INT64_FORMAT,
1121 totalPages, deletedPages, innerPages, leafPages, emptyPages,
1123 usedInnerSpace / 1024.0,
1124 usedLeafSpace / 1024.0,
1125 (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
1126 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
1127 leafTuples, innerTuples, nAllTheSame,
1128 nLeafPlaceholder, nInnerPlaceholder,
1129 nLeafRedirect, nInnerRedirect);
1131 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1135 #if PG_VERSION_NUM >= 90200
1137 typedef struct SPGistPrintStackElem {
1138 ItemPointerData iptr;
1141 } SPGistPrintStackElem;
1143 typedef struct SPGistPrint {
1146 Datum dvalues[8 /* see CreateTemplateTupleDesc call */];
1147 #if PG_VERSION_NUM >= 110000
1156 pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) {
1157 MemoryContext oldcontext;
1158 SPGistPrintStackElem *e;
1160 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1162 e = palloc(sizeof(*e));
1166 prst->stack = lcons(e, prst->stack);
1168 MemoryContextSwitchTo(oldcontext);
1172 close_spgist_print(SPGistPrint *prst) {
1173 index_close(prst->index, AccessExclusiveLock);
1177 PG_FUNCTION_INFO_V1(spgist_print);
1178 Datum spgist_print(PG_FUNCTION_ARGS);
1180 spgist_print(PG_FUNCTION_ARGS)
1182 #if PG_VERSION_NUM < 90200
1183 elog(NOTICE, "Function is not working under PgSQL < 9.2");
1185 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1187 FuncCallContext *funcctx;
1189 SPGistPrintStackElem *s;
1192 MemoryContext oldcontext;
1194 if (SRF_IS_FIRSTCALL()) {
1195 text *name=PG_GETARG_TEXT_P(0);
1198 ItemPointerData ipd;
1201 funcctx = SRF_FIRSTCALL_INIT();
1202 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1203 index = relation_openrv(relvar, AccessExclusiveLock);
1205 if (!IS_INDEX(index) || !IS_SPGIST(index))
1206 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1207 RelationGetRelationName(index));
1209 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1211 prst = palloc(sizeof(*prst));
1213 prst->index = index;
1214 initSpGistState(&prst->state, index);
1216 #if PG_VERSION_NUM >= 120000
1217 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1);
1219 tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
1221 TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
1222 TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
1223 TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
1224 TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
1225 TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
1226 TupleDescInitEntry(tupdesc, 6, "prefix",
1227 (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
1228 TupleDescInitEntry(tupdesc, 7, "label",
1229 (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
1230 TupleDescInitEntry(tupdesc, 8, "leaf",
1231 (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
1233 #if PG_VERSION_NUM >= 120000
1234 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1236 funcctx->slot = TupleDescGetSlot(tupdesc);
1239 funcctx->user_fctx = (void*)prst;
1241 MemoryContextSwitchTo(oldcontext);
1243 ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
1245 pushSPGistPrint(funcctx, prst, &ipd, 1);
1247 PG_FREE_IF_COPY(name,0);
1250 funcctx = SRF_PERCALL_SETUP();
1251 prst = (SPGistPrint*)(funcctx->user_fctx);
1255 if ( prst->stack == NIL ) {
1256 close_spgist_print(prst);
1257 SRF_RETURN_DONE(funcctx);
1260 CHECK_FOR_INTERRUPTS();
1262 s = (SPGistPrintStackElem*)linitial(prst->stack);
1263 prst->stack = list_delete_first(prst->stack);
1265 if (ItemPointerIsValid(&s->iptr))
1273 SpGistDeadTuple dtuple;
1276 buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr));
1277 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1279 page = BufferGetPage(buffer);
1280 if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) {
1281 UnlockReleaseBuffer(buffer);
1286 dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr)));
1288 if (dtuple->tupstate != SPGIST_LIVE) {
1289 UnlockReleaseBuffer(buffer);
1294 if (SpGistPageIsLeaf(page)) {
1295 SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple;
1297 tid = palloc(sizeof(ItemPointerData));
1299 prst->dvalues[0] = PointerGetDatum(tid);
1300 prst->nulls[0] = ISNOTNULL;
1301 prst->nulls[1] = ISNULL;
1302 prst->nulls[2] = ISNULL;
1303 prst->dvalues[3] = s->level;
1304 prst->nulls[3] = ISNOTNULL;
1305 prst->nulls[4] = ISNULL;
1306 prst->nulls[5] = ISNULL;
1307 prst->nulls[6] = ISNULL;
1308 prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
1309 prst->state.attType.attbyval, prst->state.attType.attlen);
1310 prst->nulls[7] = ISNOTNULL;
1312 SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple;
1314 SpGistNodeTuple node;
1316 SGITITERATE(innerTuple, i, node) {
1317 if (ItemPointerIsValid(&node->t_tid)) {
1323 if (i >= innerTuple->nNodes) {
1324 UnlockReleaseBuffer(buffer);
1329 tid = palloc(sizeof(ItemPointerData));
1331 prst->dvalues[0] = PointerGetDatum(tid);
1332 prst->nulls[0] = ISNOTNULL;
1333 prst->dvalues[1] = innerTuple->allTheSame;
1334 prst->nulls[1] = ISNOTNULL;
1335 prst->dvalues[2] = Int32GetDatum(s->nlabel);
1336 prst->nulls[2] = ISNOTNULL;
1337 prst->dvalues[3] = s->level;
1338 prst->nulls[3] = ISNOTNULL;
1339 tid = palloc(sizeof(ItemPointerData));
1341 prst->dvalues[4] = PointerGetDatum(tid);
1342 prst->nulls[5] = ISNOTNULL;
1343 if (innerTuple->prefixSize > 0) {
1344 prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
1345 prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
1346 prst->nulls[5] = ISNOTNULL;
1348 prst->nulls[5] = ISNULL;
1349 if (!IndexTupleHasNulls(node)) {
1350 prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
1351 prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
1352 prst->nulls[6] = ISNOTNULL;
1354 prst->nulls[6] = ISNULL;
1355 prst->nulls[7] = ISNULL;
1357 pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
1359 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1360 prst->stack = lcons(s, prst->stack);
1361 MemoryContextSwitchTo(oldcontext);
1365 UnlockReleaseBuffer(buffer);
1369 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
1370 #if PG_VERSION_NUM >= 120000
1371 result = HeapTupleGetDatum(htuple);
1373 result = TupleGetDatum(funcctx->slot, htuple);
1376 SRF_RETURN_NEXT(funcctx, result);
1380 PG_FUNCTION_INFO_V1(gin_statpage);
1381 Datum gin_statpage(PG_FUNCTION_ARGS);
1383 gin_statpage(PG_FUNCTION_ARGS)
1385 #if PG_VERSION_NUM < 90400
1386 elog(NOTICE, "Function is not working under PgSQL < 9.4");
1388 PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
1390 text *name = PG_GETARG_TEXT_P(0);
1396 #if PG_VERSION_NUM >= 100000
1404 entryInnerPages = 0,
1407 uint64 dataInnerFreeSpace = 0,
1408 dataLeafFreeSpace = 0,
1409 dataInnerTuplesCount = 0,
1410 dataLeafIptrsCount = 0,
1411 entryInnerFreeSpace = 0,
1412 entryLeafFreeSpace = 0,
1413 entryInnerTuplesCount = 0,
1414 entryLeafTuplesCount = 0,
1415 entryPostingSize = 0,
1416 entryPostingCount = 0,
1420 relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
1421 index = relation_openrv(relvar, AccessExclusiveLock);
1423 if (index->rd_rel->relkind != RELKIND_INDEX ||
1424 index->rd_rel->relam != GIN_AM_OID)
1425 elog(ERROR, "relation \"%s\" is not an SPGiST index",
1426 RelationGetRelationName(index));
1428 totalPages = RelationGetNumberOfBlocks(index);
1430 for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
1436 buffer = ReadBuffer(index, blkno);
1437 LockBuffer(buffer, BUFFER_LOCK_SHARE);
1439 page = BufferGetPage(buffer);
1440 header = (PageHeader)page;
1442 #if PG_VERSION_NUM >= 100000
1443 if (GinPageIsDeleted(page))
1449 if (GinPageIsData(page))
1452 if (GinPageIsLeaf(page))
1454 ItemPointerData minItem, *ptr;
1459 dataLeafFreeSpace += header->pd_upper - header->pd_lower;
1460 ItemPointerSetMin(&minItem);
1462 ptr = GinDataLeafPageGetItems(page, &nlist, minItem);
1467 dataLeafIptrsCount += nlist;
1475 dataInnerFreeSpace += header->pd_upper - header->pd_lower;
1476 dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
1482 OffsetNumber i, maxoff;
1484 maxoff = PageGetMaxOffsetNumber(page);
1487 if (GinPageIsLeaf(page))
1490 entryLeafFreeSpace += header->pd_upper - header->pd_lower;
1491 entryLeafTuplesCount += maxoff;
1496 entryInnerFreeSpace += header->pd_upper - header->pd_lower;
1497 entryInnerTuplesCount += maxoff;
1500 for (i = 1; i <= maxoff; i++)
1502 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
1504 if (GinPageIsLeaf(page))
1506 GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
1507 entryPostingCount += GinGetNPosting(itup);
1508 entryPostingSize += SizeOfGinPostingList(list);
1509 entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
1513 entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
1518 UnlockReleaseBuffer(buffer);
1521 index_close(index, AccessExclusiveLock);
1524 snprintf(res, sizeof(res),
1526 #if PG_VERSION_NUM >= 100000
1527 "deletedPages: %u\n"
1528 "emptyDataPages: %u\n"
1531 "dataInnerPages: %u\n"
1532 "dataLeafPages: %u\n"
1533 "dataInnerFreeSpace: " INT64_FORMAT "\n"
1534 "dataLeafFreeSpace: " INT64_FORMAT "\n"
1535 "dataInnerTuplesCount: " INT64_FORMAT "\n"
1536 "dataLeafIptrsCount: " INT64_FORMAT "\n"
1538 "entryInnerPages: %u\n"
1539 "entryLeafPages: %u\n"
1540 "entryInnerFreeSpace: " INT64_FORMAT "\n"
1541 "entryLeafFreeSpace: " INT64_FORMAT "\n"
1542 "entryInnerTuplesCount: " INT64_FORMAT "\n"
1543 "entryLeafTuplesCount: " INT64_FORMAT "\n"
1544 "entryPostingSize: " INT64_FORMAT "\n"
1545 "entryPostingCount: " INT64_FORMAT "\n"
1546 "entryAttrSize: " INT64_FORMAT "\n"
1549 #if PG_VERSION_NUM >= 100000
1558 dataInnerTuplesCount,
1563 entryInnerFreeSpace,
1565 entryInnerTuplesCount,
1566 entryLeafTuplesCount,
1572 PG_RETURN_TEXT_P(CStringGetTextDatum(res));
1576 #if PG_VERSION_NUM >= 120000
1577 typedef enum {stat, print} TreeCond;
1585 * Depth-first search for btree
1586 * using for statistic data collection
1587 * and printing index values by level
1590 btree_deep_search(Relation rel, int level,
1591 BlockNumber blk, BtreeIdxInfo *btreeIdxInfo, TreeCond cond)
1599 BTPageOpaque opaque;
1600 Buffer buffer = _bt_getbuf(rel, blk, BT_READ);
1602 page = (Page) BufferGetPage(buffer);
1603 opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1604 maxoff = PageGetMaxOffsetNumber(page);
1610 btreeIdxInfo->idxStat.numpages++;
1611 btreeIdxInfo->idxStat.tuplesize+=BTMaxItemSize(page)-PageGetFreeSpace(page);
1612 btreeIdxInfo->idxStat.totalsize+=BLCKSZ;
1613 btreeIdxInfo->idxStat.numtuple+=maxoff;
1615 if (level > btreeIdxInfo->idxStat.level)
1616 btreeIdxInfo->idxStat.level = level;
1618 if (P_ISLEAF(opaque))
1620 btreeIdxInfo->idxStat.numleafpages++;
1621 btreeIdxInfo->idxStat.leaftuplesize+=BTMaxItemSize(page)-
1622 PageGetFreeSpace(page);
1623 btreeIdxInfo->idxStat.numleaftuple+=maxoff;
1629 while ( (btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt))
1630 + level*4 + 128 >= btreeIdxInfo->idxInfo.len )
1632 int dist=btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt);
1633 btreeIdxInfo->idxInfo.len *= 2;
1634 btreeIdxInfo->idxInfo.txt=(text*)repalloc(btreeIdxInfo->idxInfo.txt,
1635 btreeIdxInfo->idxInfo.len);
1636 btreeIdxInfo->idxInfo.ptr = ((char*)btreeIdxInfo->idxInfo.txt)+dist;
1639 sprintf(btreeIdxInfo->idxInfo.ptr, "lvl: %d, blk: %d, numTuples: %d\n",
1644 btreeIdxInfo->idxInfo.ptr=strchr(btreeIdxInfo->idxInfo.ptr,'\0');
1649 if (!P_ISLEAF(opaque) && ((level < btreeIdxInfo->idxInfo.maxlevel)
1650 ||(btreeIdxInfo->idxInfo.maxlevel<0)))
1652 for (i = P_FIRSTDATAKEY(opaque); i <= maxoff; i = OffsetNumberNext(i))
1654 iid = PageGetItemId(page, i);
1656 if (!ItemIdIsValid(iid))
1657 btreeIdxInfo->idxStat.numinvalidtuple++;
1659 itup = (IndexTuple) PageGetItem(page, iid);
1660 cblk = BTreeInnerTupleGetDownLink(itup);
1662 btree_deep_search(rel, level + 1, cblk, btreeIdxInfo, cond);
1665 UnlockReleaseBuffer(buffer);
1669 * Print some statistic about btree index
1670 * This function shows information for live pages only
1671 * and do not shows information about deleting pages
1673 * SELECT btree_stat(INDEXNAME);
1675 PG_FUNCTION_INFO_V1(btree_stat);
1676 Datum btree_stat(PG_FUNCTION_ARGS);
1678 btree_stat(PG_FUNCTION_ARGS)
1680 text *name=PG_GETARG_TEXT_PP(0);
1684 BtreeIdxInfo btreeIdxInfo;
1688 BTMetaPageData *metad;
1689 BlockNumber rootBlk;
1691 text *out=(text*)palloc(1024);
1692 char *ptr=((char*)out)+VARHDRSZ;
1693 relname_list = textToQualifiedNameList(name);
1694 relvar = makeRangeVarFromNameList(relname_list);
1695 index = btree_index_open(relvar);
1697 memset(&btreeIdxInfo.idxStat, 0, sizeof(IdxStat));
1699 /* Start dts from root */
1700 metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
1701 metapg = BufferGetPage(metabuf);
1702 metad = BTPageGetMeta(metapg);
1703 rootBlk = metad->btm_root;
1704 UnlockReleaseBuffer(metabuf);
1706 btree_deep_search(index, 0, rootBlk, &btreeIdxInfo,stat);
1708 btree_index_close(index);
1711 "Number of levels: %d\n"
1712 "Number of pages: %d\n"
1713 "Number of leaf pages: %d\n"
1714 "Number of tuples: %d\n"
1715 "Number of invalid tuples: %d\n"
1716 "Number of leaf tuples: %d\n"
1717 "Total size of tuples: "INT64_FORMAT" bytes\n"
1718 "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
1719 "Total size of index: "INT64_FORMAT" bytes\n",
1720 btreeIdxInfo.idxStat.level+1,
1721 btreeIdxInfo.idxStat.numpages,
1722 btreeIdxInfo.idxStat.numleafpages,
1723 btreeIdxInfo.idxStat.numtuple,
1724 btreeIdxInfo.idxStat.numinvalidtuple,
1725 btreeIdxInfo.idxStat.numleaftuple,
1726 btreeIdxInfo.idxStat.tuplesize,
1727 btreeIdxInfo.idxStat.leaftuplesize,
1728 btreeIdxInfo.idxStat.totalsize);
1730 ptr=strchr(ptr,'\0');
1732 SET_VARSIZE(out, ptr-((char*)out));
1733 PG_RETURN_POINTER(out);
1736 typedef struct BtPItem
1740 OffsetNumber offset;
1742 struct BtPItem *next;
1756 * Open page in btree
1757 * Returns nitem as a pointer to stack for btree levels stored.
1758 * We process tuples from buffer in the top of nitem.
1759 * After the complete processing of a top buffer
1760 * we return to buffer on one level upper and go to next btree leaf.
1763 openBtPPage( FuncCallContext *funcctx, BlockNumber blk )
1766 MemoryContext oldcontext;
1768 Relation index = ( (BtTypeStorage*)(funcctx->user_fctx) )->index;
1769 BTPageOpaque opaque;
1770 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1771 nitem = (BtPItem*)palloc( sizeof(BtPItem) );
1772 memset(nitem,0,sizeof(BtPItem));
1774 nitem->buffer = _bt_getbuf(index, blk, BT_READ);
1775 Assert(BufferIsValid(nitem->buffer));
1776 nitem->page = (Page) BufferGetPage(nitem->buffer);
1777 opaque = (BTPageOpaque)PageGetSpecialPointer(nitem->page);
1778 nitem->offset=P_FIRSTDATAKEY(opaque);
1779 nitem->next = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1780 nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
1781 ( (BtTypeStorage*)(funcctx->user_fctx) )->item = nitem;
1783 MemoryContextSwitchTo(oldcontext);
1789 closeBtPPage( FuncCallContext *funcctx )
1791 BtPItem *oitem = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1793 ( (BtTypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
1795 UnlockReleaseBuffer(oitem->buffer);
1797 return ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
1801 btree_close_call( FuncCallContext *funcctx )
1803 BtTypeStorage *st = (BtTypeStorage*)(funcctx->user_fctx);
1805 while(st->item && closeBtPPage(funcctx));
1810 btree_index_close(st->index);
1814 * Settings for first call of btree_print
1815 * Sets the current memory context
1818 btree_setup_firstcall(FuncCallContext *funcctx, text *name)
1820 MemoryContext oldcontext;
1823 char attname[NAMEDATALEN];
1828 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1830 st=(BtTypeStorage*)palloc( sizeof(BtTypeStorage) );
1831 memset(st,0,sizeof(BtTypeStorage));
1832 st->relname_list = textToQualifiedNameList(name);
1833 st->relvar = makeRangeVarFromNameList(st->relname_list);
1834 st->index = btree_index_open(st->relvar);
1836 funcctx->user_fctx = (void*)st;
1838 tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
1839 TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
1840 TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
1841 for (i = 0; i < st->index->rd_att->natts; i++)
1843 sprintf(attname, "z%d", i+2);
1848 TS_GET_TYPEVAL(st, i, atttypid),
1849 TS_GET_TYPEVAL(st, i, atttypmod),
1850 TS_GET_TYPEVAL(st, i, attndims) );
1851 BlessTupleDesc(tupdesc);
1853 BlessTupleDesc(tupdesc);
1856 st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
1857 st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
1859 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1861 buffer = _bt_gettrueroot(st->index);
1862 blk = BufferGetBlockNumber(buffer);
1863 UnlockReleaseBuffer(buffer);
1865 MemoryContextSwitchTo(oldcontext);
1867 st->item=openBtPPage(funcctx, blk);
1871 * Show index elements for btree from root to MAXLEVEL
1872 * SELECT btree_tree(INDEXNAME[, MAXLEVEL]);
1874 PG_FUNCTION_INFO_V1(btree_tree);
1875 Datum btree_tree(PG_FUNCTION_ARGS);
1877 btree_tree(PG_FUNCTION_ARGS)
1879 text *name=PG_GETARG_TEXT_PP(0);
1883 BtreeIdxInfo btreeIdxInfo;
1886 BTMetaPageData *metad;
1887 BlockNumber rootBlk;
1890 * If we use MAXLEVEL is not used in SELECT btree_tree(INDEXNAME),
1891 * info.maxlevel set to -1
1892 * If MAXLEVEL is used in btree_tree call, set info.maxlevel = MAXLEVEL
1894 btreeIdxInfo.idxInfo.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
1895 btreeIdxInfo.idxInfo.len=1024;
1896 btreeIdxInfo.idxInfo.txt=(text*)palloc( btreeIdxInfo.idxInfo.len );
1897 btreeIdxInfo.idxInfo.ptr=((char*)btreeIdxInfo.idxInfo.txt)+VARHDRSZ;
1899 relname_list = textToQualifiedNameList(name);
1900 relvar = makeRangeVarFromNameList(relname_list);
1901 index = btree_index_open(relvar);
1903 /* Start dts from root block */
1904 metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
1905 metapg = BufferGetPage(metabuf);
1906 metad = BTPageGetMeta(metapg);
1907 rootBlk = metad->btm_root;
1908 UnlockReleaseBuffer(metabuf);
1910 btree_deep_search(index, 0, rootBlk, &btreeIdxInfo, print);
1912 btree_index_close(index);
1914 btreeIdxInfo.idxInfo.ptr=strchr(btreeIdxInfo.idxInfo.ptr,'\0');
1916 SET_VARSIZE(btreeIdxInfo.idxInfo.txt,
1917 btreeIdxInfo.idxInfo.ptr-((char*)btreeIdxInfo.idxInfo.txt));
1918 PG_RETURN_POINTER(btreeIdxInfo.idxInfo.txt);
1922 * Print objects stored in btree tuples
1923 * works only if objects in index have textual representation
1924 * select * from btree_print(INDEXNAME)
1925 * as t(level int, valid bool, a box) where level =1;
1927 PG_FUNCTION_INFO_V1(btree_print);
1928 Datum btree_print(PG_FUNCTION_ARGS);
1930 btree_print(PG_FUNCTION_ARGS)
1932 FuncCallContext *funcctx;
1939 BTPageOpaque opaque;
1940 Datum result=(Datum)0;
1942 if (SRF_IS_FIRSTCALL())
1944 text *name=PG_GETARG_TEXT_PP(0);
1945 funcctx = SRF_FIRSTCALL_INIT();
1946 btree_setup_firstcall(funcctx, name);
1947 PG_FREE_IF_COPY(name,0);
1950 funcctx = SRF_PERCALL_SETUP();
1951 st = (BtTypeStorage*)(funcctx->user_fctx);
1954 * Looking through the stack of btree pages.
1955 * If the item == NULL, then the search is over.
1956 * For each btree page we look through all the tuples.
1957 * If the offset is larger than the btree page size, the search is over.
1962 btree_close_call(funcctx);
1963 SRF_RETURN_DONE(funcctx);
1966 /* View all tuples on the page */
1967 while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) )
1969 if ( ! closeBtPPage(funcctx) ) {
1970 btree_close_call(funcctx);
1971 SRF_RETURN_DONE(funcctx);
1975 iid = PageGetItemId( st->item->page, st->item->offset );
1976 ituple = (IndexTuple) PageGetItem(st->item->page, iid);
1978 st->dvalues[0] = Int32GetDatum( st->item->level );
1979 st->nulls[0] = ISNOTNULL;
1980 opaque = (BTPageOpaque)PageGetSpecialPointer(st->item->page);
1981 st->dvalues[1] = BoolGetDatum(P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)) ? false : true);
1982 st->nulls[1] = ISNOTNULL;
1983 for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++)
1985 if (!P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)))
1987 st->dvalues[i] = (Datum)0;
1988 st->nulls[i] = ISNULL;
1992 st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
1993 st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
1997 htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
1998 result = TupleGetDatum(funcctx->slot, htuple);
1999 result = HeapTupleGetDatum(htuple);
2000 st->item->offset = OffsetNumberNext(st->item->offset);
2002 if (!P_ISLEAF(opaque))
2004 BlockNumber blk = BTreeInnerTupleGetDownLink(ituple);
2005 openBtPPage(funcctx, blk );
2008 SRF_RETURN_NEXT(funcctx, result);
2012 * Print some statistic about brin index
2013 * SELECT brin_stat(INDEXNAME);
2015 PG_FUNCTION_INFO_V1(brin_stat);
2016 Datum brin_stat(PG_FUNCTION_ARGS);
2018 brin_stat(PG_FUNCTION_ARGS)
2020 text *name=PG_GETARG_TEXT_PP(0);
2025 BlockNumber startBlk;
2026 BlockNumber heapNumBlocks;
2027 BlockNumber pagesPerRange;
2033 uint32 numRevmapPages=0;
2034 uint32 numTuples = 0;
2035 uint32 numEmptyPages = 0;
2036 uint32 numRegularPages = 0;
2037 uint64 freeSpace = 0;
2038 uint64 usedSpace = 0;
2040 text *out=(text*)palloc(1024);
2041 char *ptr=((char*)out)+VARHDRSZ;
2043 relname_list = textToQualifiedNameList(name);
2044 relvar = makeRangeVarFromNameList(relname_list);
2045 index = brin_index_open(relvar);
2047 revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
2048 heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
2050 /* Determine range of pages to process */
2051 heapNumBlocks = RelationGetNumberOfBlocks(heapRel); // total pages
2053 table_close(heapRel, AccessShareLock);
2055 /* The index is up to date, no update required */
2058 buf = InvalidBuffer;
2059 /* Scan the revmap */
2060 for (; startBlk < heapNumBlocks; startBlk += pagesPerRange)
2067 /* Get on-disk tuple */
2068 tup = brinGetTupleForHeapBlock(revmap, startBlk, &buf, &off, NULL,
2069 BUFFER_LOCK_SHARE, NULL);
2076 page = BufferGetPage(buf);
2078 if (BRIN_IS_REGULAR_PAGE(page))
2080 freeSpace += PageGetFreeSpace(page);
2081 bdesc = brin_build_desc(index);
2082 usedSpace += MAXALIGN(sizeof(BrinMemTuple) +
2083 sizeof(BrinValues) * bdesc->bd_tupdesc->natts);
2086 /* If the revmap page points to void */
2089 UnlockReleaseBuffer(buf);
2092 brinRevmapTerminate(revmap);
2095 "Number of revmap pages: %d\n"
2096 "Number of empty revmap pages: %d\n"
2097 "Number of regular pages: %d\n"
2098 "Number of tuples: %d\n"
2099 "Used space "INT64_FORMAT" bytes\n"
2100 "Free space "INT64_FORMAT" bytes\n",
2109 ptr=strchr(ptr,'\0');
2111 brin_index_close(index);
2112 SET_VARSIZE(out, ptr-((char*)out));
2113 PG_RETURN_POINTER(out);
2117 * Print numbers of heap blocks from revmap
2118 * and numbers of end blocks from ranges
2120 PG_FUNCTION_INFO_V1(brin_print);
2121 Datum brin_print(PG_FUNCTION_ARGS);
2123 brin_print(PG_FUNCTION_ARGS)
2125 text *name=PG_GETARG_TEXT_PP(0);
2130 BlockNumber heapBlk;
2131 BlockNumber numBlocks;
2132 BlockNumber pagesPerRange;
2137 text *out=(text*)palloc(len);
2138 char *ptr=((char*)out)+VARHDRSZ;
2140 relname_list = textToQualifiedNameList(name);
2141 relvar = makeRangeVarFromNameList(relname_list);
2142 index = brin_index_open(relvar);
2144 heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
2147 /* Determine range of pages to process */
2148 numBlocks = RelationGetNumberOfBlocks(heapRel);
2149 table_close(heapRel, AccessShareLock);
2150 revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
2152 for(heapBlk = 0; heapBlk < numBlocks; heapBlk += pagesPerRange)
2154 BlockNumber rangeEndBlk;
2155 BlockNumber rangeBlk;
2157 while ( (ptr-((char*)out)) + heapBlk*4 + 128 >= len )
2159 int dist=ptr-((char*)out);
2161 out=(text*)repalloc(out, len);
2162 ptr = ((char*)out)+dist;
2166 /* Compute rage end */
2167 if (heapBlk + pagesPerRange > numBlocks)
2168 rangeEndBlk = Min(RelationGetNumberOfBlocks(heapRel) - heapBlk,
2171 /* Easy case: range is known to be complete */
2172 rangeEndBlk = pagesPerRange;
2173 sprintf(ptr, "Start block: %d; end block: %d \n", heapBlk, rangeEndBlk);
2175 for (rangeBlk = 0; rangeBlk < rangeEndBlk; rangeBlk++)
2178 /* Read buffer for open table */
2179 Buffer buf = ReadBuffer(heapRel, rangeBlk);
2180 page = BufferGetPage(buf);
2181 sprintf(ptr, "Start block: %d; end block: %d; offset: %d, free: %d\n",
2184 (int) PageGetMaxOffsetNumber(page),
2185 (int) PageGetFreeSpace(page));
2190 ptr=strchr(ptr,'\0');
2193 brinRevmapTerminate(revmap);
2195 brin_index_close(index);
2196 ptr=strchr(ptr,'\0');
2197 SET_VARSIZE(out, ptr-((char*)out));
2198 PG_RETURN_POINTER(out);