X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;f=gevel.c;h=fe2f89684a7ea6cdbf91df0313915f2df00762d3;hb=d104410335f72c8381c50cb59cf67a3b72db832e;hp=675690c9e895999545e5a242c1c3d832c8203594;hpb=228f386276308fe0ed63d6eb7885cfc3ff09eed0;p=gevel.git diff --git a/gevel.c b/gevel.c index 675690c..fe2f896 100644 --- a/gevel.c +++ b/gevel.c @@ -1,19 +1,37 @@ #include "postgres.h" #include "access/genam.h" +#include "access/gin.h" +#if PG_VERSION_NUM >= 90100 +#include "access/gin_private.h" +#endif #include "access/gist.h" #include "access/gist_private.h" #include "access/gistscan.h" +#if PG_VERSION_NUM >= 90200 +#include "access/spgist_private.h" +#include "access/spgist.h" +#include "utils/datum.h" +#endif #include "access/heapam.h" #include "catalog/index.h" #include "miscadmin.h" #include "storage/lmgr.h" #include "catalog/namespace.h" +#if PG_VERSION_NUM >= 80300 +#include +#endif +#include #include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/datum.h" +#include "utils/fmgroids.h" #include #include #include #include +#include + #define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData))) @@ -36,6 +54,7 @@ typedef struct { int len; } IdxInfo; +static Relation checkOpenedRelation(Relation r, Oid PgAmOid); #ifdef PG_MODULE_MAGIC /* >= 8.2 */ @@ -44,12 +63,30 @@ PG_MODULE_MAGIC; static Relation gist_index_open(RangeVar *relvar) { +#if PG_VERSION_NUM < 90200 Oid relOid = RangeVarGetRelid(relvar, false); - return index_open(relOid, AccessExclusiveLock); +#else + Oid relOid = RangeVarGetRelid(relvar, NoLock, false); +#endif + 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) { +#if PG_VERSION_NUM < 90200 + Oid relOid = RangeVarGetRelid(relvar, false); +#else + Oid relOid = RangeVarGetRelid(relvar, NoLock, false); +#endif + 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 +94,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,8 +103,47 @@ 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 +#define stringToQualifiedNameList(x,y) stringToQualifiedNameList(x) +#endif + +#if PG_VERSION_NUM < 80300 +#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; @@ -96,13 +172,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: %d 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, - (int) blk, + blk, (int) maxoff, - PageGetFreeSpace(page), + (int) PageGetFreeSpace(page), 100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE), GistPageGetOpaque(page)->rightlink, ( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" ); @@ -130,7 +206,6 @@ gist_tree(PG_FUNCTION_ARGS) { List *relname_list; IdxInfo info; - relname_list = stringToQualifiedNameList(relname, "gist_tree"); relvar = makeRangeVarFromNameList(relname_list); index = gist_index_open(relvar); @@ -146,7 +221,7 @@ gist_tree(PG_FUNCTION_ARGS) { gist_index_close(index); pfree(relname); - VARATT_SIZEP(info.txt)=info.ptr-((char*)info.txt); + SET_VARSIZE(info.txt, info.ptr-((char*)info.txt)); PG_RETURN_POINTER(info.txt); } @@ -256,7 +331,7 @@ gist_stat(PG_FUNCTION_ARGS) { ptr=strchr(ptr,'\0'); - VARATT_SIZEP(out)=ptr-((char*)out); + SET_VARSIZE(out, ptr-((char*)out)); PG_RETURN_POINTER(out); } @@ -427,3 +502,703 @@ gist_print(PG_FUNCTION_ARGS) { SRF_RETURN_NEXT(funcctx, result); } +typedef struct GinStatState { + Relation index; + GinState ginstate; + OffsetNumber attnum; + + Buffer buffer; + OffsetNumber offset; + Datum curval; +#if PG_VERSION_NUM >= 90100 + GinNullCategory category; +#endif + 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; +#if PG_VERSION_NUM >= 90100 + GinNullCategory category; +#elif PG_VERSION_NUM < 80400 + bool isnull = false; +#endif + Datum datum; + IndexTuple itup; + + if (moveRightIfItNeeded(st)==false) + return false; + + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset)); +#if PG_VERSION_NUM >= 90100 + datum = gintuple_get_key(&st->ginstate, itup, &category); + cmp = ginCompareAttEntries(&st->ginstate, + st->attnum + 1, st->curval, st->category, + gintuple_get_attrnum(&st->ginstate, itup), datum, category); +#elif PG_VERSION_NUM >= 80400 + datum = gin_index_getattr(&st->ginstate, itup); + + cmp = compareAttEntries(&st->ginstate, + st->attnum + 1, st->curval, + gintuple_get_attrnum(&st->ginstate, itup), datum); +#else + datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull); + + cmp = DatumGetInt32( + FunctionCall2( + &st->ginstate.compareFn, + st->curval, + datum + )); +#endif + if ( cmp == 0 ) + { + if ( !st->index->rd_att->attrs[st->attnum]->attbyval ) + pfree( (void*) st->curval ); + return true; + } + + st->offset++; + } + + return false; +} + +static void +gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) { + 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, "gin_stat"))); + initGinState( &st->ginstate, st->index ); + +#if PG_VERSION_NUM >= 80400 + if (attnum < 0 || attnum >= st->index->rd_att->natts) + elog(ERROR,"Wrong column's number"); + st->attnum = attnum; +#else + st->attnum = 0; +#endif + + funcctx->user_fctx = (void*)st; + + tupdesc = CreateTemplateTupleDesc(2, false); + 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) ); + + 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; +#if PG_VERSION_NUM < 80400 + bool isnull; +#endif + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + st->curval = datumCopy( +#if PG_VERSION_NUM >= 90100 + gintuple_get_key(&st->ginstate, itup, &st->category), +#elif PG_VERSION_NUM >= 80400 + gin_index_getattr(&st->ginstate, itup), +#else + index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull), +#endif + st->index->rd_att->attrs[st->attnum]->attbyval, + st->index->rd_att->attrs[st->attnum]->attlen ); + MemoryContextSwitchTo(oldcontext); + + 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'; +#endif + + if ( GinIsPostingTree(itup) ) { + BlockNumber rootblkno = GinGetPostingTree(itup); + GinPostingTreeScan *gdi; + Buffer entrybuffer; + Page page; + + LockBuffer(st->buffer, GIN_UNLOCK); +#if PG_VERSION_NUM >= 90100 + gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE); + entrybuffer = ginScanBeginPostingTree(gdi); +#else + gdi = prepareScanPostingTree(st->index, rootblkno, TRUE); + entrybuffer = scanBeginPostingTree(gdi); +#endif + + 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_NARGS()==2) ? PG_GETARG_INT32(1) : 0 ); + 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); + } + + for(;;) { + 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)); + +#if PG_VERSION_NUM >= 80400 + if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple)) +#endif + break; + } + + processTuple( funcctx, st, ituple ); + + htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls); + result = TupleGetDatum(funcctx->slot, htuple); + + SRF_RETURN_NEXT(funcctx, result); +} + +PG_FUNCTION_INFO_V1(gin_count_estimate); +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; + IndexScanDesc scan; + int64 count = 0; + char *relname=t2c(name); + ScanKeyData key; +#if PG_VERSION_NUM >= 80400 + TIDBitmap *bitmap = tbm_create(work_mem * 1024L); +#else +#define MAXTIDS 1024 + ItemPointerData tids[MAXTIDS]; + int32 returned_tids; + bool more; +#endif + + index = gin_index_open( + makeRangeVarFromNameList(stringToQualifiedNameList(relname, "gin_count_estimate"))); + + if ( index->rd_opcintype[0] != TSVECTOROID ) { + gin_index_close(index); + elog(ERROR, "Column type is not a tsvector"); + } + + key.sk_flags = 0; + key.sk_attno = 1; + key.sk_strategy = TSearchStrategyNumber; + key.sk_subtype = 0; + key.sk_argument = PG_GETARG_DATUM(1); + + fmgr_info( F_TS_MATCH_VQ , &key.sk_func ); + +#if PG_VERSION_NUM >= 90100 + scan = index_beginscan_bitmap(index, SnapshotNow, 1); + index_rescan(scan, &key, 1, NULL, 0); + + count = index_getbitmap(scan, bitmap); + tbm_free(bitmap); +#elif PG_VERSION_NUM >= 80400 + scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key); + + count = index_getbitmap(scan, bitmap); + tbm_free(bitmap); +#else + scan = index_beginscan_multi(index, SnapshotNow, 1, &key); + + do { + more = index_getmulti(scan, tids, MAXTIDS, &returned_tids); + count += returned_tids; + } while(more); +#endif + + index_endscan( scan ); + gin_index_close(index); + + PG_RETURN_INT64(count); +} +#else +Datum +gin_count_estimate(PG_FUNCTION_ARGS) { + elog(NOTICE, "Function is not working under PgSQL < 8.3"); + + PG_RETURN_INT64(0); +} +#endif + +PG_FUNCTION_INFO_V1(spgist_stat); +Datum spgist_stat(PG_FUNCTION_ARGS); +Datum +spgist_stat(PG_FUNCTION_ARGS) +{ +#if PG_VERSION_NUM < 90200 + elog(NOTICE, "Function is not working under PgSQL < 9.2"); + + PG_RETURN_TEXT_P(CStringGetTextDatum("???")); +#else + text *name = PG_GETARG_TEXT_P(0); + RangeVar *relvar; + Relation index; + BlockNumber blkno; + BlockNumber totalPages = 0, + innerPages = 0, + leafPages = 0, + emptyPages = 0, + deletedPages = 0; + double usedSpace = 0.0; + char res[1024]; + int bufferSize = -1; + int64 innerTuples = 0, + leafTuples = 0, + nAllTheSame = 0, + nLeafPlaceholder = 0, + nInnerPlaceholder = 0, + nLeafRedirect = 0, + nInnerRedirect = 0; + +#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) +#define IS_SPGIST(r) ((r)->rd_rel->relam == SPGIST_AM_OID) + + relvar = makeRangeVarFromNameList(textToQualifiedNameList(name)); + index = relation_openrv(relvar, AccessExclusiveLock); + + if (!IS_INDEX(index) || !IS_SPGIST(index)) + elog(ERROR, "relation \"%s\" is not an SPGiST index", + RelationGetRelationName(index)); + + totalPages = RelationGetNumberOfBlocks(index); + + for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++) + { + Buffer buffer; + Page page; + int pageFree; + + buffer = ReadBuffer(index, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = BufferGetPage(buffer); + + if (PageIsNew(page) || SpGistPageIsDeleted(page)) + { + deletedPages++; + UnlockReleaseBuffer(buffer); + continue; + } + + if (SpGistPageIsLeaf(page)) + { + leafPages++; + leafTuples += PageGetMaxOffsetNumber(page); + nLeafPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder; + nLeafRedirect += SpGistPageGetOpaque(page)->nRedirection; + } + else + { + int i, + max; + + innerPages++; + max = PageGetMaxOffsetNumber(page); + innerTuples += max; + nInnerPlaceholder += SpGistPageGetOpaque(page)->nPlaceholder; + nInnerRedirect += SpGistPageGetOpaque(page)->nRedirection; + for (i = FirstOffsetNumber; i <= max; i++) + { + SpGistInnerTuple it; + + it = (SpGistInnerTuple) PageGetItem(page, + PageGetItemId(page, i)); + if (it->allTheSame) + nAllTheSame++; + } + } + + if (bufferSize < 0) + bufferSize = BufferGetPageSize(buffer) + - MAXALIGN(sizeof(SpGistPageOpaqueData)) + - SizeOfPageHeaderData; + + pageFree = PageGetExactFreeSpace(page); + + usedSpace += bufferSize - pageFree; + + if (pageFree == bufferSize) + emptyPages++; + + UnlockReleaseBuffer(buffer); + } + + index_close(index, AccessExclusiveLock); + + totalPages--; /* discount metapage */ + + snprintf(res, sizeof(res), + "totalPages: %u\n" + "deletedPages: %u\n" + "innerPages: %u\n" + "leafPages: %u\n" + "emptyPages: %u\n" + "usedSpace: %.2f kbytes\n" + "freeSpace: %.2f kbytes\n" + "fillRatio: %.2f%%\n" + "leafTuples: " INT64_FORMAT "\n" + "innerTuples: " INT64_FORMAT "\n" + "innerAllTheSame: " INT64_FORMAT "\n" + "leafPlaceholders: " INT64_FORMAT "\n" + "innerPlaceholders: " INT64_FORMAT "\n" + "leafRedirects: " INT64_FORMAT "\n" + "innerRedirects: " INT64_FORMAT, + totalPages, deletedPages, innerPages, leafPages, emptyPages, + usedSpace / 1024.0, + (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024, + 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))), + leafTuples, innerTuples, nAllTheSame, + nLeafPlaceholder, nInnerPlaceholder, + nLeafRedirect, nInnerRedirect); + + PG_RETURN_TEXT_P(CStringGetTextDatum(res)); +#endif +} + +#if PG_VERSION_NUM >= 90200 + +typedef struct SPGistPrintStackElem { + 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 */]; + List *stack; +} SPGistPrint; + +static void +pushSPGistPrint(FuncCallContext *funcctx, SPGistPrint *prst, ItemPointer ip, int level) { + MemoryContext oldcontext; + SPGistPrintStackElem *e; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + e = palloc(sizeof(*e)); + e->iptr = *ip; + e->nlabel = 0; + e->level = level; + prst->stack = lcons(e, prst->stack); + + MemoryContextSwitchTo(oldcontext); +} + +static void +close_spgist_print(SPGistPrint *prst) { + index_close(prst->index, AccessExclusiveLock); +} +#endif + +PG_FUNCTION_INFO_V1(spgist_print); +Datum spgist_print(PG_FUNCTION_ARGS); +Datum +spgist_print(PG_FUNCTION_ARGS) +{ +#if PG_VERSION_NUM < 90200 + elog(NOTICE, "Function is not working under PgSQL < 9.2"); + + PG_RETURN_TEXT_P(CStringGetTextDatum("???")); +#else + FuncCallContext *funcctx; + SPGistPrint *prst; + SPGistPrintStackElem *s; + HeapTuple htuple; + Datum result; + MemoryContext oldcontext; + + if (SRF_IS_FIRSTCALL()) { + text *name=PG_GETARG_TEXT_P(0); + RangeVar *relvar; + Relation index; + ItemPointerData ipd; + TupleDesc tupdesc; + + funcctx = SRF_FIRSTCALL_INIT(); + relvar = makeRangeVarFromNameList(textToQualifiedNameList(name)); + index = relation_openrv(relvar, AccessExclusiveLock); + + if (!IS_INDEX(index) || !IS_SPGIST(index)) + elog(ERROR, "relation \"%s\" is not an SPGiST index", + RelationGetRelationName(index)); + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + prst = palloc(sizeof(*prst)); + + prst->index = index; + initSpGistState(&prst->state, index); + + tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */, 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", + (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0); + TupleDescInitEntry(tupdesc, 6, "label", + (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0); + TupleDescInitEntry(tupdesc, 7, "leaf", + (prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0); + + funcctx->slot = TupleDescGetSlot(tupdesc); + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + funcctx->user_fctx = (void*)prst; + + MemoryContextSwitchTo(oldcontext); + + ItemPointerSet(&ipd, SPGIST_ROOT_BLKNO, FirstOffsetNumber); + prst->stack = NIL; + pushSPGistPrint(funcctx, prst, &ipd, 1); + + PG_FREE_IF_COPY(name,0); + } + + funcctx = SRF_PERCALL_SETUP(); + prst = (SPGistPrint*)(funcctx->user_fctx); + +next: + for(;;) { + if ( prst->stack == NIL ) { + close_spgist_print(prst); + SRF_RETURN_DONE(funcctx); + } + + CHECK_FOR_INTERRUPTS(); + + s = (SPGistPrintStackElem*)linitial(prst->stack); + prst->stack = list_delete_first(prst->stack); + + if (ItemPointerIsValid(&s->iptr)) + break; + free(s); + } + + do { + Buffer buffer; + Page page; + SpGistDeadTuple dtuple; + ItemPointer tid; + + buffer = ReadBuffer(prst->index, ItemPointerGetBlockNumber(&s->iptr)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = BufferGetPage(buffer); + if (ItemPointerGetOffsetNumber(&s->iptr) > PageGetMaxOffsetNumber(page)) { + UnlockReleaseBuffer(buffer); + pfree(s); + goto next; + } + + dtuple = (SpGistDeadTuple)PageGetItem(page, PageGetItemId(page, ItemPointerGetOffsetNumber(&s->iptr))); + + if (dtuple->tupstate != SPGIST_LIVE) { + UnlockReleaseBuffer(buffer); + pfree(s); + goto next; + } + + if (SpGistPageIsLeaf(page)) { + SpGistLeafTuple leafTuple = (SpGistLeafTuple)dtuple; + + 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] = ' '; + } else { + SpGistInnerTuple innerTuple = (SpGistInnerTuple)dtuple; + int i; + SpGistNodeTuple node; + + SGITITERATE(innerTuple, i, node) { + if (ItemPointerIsValid(&node->t_tid)) { + if (i >= s->nlabel) + break; + } + } + + if (i >= innerTuple->nNodes) { + UnlockReleaseBuffer(buffer); + pfree(s); + goto 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] = ' '; + 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] = ' '; + } 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] = ' '; + } else + prst->nulls[5] = 'n'; + prst->nulls[6] = 'n'; + + pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1); + s->nlabel = i + 1; + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + prst->stack = lcons(s, prst->stack); + MemoryContextSwitchTo(oldcontext); + s = NULL; + } + + UnlockReleaseBuffer(buffer); + if (s) pfree(s); + } while(0); + + htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls); + result = TupleGetDatum(funcctx->slot, htuple); + + SRF_RETURN_NEXT(funcctx, result); +#endif +} +