X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;f=gevel.c;h=f20ec38d4129de304c82c747b83ca30b881d51c9;hb=1c698a5b55e360c5e5863c9e0d5b3fe9fd6e23ec;hp=fe2f89684a7ea6cdbf91df0313915f2df00762d3;hpb=7f5ef1863ab78cdebd7cd23e5f8d38d18db5c1f4;p=gevel.git diff --git a/gevel.c b/gevel.c index fe2f896..f20ec38 100644 --- a/gevel.c +++ b/gevel.c @@ -2,6 +2,9 @@ #include "access/genam.h" #include "access/gin.h" +#if PG_VERSION_NUM >= 90400 +#include "utils/snapmgr.h" +#endif #if PG_VERSION_NUM >= 90100 #include "access/gin_private.h" #endif @@ -15,12 +18,19 @@ #endif #include "access/heapam.h" #include "catalog/index.h" +#if PG_VERSION_NUM >= 90600 +#include +#endif #include "miscadmin.h" #include "storage/lmgr.h" #include "catalog/namespace.h" #if PG_VERSION_NUM >= 80300 #include #endif +#if PG_VERSION_NUM >= 100000 +#include +#include +#endif #include #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -33,37 +43,47 @@ #include -#define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData))) +#define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData))) #ifndef PG_NARGS #define PG_NARGS() (fcinfo->nargs) #endif +#if PG_VERSION_NUM >= 90600 +#define ISNULL true +#define ISNOTNULL false +#define heap_formtuple heap_form_tuple +#else +#define ISNULL 'n' +#define ISNOTNULL ' ' +#endif + static char *t2c(text* in) { - char *out=palloc( VARSIZE(in) ); - memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ); - out[ VARSIZE(in)-VARHDRSZ ] ='\0'; - return out; + char *out=palloc(VARSIZE(in)); + + memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ); + out[ VARSIZE(in)-VARHDRSZ ] ='\0'; + return out; } typedef struct { int maxlevel; text *txt; char *ptr; - int len; + int len; } IdxInfo; static Relation checkOpenedRelation(Relation r, Oid PgAmOid); #ifdef PG_MODULE_MAGIC -/* >= 8.2 */ +/* >= 8.2 */ PG_MODULE_MAGIC; static Relation gist_index_open(RangeVar *relvar) { -#if PG_VERSION_NUM < 90200 +#if PG_VERSION_NUM < 90200 Oid relOid = RangeVarGetRelid(relvar, false); #else Oid relOid = RangeVarGetRelid(relvar, NoLock, false); @@ -76,7 +96,7 @@ gist_index_open(RangeVar *relvar) { static Relation gin_index_open(RangeVar *relvar) { -#if PG_VERSION_NUM < 90200 +#if PG_VERSION_NUM < 90200 Oid relOid = RangeVarGetRelid(relvar, false); #else Oid relOid = RangeVarGetRelid(relvar, NoLock, false); @@ -127,9 +147,9 @@ gin_index_close(Relation rel) { #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l) #endif -static Relation +static Relation checkOpenedRelation(Relation r, Oid PgAmOid) { - if ( r->rd_am == NULL ) + if ( r->rd_index == NULL ) elog(ERROR, "Relation %s.%s is not an index", get_namespace_name(RelationGetNamespace(r)), RelationGetRelationName(r) @@ -140,7 +160,7 @@ checkOpenedRelation(Relation r, Oid PgAmOid) { get_namespace_name(RelationGetNamespace(r)), RelationGetRelationName(r) ); - + return r; } @@ -172,13 +192,13 @@ gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo info->ptr = ((char*)info->txt)+dist; } - sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", + sprintf(info->ptr, "%s%d(l:%d) blk: %u numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", pred, - coff, - level, + coff, + level, blk, - (int) maxoff, - (int) PageGetFreeSpace(page), + (int) maxoff, + (int) PageGetFreeSpace(page), 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE), GistPageGetOpaque(page)->rightlink, ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" ); @@ -211,7 +231,7 @@ gist_tree(PG_FUNCTION_ARGS) { index = gist_index_open(relvar); PG_FREE_IF_COPY(name,0); - info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1; + info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1; info.len=1024; info.txt=(text*)palloc( info.len ); info.ptr=((char*)info.txt)+VARHDRSZ; @@ -226,12 +246,12 @@ gist_tree(PG_FUNCTION_ARGS) { } typedef struct { - int level; - int numpages; - int numleafpages; - int numtuple; - int numinvalidtuple; - int numleaftuple; + int level; + int numpages; + int numleafpages; + int numtuple; + int numinvalidtuple; + int numleaftuple; uint64 tuplesize; uint64 leaftuplesize; uint64 totalsize; @@ -309,7 +329,7 @@ gist_stat(PG_FUNCTION_ARGS) { gist_index_close(index); pfree(relname); - sprintf(ptr, + sprintf(ptr, "Number of levels: %d\n" "Number of pages: %d\n" "Number of leaf pages: %d\n" @@ -330,7 +350,7 @@ gist_stat(PG_FUNCTION_ARGS) { info.totalsize); ptr=strchr(ptr,'\0'); - + SET_VARSIZE(out, ptr-((char*)out)); PG_RETURN_POINTER(out); } @@ -348,7 +368,11 @@ typedef struct { RangeVar *relvar; Relation index; Datum *dvalues; +#if PG_VERSION_NUM >= 90600 + bool *nulls; +#else char *nulls; +#endif GPItem *item; } TypeStorage; @@ -357,7 +381,7 @@ openGPPage( FuncCallContext *funcctx, BlockNumber blk ) { GPItem *nitem; MemoryContext oldcontext; Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index; - + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); nitem = (GPItem*)palloc( sizeof(GPItem) ); memset(nitem,0,sizeof(GPItem)); @@ -366,22 +390,22 @@ openGPPage( FuncCallContext *funcctx, BlockNumber blk ) { nitem->page = (Page) BufferGetPage(nitem->buffer); nitem->offset=FirstOffsetNumber; nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item; - nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1; + nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1; ( (TypeStorage*)(funcctx->user_fctx) )->item = nitem; MemoryContextSwitchTo(oldcontext); return nitem; -} +} static GPItem* closeGPPage( FuncCallContext *funcctx ) { GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item; ( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next; - + ReleaseBuffer(oitem->buffer); pfree( oitem ); - return ( (TypeStorage*)(funcctx->user_fctx) )->item; + return ( (TypeStorage*)(funcctx->user_fctx) )->item; } static void @@ -418,7 +442,7 @@ setup_firstcall(FuncCallContext *funcctx, text *name) { } st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum)); - st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char)); + st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(*st->nulls)); funcctx->slot = TupleDescGetSlot(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); @@ -427,16 +451,16 @@ setup_firstcall(FuncCallContext *funcctx, text *name) { pfree(relname); st->item=openGPPage(funcctx, GIST_ROOT_BLKNO); -} +} -static void +static void close_call( FuncCallContext *funcctx ) { TypeStorage *st = (TypeStorage*)(funcctx->user_fctx); - - while( st->item && closeGPPage(funcctx) ); - - pfree( st->dvalues ); - pfree( st->nulls ); + + while(st->item && closeGPPage(funcctx)); + + pfree(st->dvalues); + pfree(st->nulls); gist_index_close(st->index); } @@ -461,7 +485,7 @@ gist_print(PG_FUNCTION_ARGS) { PG_FREE_IF_COPY(name,0); } - funcctx = SRF_PERCALL_SETUP(); + funcctx = SRF_PERCALL_SETUP(); st = (TypeStorage*)(funcctx->user_fctx); if ( !st->item ) { @@ -473,23 +497,23 @@ gist_print(PG_FUNCTION_ARGS) { if ( ! closeGPPage(funcctx) ) { close_call(funcctx); SRF_RETURN_DONE(funcctx); - } + } } iid = PageGetItemId( st->item->page, st->item->offset ); ituple = (IndexTuple) PageGetItem(st->item->page, iid); st->dvalues[0] = Int32GetDatum( st->item->level ); - st->nulls[0] = ' '; + st->nulls[0] = ISNOTNULL; st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true ); - st->nulls[1] = ' '; + st->nulls[1] = ISNOTNULL; for(i=2; iattinmeta->tupdesc->natts; i++) { if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) { st->dvalues[i] = (Datum)0; - st->nulls[i] = 'n'; + st->nulls[i] = ISNULL; } else { - st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); - st->nulls[i] = ( isnull ) ? 'n' : ' '; + st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); + st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL; } } @@ -526,7 +550,7 @@ moveRightIfItNeeded( GinStatState *st ) /* * We scaned the whole page, so we should take right page */ - BlockNumber blkno = GinPageGetOpaque(page)->rightlink; + BlockNumber blkno = GinPageGetOpaque(page)->rightlink; if ( GinPageRightMost(page) ) return false; /* no more page */ @@ -541,13 +565,13 @@ moveRightIfItNeeded( GinStatState *st ) } /* - * Refinds a previois position, at returns it has correctly + * Refinds a previois position, at returns it has correctly * set offset and buffer is locked */ static bool refindPosition(GinStatState *st) { - Page page; + Page page; /* find left if needed (it causes only for first search) */ for (;;) { @@ -572,14 +596,14 @@ refindPosition(GinStatState *st) } for(;;) { - int cmp; + int cmp; #if PG_VERSION_NUM >= 90100 GinNullCategory category; #elif PG_VERSION_NUM < 80400 bool isnull = false; #endif Datum datum; - IndexTuple itup; + IndexTuple itup; if (moveRightIfItNeeded(st)==false) return false; @@ -608,7 +632,7 @@ refindPosition(GinStatState *st) #endif if ( cmp == 0 ) { - if ( !st->index->rd_att->attrs[st->attnum]->attbyval ) + if ( st->curval && !st->index->rd_att->attrs[st->attnum]->attbyval ) pfree( (void*) st->curval ); return true; } @@ -645,13 +669,13 @@ gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) { funcctx->user_fctx = (void*)st; tupdesc = CreateTemplateTupleDesc(2, false); - TupleDescInitEntry(tupdesc, 1, "value", - st->index->rd_att->attrs[st->attnum]->atttypid, + TupleDescInitEntry(tupdesc, 1, "value", + st->index->rd_att->attrs[st->attnum]->atttypid, st->index->rd_att->attrs[st->attnum]->atttypmod, st->index->rd_att->attrs[st->attnum]->attndims); TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0); - memset( st->nulls, ' ', 2*sizeof(char) ); + memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) ); funcctx->slot = TupleDescGetSlot(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); @@ -665,15 +689,25 @@ gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) { static void processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) { - MemoryContext oldcontext; -#if PG_VERSION_NUM < 80400 + MemoryContext oldcontext; +#if PG_VERSION_NUM >= 90100 + Datum key; +#elif PG_VERSION_NUM < 80400 bool isnull; #endif oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + +#if PG_VERSION_NUM >= 90100 + key = gintuple_get_key(&st->ginstate, itup, &st->category); + + if (st->category != GIN_CAT_NORM_KEY) + st->curval = (Datum)0; + else +#endif st->curval = datumCopy( #if PG_VERSION_NUM >= 90100 - gintuple_get_key(&st->ginstate, itup, &st->category), + key, #elif PG_VERSION_NUM >= 80400 gin_index_getattr(&st->ginstate, itup), #else @@ -686,30 +720,59 @@ processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) { st->dvalues[0] = st->curval; #if PG_VERSION_NUM >= 90100 /* do no distiguish various null category */ - st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ' ' : 'n'; + st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL; #endif - + if ( GinIsPostingTree(itup) ) { BlockNumber rootblkno = GinGetPostingTree(itup); +#if PG_VERSION_NUM >= 90400 + GinBtreeData btree; + GinBtreeStack *stack; + ItemPointerData minItem; + int nlist; + ItemPointer list; +#else GinPostingTreeScan *gdi; - Buffer entrybuffer; + Buffer entrybuffer; +#endif Page page; + uint32 predictNumber; LockBuffer(st->buffer, GIN_UNLOCK); -#if PG_VERSION_NUM >= 90100 +#if PG_VERSION_NUM >= 90400 + stack = ginScanBeginPostingTree(&btree, st->index, rootblkno +#if PG_VERSION_NUM >= 90600 + , NULL +#endif + ); + page = BufferGetPage(stack->buffer); + ItemPointerSetMin(&minItem); + list = GinDataLeafPageGetItems(page, &nlist, minItem); + pfree(list); + predictNumber = stack->predictNumber; + st->dvalues[1] = Int32GetDatum( predictNumber * nlist ); +#elif PG_VERSION_NUM >= 90100 gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE); entrybuffer = ginScanBeginPostingTree(gdi); + page = BufferGetPage(entrybuffer); + predictNumber = gdi->stack->predictNumber; + st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff ); #else gdi = prepareScanPostingTree(st->index, rootblkno, TRUE); entrybuffer = scanBeginPostingTree(gdi); -#endif - page = BufferGetPage(entrybuffer); - st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff ); + predictNumber = gdi->stack->predictNumber; + st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff ); +#endif +#if PG_VERSION_NUM < 90400 LockBuffer(entrybuffer, GIN_UNLOCK); freeGinBtreeStack(gdi->stack); pfree(gdi); +#else + LockBuffer(stack->buffer, GIN_UNLOCK); + freeGinBtreeStack(stack); +#endif } else { st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) ); LockBuffer(st->buffer, GIN_UNLOCK); @@ -734,7 +797,7 @@ gin_stat(PG_FUNCTION_ARGS) { PG_FREE_IF_COPY(name,0); } - funcctx = SRF_PERCALL_SETUP(); + funcctx = SRF_PERCALL_SETUP(); st = (GinStatState*)(funcctx->user_fctx); if ( refindPosition(st) == false ) { @@ -746,8 +809,8 @@ gin_stat(PG_FUNCTION_ARGS) { for(;;) { st->offset++; - - if (moveRightIfItNeeded(st)==false) { + + if (moveRightIfItNeeded(st)==false) { UnlockReleaseBuffer( st->buffer ); gin_index_close(st->index); @@ -755,7 +818,7 @@ gin_stat(PG_FUNCTION_ARGS) { } page = BufferGetPage(st->buffer); - ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); + ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); #if PG_VERSION_NUM >= 80400 if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple)) @@ -764,7 +827,7 @@ gin_stat(PG_FUNCTION_ARGS) { } processTuple( funcctx, st, ituple ); - + htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls); result = TupleGetDatum(funcctx->slot, htuple); @@ -776,14 +839,18 @@ Datum gin_count_estimate(PG_FUNCTION_ARGS); #if PG_VERSION_NUM >= 80300 Datum gin_count_estimate(PG_FUNCTION_ARGS) { - text *name=PG_GETARG_TEXT_P(0); - Relation index; + text *name=PG_GETARG_TEXT_P(0); + Relation index; IndexScanDesc scan; int64 count = 0; - char *relname=t2c(name); + char *relname=t2c(name); ScanKeyData key; #if PG_VERSION_NUM >= 80400 - TIDBitmap *bitmap = tbm_create(work_mem * 1024L); + TIDBitmap *bitmap = tbm_create(work_mem * 1024L +#if PG_VERSION_NUM >= 100000 + , NULL +#endif + ); #else #define MAXTIDS 1024 ItemPointerData tids[MAXTIDS]; @@ -799,8 +866,8 @@ gin_count_estimate(PG_FUNCTION_ARGS) { elog(ERROR, "Column type is not a tsvector"); } - key.sk_flags = 0; - key.sk_attno = 1; + key.sk_flags = 0; + key.sk_attno = 1; key.sk_strategy = TSearchStrategyNumber; key.sk_subtype = 0; key.sk_argument = PG_GETARG_DATUM(1); @@ -808,7 +875,11 @@ gin_count_estimate(PG_FUNCTION_ARGS) { fmgr_info( F_TS_MATCH_VQ , &key.sk_func ); #if PG_VERSION_NUM >= 90100 +#if PG_VERSION_NUM >= 90400 + scan = index_beginscan_bitmap(index, SnapshotSelf, 1); +#else scan = index_beginscan_bitmap(index, SnapshotNow, 1); +#endif index_rescan(scan, &key, 1, NULL, 0); count = index_getbitmap(scan, bitmap); @@ -860,7 +931,9 @@ spgist_stat(PG_FUNCTION_ARGS) leafPages = 0, emptyPages = 0, deletedPages = 0; - double usedSpace = 0.0; + double usedSpace = 0.0, + usedLeafSpace = 0.0, + usedInnerSpace = 0.0; char res[1024]; int bufferSize = -1; int64 innerTuples = 0, @@ -937,6 +1010,10 @@ spgist_stat(PG_FUNCTION_ARGS) pageFree = PageGetExactFreeSpace(page); usedSpace += bufferSize - pageFree; + if (SpGistPageIsLeaf(page)) + usedLeafSpace += bufferSize - pageFree; + else + usedInnerSpace += bufferSize - pageFree; if (pageFree == bufferSize) emptyPages++; @@ -955,6 +1032,8 @@ spgist_stat(PG_FUNCTION_ARGS) "leafPages: %u\n" "emptyPages: %u\n" "usedSpace: %.2f kbytes\n" + "usedInnerSpace: %.2f kbytes\n" + "usedLeafSpace: %.2f kbytes\n" "freeSpace: %.2f kbytes\n" "fillRatio: %.2f%%\n" "leafTuples: " INT64_FORMAT "\n" @@ -966,8 +1045,10 @@ spgist_stat(PG_FUNCTION_ARGS) "innerRedirects: " INT64_FORMAT, totalPages, deletedPages, innerPages, leafPages, emptyPages, usedSpace / 1024.0, - (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024, - 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))), + usedInnerSpace / 1024.0, + usedLeafSpace / 1024.0, + (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024, + 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))), leafTuples, innerTuples, nAllTheSame, nLeafPlaceholder, nInnerPlaceholder, nLeafRedirect, nInnerRedirect); @@ -979,16 +1060,16 @@ spgist_stat(PG_FUNCTION_ARGS) #if PG_VERSION_NUM >= 90200 typedef struct SPGistPrintStackElem { - ItemPointerData iptr; + ItemPointerData iptr; int16 nlabel; int level; } SPGistPrintStackElem; typedef struct SPGistPrint { SpGistState state; - Relation index; - Datum dvalues[7 /* see CreateTemplateTupleDesc call */]; - char nulls[7 /* see CreateTemplateTupleDesc call */]; + Relation index; + Datum dvalues[8 /* see CreateTemplateTupleDesc call */]; + char nulls[8 /* see CreateTemplateTupleDesc call */]; List *stack; } SPGistPrint; @@ -1003,7 +1084,7 @@ pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int e->iptr = *ip; e->nlabel = 0; e->level = level; - prst->stack = lcons(e, prst->stack); + prst->stack = lcons(e, prst->stack); MemoryContextSwitchTo(oldcontext); } @@ -1032,8 +1113,8 @@ spgist_print(PG_FUNCTION_ARGS) MemoryContext oldcontext; if (SRF_IS_FIRSTCALL()) { - text *name=PG_GETARG_TEXT_P(0); - RangeVar *relvar; + text *name=PG_GETARG_TEXT_P(0); + RangeVar *relvar; Relation index; ItemPointerData ipd; TupleDesc tupdesc; @@ -1053,16 +1134,17 @@ spgist_print(PG_FUNCTION_ARGS) prst->index = index; initSpGistState(&prst->state, index); - tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */, false); + tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false); TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0); - TupleDescInitEntry(tupdesc, 2, "node", INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, 3, "level", INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, 4, "tid_pointer", TIDOID, -1, 0); - TupleDescInitEntry(tupdesc, 5, "prefix", + TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0); + TupleDescInitEntry(tupdesc, 6, "prefix", (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0); - TupleDescInitEntry(tupdesc, 6, "label", + TupleDescInitEntry(tupdesc, 7, "label", (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0); - TupleDescInitEntry(tupdesc, 7, "leaf", + TupleDescInitEntry(tupdesc, 8, "leaf", (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0); funcctx->slot = TupleDescGetSlot(tupdesc); @@ -1079,7 +1161,7 @@ spgist_print(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(name,0); } - funcctx = SRF_PERCALL_SETUP(); + funcctx = SRF_PERCALL_SETUP(); prst = (SPGistPrint*)(funcctx->user_fctx); next: @@ -1129,19 +1211,20 @@ next: tid = palloc(sizeof(ItemPointerData)); *tid = s->iptr; prst->dvalues[0] = PointerGetDatum(tid); - prst->nulls[0] = ' '; - prst->nulls[1] = 'n'; - prst->dvalues[2] = s->level; - prst->nulls[2] = ' '; - prst->nulls[3] = 'n'; - prst->nulls[4] = 'n'; - prst->nulls[5] = 'n'; - prst->dvalues[6] = datumCopy(SGLTDATUM(leafTuple, &prst->state), - prst->state.attType.attbyval, prst->state.attType.attlen); - prst->nulls[6] = ' '; + prst->nulls[0] = ISNOTNULL; + prst->nulls[1] = ISNULL; + prst->nulls[2] = ISNULL; + prst->dvalues[3] = s->level; + prst->nulls[3] = ISNOTNULL; + prst->nulls[4] = ISNULL; + prst->nulls[5] = ISNULL; + prst->nulls[6] = ISNULL; + prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state), + prst->state.attType.attbyval, prst->state.attType.attlen); + prst->nulls[7] = ISNOTNULL; } else { SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple; - int i; + int i; SpGistNodeTuple node; SGITITERATE(innerTuple, i, node) { @@ -1160,28 +1243,30 @@ next: tid = palloc(sizeof(ItemPointerData)); *tid = s->iptr; prst->dvalues[0] = PointerGetDatum(tid); - prst->nulls[0] = ' '; - prst->dvalues[1] = Int32GetDatum(s->nlabel); - prst->nulls[1] = ' '; - prst->dvalues[2] = s->level; - prst->nulls[2] = ' '; + prst->nulls[0] = ISNOTNULL; + prst->dvalues[1] = innerTuple->allTheSame; + prst->nulls[1] = ISNOTNULL; + prst->dvalues[2] = Int32GetDatum(s->nlabel); + prst->nulls[2] = ISNOTNULL; + prst->dvalues[3] = s->level; + prst->nulls[3] = ISNOTNULL; tid = palloc(sizeof(ItemPointerData)); *tid = node->t_tid; - prst->dvalues[3] = PointerGetDatum(tid); - prst->nulls[3] = ' '; - if (prst->state.attPrefixType.attbyval != VOIDOID && innerTuple->prefixSize > 0) { - prst->dvalues[4] = datumCopy(SGITDATUM(innerTuple, &prst->state), - prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen); - prst->nulls[4] = ' '; + prst->dvalues[4] = PointerGetDatum(tid); + prst->nulls[5] = ISNOTNULL; + if (innerTuple->prefixSize > 0) { + prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state), + prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen); + prst->nulls[5] = ISNOTNULL; } else - prst->nulls[4] = 'n'; - if (prst->state.attLabelType.attbyval != VOIDOID && !IndexTupleHasNulls(node)) { - prst->dvalues[5] = datumCopy(SGNTDATUM(node, &prst->state), - prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen); - prst->nulls[5] = ' '; + prst->nulls[5] = ISNULL; + if (!IndexTupleHasNulls(node)) { + prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state), + prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen); + prst->nulls[6] = ISNOTNULL; } else - prst->nulls[5] = 'n'; - prst->nulls[6] = 'n'; + prst->nulls[6] = ISNULL; + prst->nulls[7] = ISNULL; pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1); s->nlabel = i + 1; @@ -1202,3 +1287,172 @@ next: #endif } + +PG_FUNCTION_INFO_V1(gin_statpage); +Datum gin_statpage(PG_FUNCTION_ARGS); +Datum +gin_statpage(PG_FUNCTION_ARGS) +{ +#if PG_VERSION_NUM < 90400 + elog(NOTICE, "Function is not working under PgSQL < 9.4"); + + PG_RETURN_TEXT_P(CStringGetTextDatum("???")); +#else + text *name = PG_GETARG_TEXT_P(0); + RangeVar *relvar; + Relation index; + BlockNumber blkno; + char res[1024]; + uint32 totalPages, + entryPages = 0, + dataPages = 0, + dataInnerPages = 0, + dataLeafPages = 0, + entryInnerPages = 0, + entryLeafPages = 0 + ; + uint64 dataInnerFreeSpace = 0, + dataLeafFreeSpace = 0, + dataInnerTuplesCount = 0, + dataLeafIptrsCount = 0, + entryInnerFreeSpace = 0, + entryLeafFreeSpace = 0, + entryInnerTuplesCount = 0, + entryLeafTuplesCount = 0, + entryPostingSize = 0, + entryPostingCount = 0, + entryAttrSize = 0 + ; + + relvar = makeRangeVarFromNameList(textToQualifiedNameList(name)); + index = relation_openrv(relvar, AccessExclusiveLock); + + if (index->rd_rel->relkind != RELKIND_INDEX || + index->rd_rel->relam != GIN_AM_OID) + elog(ERROR, "relation \"%s\" is not an SPGiST index", + RelationGetRelationName(index)); + + totalPages = RelationGetNumberOfBlocks(index); + + for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++) + { + Buffer buffer; + Page page; + PageHeader header; + + buffer = ReadBuffer(index, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = BufferGetPage(buffer); + header = (PageHeader)page; + + if (GinPageIsData(page)) + { + dataPages++; + if (GinPageIsLeaf(page)) + { + ItemPointerData minItem; + int nlist; + + dataLeafPages++; + dataLeafFreeSpace += header->pd_upper - header->pd_lower; + ItemPointerSetMin(&minItem); + pfree(GinDataLeafPageGetItems(page, &nlist, minItem)); + dataLeafIptrsCount += nlist; + } + else + { + dataInnerPages++; + dataInnerFreeSpace += header->pd_upper - header->pd_lower; + dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff; + } + } + else + { + IndexTuple itup; + OffsetNumber i, maxoff; + + maxoff = PageGetMaxOffsetNumber(page); + + entryPages++; + if (GinPageIsLeaf(page)) + { + entryLeafPages++; + entryLeafFreeSpace += header->pd_upper - header->pd_lower; + entryLeafTuplesCount += maxoff; + } + else + { + entryInnerPages++; + entryInnerFreeSpace += header->pd_upper - header->pd_lower; + entryInnerTuplesCount += maxoff; + } + + for (i = 1; i <= maxoff; i++) + { + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i)); + + if (GinPageIsLeaf(page)) + { + GinPostingList *list = (GinPostingList *)GinGetPosting(itup); + entryPostingCount += GinGetNPosting(itup); + entryPostingSize += SizeOfGinPostingList(list); + entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info); + } + else + { + entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info); + } + } + } + + UnlockReleaseBuffer(buffer); + } + + index_close(index, AccessExclusiveLock); + totalPages--; + + snprintf(res, sizeof(res), + "totalPages: %u\n" + "dataPages: %u\n" + "dataInnerPages: %u\n" + "dataLeafPages: %u\n" + "dataInnerFreeSpace: " INT64_FORMAT "\n" + "dataLeafFreeSpace: " INT64_FORMAT "\n" + "dataInnerTuplesCount: " INT64_FORMAT "\n" + "dataLeafIptrsCount: " INT64_FORMAT "\n" + "entryPages: %u\n" + "entryInnerPages: %u\n" + "entryLeafPages: %u\n" + "entryInnerFreeSpace: " INT64_FORMAT "\n" + "entryLeafFreeSpace: " INT64_FORMAT "\n" + "entryInnerTuplesCount: " INT64_FORMAT "\n" + "entryLeafTuplesCount: " INT64_FORMAT "\n" + "entryPostingSize: " INT64_FORMAT "\n" + "entryPostingCount: " INT64_FORMAT "\n" + "entryAttrSize: " INT64_FORMAT "\n" + , + totalPages, + dataPages, + dataInnerPages, + dataLeafPages, + dataInnerFreeSpace, + dataLeafFreeSpace, + dataInnerTuplesCount, + dataLeafIptrsCount, + entryPages, + entryInnerPages, + entryLeafPages, + entryInnerFreeSpace, + entryLeafFreeSpace, + entryInnerTuplesCount, + entryLeafTuplesCount, + entryPostingSize, + entryPostingCount, + entryAttrSize + ); + + PG_RETURN_TEXT_P(CStringGetTextDatum(res)); +#endif +} +