X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?p=gevel.git;a=blobdiff_plain;f=gevel.c;h=d13c724a04037107bc3374b50ab383b490f2ef40;hp=ef8f3d9f1f0809a03411a0711b34288405609316;hb=3cc93ce2ee4d6fac298a059d30f6e4b11f174c3c;hpb=9e905f5552e767ed63cac78e424536c8104485f9 diff --git a/gevel.c b/gevel.c index ef8f3d9..d13c724 100644 --- a/gevel.c +++ b/gevel.c @@ -11,6 +11,7 @@ #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" @@ -975,3 +976,229 @@ spgist_stat(PG_FUNCTION_ARGS) #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_HEAD_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 +} +