#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"
#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
+}
+