X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=gevel.c;h=6018003976666d90dbc4701741c384605c71d1a4;hb=36dfc34b6f6aac26ff32621823546bc8dbaba2f1;hp=4c192128354c54fb35dd13d2a5228a0c1aaea1dd;hpb=a28232afd1ec8cf67df1de4d4d8c8f5783762004;p=gevel.git diff --git a/gevel.c b/gevel.c index 4c19212..6018003 100644 --- a/gevel.c +++ b/gevel.c @@ -1,6 +1,7 @@ #include "postgres.h" #include "access/genam.h" +#include "access/gin.h" #include "access/gist.h" #include "access/gist_private.h" #include "access/gistscan.h" @@ -10,6 +11,8 @@ #include "storage/lmgr.h" #include "catalog/namespace.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/datum.h" #include #include #include @@ -36,6 +39,7 @@ typedef struct { int len; } IdxInfo; +static Relation checkOpenedRelation(Relation r, Oid PgAmOid); #ifdef PG_MODULE_MAGIC /* >= 8.2 */ @@ -45,11 +49,21 @@ PG_MODULE_MAGIC; static Relation gist_index_open(RangeVar *relvar) { Oid relOid = RangeVarGetRelid(relvar, false); - return index_open(relOid, AccessExclusiveLock); + return checkOpenedRelation( + index_open(relOid, AccessExclusiveLock), GIST_AM_OID); } #define gist_index_close(r) index_close((r), AccessExclusiveLock) +static Relation +gin_index_open(RangeVar *relvar) { + Oid relOid = RangeVarGetRelid(relvar, false); + return checkOpenedRelation( + index_open(relOid, AccessShareLock), GIN_AM_OID); +} + +#define gin_index_close(r) index_close((r), AccessShareLock) + #else /* <8.2 */ static Relation @@ -57,7 +71,7 @@ gist_index_open(RangeVar *relvar) { Relation rel = index_openrv(relvar); LockRelation(rel, AccessExclusiveLock); - return rel; + return checkOpenedRelation(rel, GIST_AM_OID); } static void @@ -66,6 +80,20 @@ gist_index_close(Relation rel) { index_close(rel); } +static Relation +gin_index_open(RangeVar *relvar) { + Relation rel = index_openrv(relvar); + + LockRelation(rel, AccessShareLock); + return checkOpenedRelation(rel, GIN_AM_OID); +} + +static void +gin_index_close(Relation rel) { + UnlockRelation(rel, AccessShareLock); + index_close(rel); +} + #endif #if PG_VERSION_NUM >= 80300 @@ -76,6 +104,23 @@ gist_index_close(Relation rel) { #define SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l) #endif +static Relation +checkOpenedRelation(Relation r, Oid PgAmOid) { + if ( r->rd_am == NULL ) + elog(ERROR, "Relation %s.%s is not an index", + get_namespace_name(RelationGetNamespace(r)), + RelationGetRelationName(r) + ); + + if ( r->rd_rel->relam != PgAmOid ) + elog(ERROR, "Index %s.%s has wrong type", + get_namespace_name(RelationGetNamespace(r)), + RelationGetRelationName(r) + ); + + return r; +} + static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff, IdxInfo *info) { Buffer buffer; @@ -434,3 +479,216 @@ gist_print(PG_FUNCTION_ARGS) { SRF_RETURN_NEXT(funcctx, result); } +typedef struct GinStatState { + Relation index; + GinState ginstate; + + Buffer buffer; + OffsetNumber offset; + Datum curval; + Datum dvalues[2]; + char nulls[2]; +} GinStatState; + +static bool +moveRightIfItNeeded( GinStatState *st ) +{ + Page page = BufferGetPage(st->buffer); + + if ( st->offset > PageGetMaxOffsetNumber(page) ) { + /* + * We scaned the whole page, so we should take right page + */ + BlockNumber blkno = GinPageGetOpaque(page)->rightlink; + + if ( GinPageRightMost(page) ) + return false; /* no more page */ + + LockBuffer(st->buffer, GIN_UNLOCK); + st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno); + LockBuffer(st->buffer, GIN_SHARE); + st->offset = FirstOffsetNumber; + } + + return true; +} + +/* + * Refinds a previois position, at returns it has correctly + * set offset and buffer is locked + */ +static bool +refindPosition(GinStatState *st) +{ + Page page; + + /* find left if needed (it causes only for first search) */ + for (;;) { + IndexTuple itup; + BlockNumber blkno; + + LockBuffer(st->buffer, GIN_SHARE); + + page = BufferGetPage(st->buffer); + if (GinPageIsLeaf(page)) + break; + + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber)); + blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid); + + LockBuffer(st->buffer,GIN_UNLOCK); + st->buffer = ReleaseAndReadBuffer(st->buffer, st->index, blkno); + } + + if (st->offset == InvalidOffsetNumber) { + return (PageGetMaxOffsetNumber(page) >= FirstOffsetNumber ) ? true : false; /* first one */ + } + + for(;;) { + int cmp; + bool isnull; + Datum datum; + IndexTuple itup; + + if (moveRightIfItNeeded(st)==false) + return false; + + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); + datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull); + cmp = DatumGetInt32( + FunctionCall2( + &st->ginstate.compareFn, + st->curval, + datum + )); + if ( cmp == 0 ) + return true; + + st->offset++; + } + + return false; +} + +static void +gin_setup_firstcall(FuncCallContext *funcctx, text *name) { + MemoryContext oldcontext; + GinStatState *st; + char *relname=t2c(name); + TupleDesc tupdesc; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + st=(GinStatState*)palloc( sizeof(GinStatState) ); + memset(st,0,sizeof(GinStatState)); + st->index = gin_index_open( + makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gist_tree"))); + initGinState( &st->ginstate, st->index ); + + funcctx->user_fctx = (void*)st; + + tupdesc = CreateTemplateTupleDesc(2, false); + TupleDescInitEntry(tupdesc, 1, "value", + st->index->rd_att->attrs[0]->atttypid, + st->index->rd_att->attrs[0]->atttypmod, + st->index->rd_att->attrs[0]->attndims); + TupleDescInitEntry(tupdesc, 2, "nrow", INT4OID, -1, 0); + + memset( st->nulls, ' ', 2*sizeof(char) ); + + funcctx->slot = TupleDescGetSlot(tupdesc); + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + MemoryContextSwitchTo(oldcontext); + pfree(relname); + + st->offset = InvalidOffsetNumber; + st->buffer = ReadBuffer( st->index, GIN_ROOT_BLKNO ); +} + +static void +processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) { + MemoryContext oldcontext; + Datum datum; + bool isnull; + + datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + st->curval = datumCopy( + index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull), + st->ginstate.tupdesc->attrs[0]->attbyval, + st->ginstate.tupdesc->attrs[0]->attlen ); + MemoryContextSwitchTo(oldcontext); + + st->dvalues[0] = st->curval; + + if ( GinIsPostingTree(itup) ) { + BlockNumber rootblkno = GinGetPostingTree(itup); + GinPostingTreeScan *gdi; + Buffer entrybuffer; + Page page; + + LockBuffer(st->buffer, GIN_UNLOCK); + gdi = prepareScanPostingTree(st->index, rootblkno, TRUE); + entrybuffer = scanBeginPostingTree(gdi); + + page = BufferGetPage(entrybuffer); + st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff ); + + LockBuffer(entrybuffer, GIN_UNLOCK); + freeGinBtreeStack(gdi->stack); + pfree(gdi); + } else { + st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) ); + LockBuffer(st->buffer, GIN_UNLOCK); + } +} + +PG_FUNCTION_INFO_V1(gin_stat); +Datum gin_stat(PG_FUNCTION_ARGS); +Datum +gin_stat(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + GinStatState *st; + Datum result=(Datum)0; + IndexTuple ituple; + HeapTuple htuple; + Page page; + + if (SRF_IS_FIRSTCALL()) { + text *name=PG_GETARG_TEXT_P(0); + funcctx = SRF_FIRSTCALL_INIT(); + gin_setup_firstcall(funcctx, name); + PG_FREE_IF_COPY(name,0); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (GinStatState*)(funcctx->user_fctx); + + if ( refindPosition(st) == false ) { + UnlockReleaseBuffer( st->buffer ); + gin_index_close(st->index); + + SRF_RETURN_DONE(funcctx); + } + + st->offset++; + + if (moveRightIfItNeeded(st)==false) { + UnlockReleaseBuffer( st->buffer ); + gin_index_close(st->index); + + SRF_RETURN_DONE(funcctx); + } + + page = BufferGetPage(st->buffer); + ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); + + processTuple( funcctx, st, ituple ); + + htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls); + result = TupleGetDatum(funcctx->slot, htuple); + + SRF_RETURN_NEXT(funcctx, result); +} +