From: teodor Date: Fri, 20 Oct 2006 10:24:00 +0000 (+0000) Subject: Initial revision X-Git-Tag: start~1 X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?p=gevel.git;a=commitdiff_plain;h=228f386276308fe0ed63d6eb7885cfc3ff09eed0 Initial revision --- 228f386276308fe0ed63d6eb7885cfc3ff09eed0 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12f3c37 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +subdir = contrib/gevel +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global + +MODULES = gevel +DATA_built = gevel.sql +DOCS = README.gevel +#REGRESS = gevel + +include $(top_srcdir)/contrib/contrib-global.mk diff --git a/README.gevel b/README.gevel new file mode 100644 index 0000000..0d865d7 --- /dev/null +++ b/README.gevel @@ -0,0 +1,45 @@ +for 8.1+ postgresql + +select gist_tree(INDEXNAME); +select gist_tree(INDEXNAME,MAXLEVEL); +select gist_stat(INDEXNAME); + + +example from gmake installcheck on rtree_gist: +select * from gist_print('pix') as t(level int, valid bool, a box); + +# select * from gist_print('pix') as t(level int, valid bool, a box) where level < 2; + level | valid | a +-------+-------+----------------------------- + 1 | t | (37357,50073),(34242,357) + 1 | t | (43499,49770),(40358,43) + 1 | t | (31193,24679),(25047,12410) + 1 | t | (31018,12142),(25083,6) + 1 | t | (49944,25174),(43471,12802) + 1 | t | (12577,49757),(6302,37534) + 1 | t | (12528,37333),(6171,24861) + 1 | t | (50027,49751),(46817,25462) + 1 | t | (46870,49912),(43664,25722) + 1 | t | (24855,25574),(12447,19263) + 1 | t | (25054,19126),(12403,12796) + 1 | t | (32737,49923),(31178,1038) + 1 | t | (3184,24465),(15,81) + 1 | t | (24951,49983),(12740,44000) + 1 | t | (24919,43956),(12617,37901) + 1 | t | (40387,49852),(37338,25217) + 1 | t | (40325,24963),(37375,491) + 1 | t | (24919,12698),(12654,6518) + 1 | t | (25002,6338),(12350,51) + 1 | t | (49985,12554),(43447,222) + 1 | t | (25003,37769),(12552,25573) + 1 | t | (34270,49382),(32763,594) + 1 | t | (6205,50012),(3,37527) + 1 | t | (6163,37358),(120,25034) + 1 | t | (12343,24542),(9295,294) + 1 | t | (9308,24151),(6234,620) + 1 | t | (6230,24629),(3169,108) + 1 | t | (31179,50040),(28113,25556) + 1 | t | (28048,49694),(25000,25000) +(29 rows) + + diff --git a/gevel.c b/gevel.c new file mode 100644 index 0000000..675690c --- /dev/null +++ b/gevel.c @@ -0,0 +1,429 @@ +#include "postgres.h" + +#include "access/genam.h" +#include "access/gist.h" +#include "access/gist_private.h" +#include "access/gistscan.h" +#include "access/heapam.h" +#include "catalog/index.h" +#include "miscadmin.h" +#include "storage/lmgr.h" +#include "catalog/namespace.h" +#include "utils/builtins.h" +#include +#include +#include +#include + +#define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData))) + +#ifndef PG_NARGS +#define PG_NARGS() (fcinfo->nargs) +#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; +} + +typedef struct { + int maxlevel; + text *txt; + char *ptr; + int len; +} IdxInfo; + + +#ifdef PG_MODULE_MAGIC +/* >= 8.2 */ + +PG_MODULE_MAGIC; + +static Relation +gist_index_open(RangeVar *relvar) { + Oid relOid = RangeVarGetRelid(relvar, false); + return index_open(relOid, AccessExclusiveLock); +} + +#define gist_index_close(r) index_close((r), AccessExclusiveLock) + +#else /* <8.2 */ + +static Relation +gist_index_open(RangeVar *relvar) { + Relation rel = index_openrv(relvar); + + LockRelation(rel, AccessExclusiveLock); + return rel; +} + +static void +gist_index_close(Relation rel) { + UnlockRelation(rel, AccessExclusiveLock); + index_close(rel); +} + +#endif + +static void +gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) { + Buffer buffer; + Page page; + IndexTuple which; + ItemId iid; + OffsetNumber i, + maxoff; + BlockNumber cblk; + char *pred; + + pred = (char *) palloc(sizeof(char) * level * 4 + 1); + MemSet(pred, ' ', level*4); + pred[level*4] = '\0'; + + buffer = ReadBuffer(r, blk); + page = (Page) BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + + + while ( (info->ptr-((char*)info->txt)) + level*4 + 128 >= info->len ) { + int dist=info->ptr-((char*)info->txt); + info->len *= 2; + info->txt=(text*)repalloc(info->txt, info->len); + info->ptr = ((char*)info->txt)+dist; + } + + sprintf(info->ptr, "%s%d(l:%d) blk: %d numTuple: %d free: %db(%.2f%%) rightlink:%u (%s)\n", + pred, + coff, + level, + (int) blk, + (int) maxoff, + PageGetFreeSpace(page), + 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE), + GistPageGetOpaque(page)->rightlink, + ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" ); + info->ptr=strchr(info->ptr,'\0'); + + if (!GistPageIsLeaf(page) && ( info->maxlevel<0 || levelmaxlevel ) ) + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + iid = PageGetItemId(page, i); + which = (IndexTuple) PageGetItem(page, iid); + cblk = ItemPointerGetBlockNumber(&(which->t_tid)); + gist_dumptree(r, level + 1, cblk, i, info); + } + ReleaseBuffer(buffer); + pfree(pred); +} + +PG_FUNCTION_INFO_V1(gist_tree); +Datum gist_tree(PG_FUNCTION_ARGS); +Datum +gist_tree(PG_FUNCTION_ARGS) { + text *name=PG_GETARG_TEXT_P(0); + char *relname=t2c(name); + RangeVar *relvar; + Relation index; + List *relname_list; + IdxInfo info; + + + relname_list = stringToQualifiedNameList(relname, "gist_tree"); + relvar = makeRangeVarFromNameList(relname_list); + index = gist_index_open(relvar); + PG_FREE_IF_COPY(name,0); + + 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; + + gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0, &info); + + gist_index_close(index); + pfree(relname); + + VARATT_SIZEP(info.txt)=info.ptr-((char*)info.txt); + PG_RETURN_POINTER(info.txt); +} + +typedef struct { + int level; + int numpages; + int numleafpages; + int numtuple; + int numinvalidtuple; + int numleaftuple; + uint64 tuplesize; + uint64 leaftuplesize; + uint64 totalsize; +} IdxStat; + +static void +gist_stattree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxStat *info) { + Buffer buffer; + Page page; + IndexTuple which; + ItemId iid; + OffsetNumber i, + maxoff; + BlockNumber cblk; + char *pred; + + pred = (char *) palloc(sizeof(char) * level * 4 + 1); + MemSet(pred, ' ', level*4); + pred[level*4] = '\0'; + + buffer = ReadBuffer(r, blk); + page = (Page) BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + + info->numpages++; + info->tuplesize+=PAGESIZE-PageGetFreeSpace(page); + info->totalsize+=BLCKSZ; + info->numtuple+=maxoff; + if ( info->level < level ) + info->level = level; + + if (GistPageIsLeaf(page)) { + info->numleafpages++; + info->leaftuplesize+=PAGESIZE-PageGetFreeSpace(page); + info->numleaftuple+=maxoff; + } else { + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + iid = PageGetItemId(page, i); + which = (IndexTuple) PageGetItem(page, iid); + if ( GistTupleIsInvalid(which) ) + info->numinvalidtuple++; + cblk = ItemPointerGetBlockNumber(&(which->t_tid)); + gist_stattree(r, level + 1, cblk, i, info); + } + } + + ReleaseBuffer(buffer); + pfree(pred); +} + +PG_FUNCTION_INFO_V1(gist_stat); +Datum gist_stat(PG_FUNCTION_ARGS); +Datum +gist_stat(PG_FUNCTION_ARGS) { + text *name=PG_GETARG_TEXT_P(0); + char *relname=t2c(name); + RangeVar *relvar; + Relation index; + List *relname_list; + IdxStat info; + text *out=(text*)palloc(1024); + char *ptr=((char*)out)+VARHDRSZ; + + + relname_list = stringToQualifiedNameList(relname, "gist_tree"); + relvar = makeRangeVarFromNameList(relname_list); + index = gist_index_open(relvar); + PG_FREE_IF_COPY(name,0); + + memset(&info, 0, sizeof(IdxStat)); + + gist_stattree(index, 0, GIST_ROOT_BLKNO, 0, &info); + + gist_index_close(index); + pfree(relname); + + sprintf(ptr, + "Number of levels: %d\n" + "Number of pages: %d\n" + "Number of leaf pages: %d\n" + "Number of tuples: %d\n" + "Number of invalid tuples: %d\n" + "Number of leaf tuples: %d\n" + "Total size of tuples: "INT64_FORMAT" bytes\n" + "Total size of leaf tuples: "INT64_FORMAT" bytes\n" + "Total size of index: "INT64_FORMAT" bytes\n", + info.level+1, + info.numpages, + info.numleafpages, + info.numtuple, + info.numinvalidtuple, + info.numleaftuple, + info.tuplesize, + info.leaftuplesize, + info.totalsize); + + ptr=strchr(ptr,'\0'); + + VARATT_SIZEP(out)=ptr-((char*)out); + PG_RETURN_POINTER(out); +} + +typedef struct GPItem { + Buffer buffer; + Page page; + OffsetNumber offset; + int level; + struct GPItem *next; +} GPItem; + +typedef struct { + List *relname_list; + RangeVar *relvar; + Relation index; + Datum *dvalues; + char *nulls; + GPItem *item; +} TypeStorage; + +static GPItem* +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)); + + nitem->buffer = ReadBuffer(index, 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; + ( (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; +} + +static void +setup_firstcall(FuncCallContext *funcctx, text *name) { + MemoryContext oldcontext; + TypeStorage *st; + char *relname=t2c(name); + TupleDesc tupdesc; + char attname[NAMEDATALEN]; + int i; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + st=(TypeStorage*)palloc( sizeof(TypeStorage) ); + memset(st,0,sizeof(TypeStorage)); + st->relname_list = stringToQualifiedNameList(relname, "gist_tree"); + st->relvar = makeRangeVarFromNameList(st->relname_list); + st->index = gist_index_open(st->relvar); + funcctx->user_fctx = (void*)st; + + tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false); + TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0); + for (i = 0; i < st->index->rd_att->natts; i++) { + sprintf(attname, "z%d", i+2); + TupleDescInitEntry( + tupdesc, + i+3, + attname, + st->index->rd_att->attrs[i]->atttypid, + st->index->rd_att->attrs[i]->atttypmod, + st->index->rd_att->attrs[i]->attndims + ); + } + + st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum)); + st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char)); + + funcctx->slot = TupleDescGetSlot(tupdesc); + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + MemoryContextSwitchTo(oldcontext); + pfree(relname); + + st->item=openGPPage(funcctx, GIST_ROOT_BLKNO); +} + +static void +close_call( FuncCallContext *funcctx ) { + TypeStorage *st = (TypeStorage*)(funcctx->user_fctx); + + while( st->item && closeGPPage(funcctx) ); + + pfree( st->dvalues ); + pfree( st->nulls ); + + gist_index_close(st->index); +} + +PG_FUNCTION_INFO_V1(gist_print); +Datum gist_print(PG_FUNCTION_ARGS); +Datum +gist_print(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + TypeStorage *st; + Datum result=(Datum)0; + ItemId iid; + IndexTuple ituple; + HeapTuple htuple; + int i; + bool isnull; + + if (SRF_IS_FIRSTCALL()) { + text *name=PG_GETARG_TEXT_P(0); + funcctx = SRF_FIRSTCALL_INIT(); + setup_firstcall(funcctx, name); + PG_FREE_IF_COPY(name,0); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (TypeStorage*)(funcctx->user_fctx); + + if ( !st->item ) { + close_call(funcctx); + SRF_RETURN_DONE(funcctx); + } + + while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) ) { + 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->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true ); + st->nulls[1] = ' '; + for(i=2; iattinmeta->tupdesc->natts; i++) { + if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) { + st->dvalues[i] = (Datum)0; + st->nulls[i] = 'n'; + } else { + st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull); + st->nulls[i] = ( isnull ) ? 'n' : ' '; + } + } + + htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls); + result = TupleGetDatum(funcctx->slot, htuple); + st->item->offset = OffsetNumberNext(st->item->offset); + if ( !GistPageIsLeaf(st->item->page) ) + openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) ); + + SRF_RETURN_NEXT(funcctx, result); +} + diff --git a/gevel.sql.in b/gevel.sql.in new file mode 100644 index 0000000..a5b9eea --- /dev/null +++ b/gevel.sql.in @@ -0,0 +1,28 @@ +SET search_path = public; +BEGIN; + +create or replace function gist_tree(text) + returns text + as 'MODULE_PATHNAME' + language 'C' + with (isstrict); + +create or replace function gist_tree(text,int4) + returns text + as 'MODULE_PATHNAME' + language 'C' + with (isstrict); + +create or replace function gist_stat(text) + returns text + as 'MODULE_PATHNAME' + language 'C' + with (isstrict); + +create or replace function gist_print(text) + returns setof record + as 'MODULE_PATHNAME' + language 'C' + with (isstrict); + +END;