add usedInnerSpace/usedLeafSpace for spgist
[gevel.git] / gevel.c
diff --git a/gevel.c b/gevel.c
index ef8f3d9..0e14761 100644 (file)
--- a/gevel.c
+++ b/gevel.c
@@ -2,6 +2,9 @@
 
 #include "access/genam.h"
 #include "access/gin.h"
+#if PG_VERSION_NUM >= 90400
+#include "utils/snapmgr.h"
+#endif
 #if PG_VERSION_NUM >= 90100
 #include "access/gin_private.h"
 #endif
@@ -11,6 +14,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"
@@ -62,7 +66,7 @@ PG_MODULE_MAGIC;
 
 static Relation
 gist_index_open(RangeVar *relvar) {
-#if PG_VERSION_NUM <= 90100 
+#if PG_VERSION_NUM < 90200 
        Oid relOid = RangeVarGetRelid(relvar, false);
 #else
        Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
@@ -75,7 +79,7 @@ gist_index_open(RangeVar *relvar) {
 
 static Relation
 gin_index_open(RangeVar *relvar) {
-#if PG_VERSION_NUM <= 90100 
+#if PG_VERSION_NUM < 90200 
        Oid relOid = RangeVarGetRelid(relvar, false);
 #else
        Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
@@ -690,25 +694,49 @@ processTuple( FuncCallContext  *funcctx,  GinStatState *st, IndexTuple itup ) {
                
        if ( GinIsPostingTree(itup) ) {
                BlockNumber     rootblkno = GinGetPostingTree(itup);
+#if PG_VERSION_NUM >= 90400
+               GinBtreeData    btree;
+               GinBtreeStack   *stack;
+               ItemPointerData minItem;
+               int                             nlist;
+               ItemPointer             list;
+#else
                GinPostingTreeScan *gdi;
                Buffer          entrybuffer;              
+#endif
                Page        page;
+               uint32          predictNumber;
 
                LockBuffer(st->buffer, GIN_UNLOCK);
-#if PG_VERSION_NUM >= 90100
+#if PG_VERSION_NUM >= 90400
+               stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
+               page = BufferGetPage(stack->buffer);
+               ItemPointerSetMin(&minItem);
+               list = GinDataLeafPageGetItems(page, &nlist, minItem);
+               pfree(list);
+               predictNumber = stack->predictNumber;
+#elif PG_VERSION_NUM >= 90100
                gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
                entrybuffer = ginScanBeginPostingTree(gdi);
+               page = BufferGetPage(entrybuffer);
+               predictNumber = gdi->stack->predictNumber;
 #else
                gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
                entrybuffer = scanBeginPostingTree(gdi);
+               page = BufferGetPage(entrybuffer);
+               predictNumber = gdi->stack->predictNumber;
 #endif
 
-               page = BufferGetPage(entrybuffer);
-               st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
+               st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
 
+#if PG_VERSION_NUM < 90400
                LockBuffer(entrybuffer, GIN_UNLOCK);
                freeGinBtreeStack(gdi->stack);
                pfree(gdi);
+#else
+               LockBuffer(stack->buffer, GIN_UNLOCK);
+               freeGinBtreeStack(stack);
+#endif
        } else {
                st->dvalues[1] = Int32GetDatum( GinGetNPosting(itup) );
                LockBuffer(st->buffer, GIN_UNLOCK);
@@ -807,7 +835,11 @@ gin_count_estimate(PG_FUNCTION_ARGS) {
        fmgr_info( F_TS_MATCH_VQ , &key.sk_func );
 
 #if PG_VERSION_NUM >= 90100
+#if PG_VERSION_NUM >= 90400
+       scan = index_beginscan_bitmap(index, SnapshotSelf, 1);
+#else
        scan = index_beginscan_bitmap(index, SnapshotNow, 1);
+#endif
        index_rescan(scan, &key, 1, NULL, 0);
 
        count = index_getbitmap(scan, bitmap);
@@ -859,7 +891,9 @@ spgist_stat(PG_FUNCTION_ARGS)
                                leafPages = 0,
                                emptyPages = 0,
                                deletedPages = 0;
-       double    usedSpace = 0.0;
+       double    usedSpace = 0.0,
+                         usedLeafSpace = 0.0,
+                         usedInnerSpace = 0.0;
        char            res[1024];
        int              bufferSize = -1;
        int64      innerTuples = 0,
@@ -882,7 +916,7 @@ spgist_stat(PG_FUNCTION_ARGS)
 
        totalPages = RelationGetNumberOfBlocks(index);
 
-       for (blkno = SPGIST_HEAD_BLKNO; blkno < totalPages; blkno++)
+       for (blkno = SPGIST_ROOT_BLKNO; blkno < totalPages; blkno++)
        {
                Buffer    buffer;
                Page            page;
@@ -936,6 +970,10 @@ spgist_stat(PG_FUNCTION_ARGS)
                pageFree = PageGetExactFreeSpace(page);
 
                usedSpace += bufferSize - pageFree;
+               if (SpGistPageIsLeaf(page))
+                       usedLeafSpace += bufferSize - pageFree;
+               else
+                       usedInnerSpace += bufferSize - pageFree;
 
                if (pageFree == bufferSize)
                        emptyPages++;
@@ -954,6 +992,8 @@ spgist_stat(PG_FUNCTION_ARGS)
                         "leafPages:         %u\n"
                         "emptyPages:        %u\n"
                         "usedSpace:         %.2f kbytes\n"
+                        "usedInnerSpace:    %.2f kbytes\n"
+                        "usedLeafSpace:     %.2f kbytes\n"
                         "freeSpace:         %.2f kbytes\n"
                         "fillRatio:         %.2f%%\n"
                         "leafTuples:        " INT64_FORMAT "\n"
@@ -965,6 +1005,8 @@ spgist_stat(PG_FUNCTION_ARGS)
                         "innerRedirects:    " INT64_FORMAT,
                         totalPages, deletedPages, innerPages, leafPages, emptyPages,
                         usedSpace / 1024.0,
+                        usedInnerSpace / 1024.0,
+                        usedLeafSpace / 1024.0,
                         (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
                         100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
                         leafTuples, innerTuples, nAllTheSame,
@@ -975,3 +1017,398 @@ 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_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 == false && 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 == false && !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
+}
+
+
+PG_FUNCTION_INFO_V1(gin_statpage);
+Datum gin_statpage(PG_FUNCTION_ARGS);
+Datum
+gin_statpage(PG_FUNCTION_ARGS)
+{
+#if PG_VERSION_NUM < 90400
+       elog(NOTICE, "Function is not working under PgSQL < 9.4");
+
+       PG_RETURN_TEXT_P(CStringGetTextDatum("???"));
+#else
+       text       *name = PG_GETARG_TEXT_P(0);
+       RangeVar   *relvar;
+       Relation        index;
+       BlockNumber blkno;
+       char            res[1024];
+       uint32          totalPages,
+                               entryPages = 0,
+                               dataPages = 0,
+                               dataInnerPages = 0,
+                               dataLeafPages = 0,
+                               entryInnerPages = 0,
+                               entryLeafPages = 0
+                               ;
+       uint64          dataInnerFreeSpace = 0,
+                               dataLeafFreeSpace = 0,
+                               dataInnerTuplesCount = 0,
+                               dataLeafIptrsCount = 0,
+                               entryInnerFreeSpace = 0,
+                               entryLeafFreeSpace = 0,
+                               entryInnerTuplesCount = 0,
+                               entryLeafTuplesCount = 0,
+                               entryPostingSize = 0,
+                               entryPostingCount = 0,
+                               entryAttrSize = 0
+                               ;
+
+       relvar = makeRangeVarFromNameList(textToQualifiedNameList(name));
+       index = relation_openrv(relvar, AccessExclusiveLock);
+
+       if (index->rd_rel->relkind != RELKIND_INDEX ||
+                       index->rd_rel->relam != GIN_AM_OID)
+               elog(ERROR, "relation \"%s\" is not an SPGiST index",
+                        RelationGetRelationName(index));
+
+       totalPages = RelationGetNumberOfBlocks(index);
+
+       for (blkno = GIN_ROOT_BLKNO; blkno < totalPages; blkno++)
+       {
+               Buffer          buffer;
+               Page            page;
+               PageHeader      header;
+
+               buffer = ReadBuffer(index, blkno);
+               LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+               page = BufferGetPage(buffer);
+               header = (PageHeader)page;
+
+               if (GinPageIsData(page))
+               {
+                       dataPages++;
+                       if (GinPageIsLeaf(page))
+                       {
+                               ItemPointerData minItem;
+                               int nlist;
+
+                               dataLeafPages++;
+                               dataLeafFreeSpace += header->pd_upper - header->pd_lower;
+                               ItemPointerSetMin(&minItem);
+                               pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
+                               dataLeafIptrsCount += nlist;
+                       }
+                       else
+                       {
+                               dataInnerPages++;
+                               dataInnerFreeSpace += header->pd_upper - header->pd_lower;
+                               dataInnerTuplesCount += GinPageGetOpaque(page)->maxoff;
+                       }
+               }
+               else
+               {
+                       IndexTuple itup;
+                       OffsetNumber i, maxoff;
+
+                       maxoff = PageGetMaxOffsetNumber(page);
+
+                       entryPages++;
+                       if (GinPageIsLeaf(page))
+                       {
+                               entryLeafPages++;
+                               entryLeafFreeSpace += header->pd_upper - header->pd_lower;
+                               entryLeafTuplesCount += maxoff;
+                       }
+                       else
+                       {
+                               entryInnerPages++;
+                               entryInnerFreeSpace += header->pd_upper - header->pd_lower;
+                               entryInnerTuplesCount += maxoff;
+                       }
+
+                       for (i = 1; i <= maxoff; i++)
+                       {
+                               itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
+
+                               if (GinPageIsLeaf(page))
+                               {
+                                       GinPostingList *list = (GinPostingList *)GinGetPosting(itup);
+                                       entryPostingCount += GinGetNPosting(itup);
+                                       entryPostingSize += SizeOfGinPostingList(list);
+                                       entryAttrSize += GinGetPostingOffset(itup) - IndexInfoFindDataOffset((itup)->t_info);
+                               }
+                               else
+                               {
+                                       entryAttrSize += IndexTupleSize(itup) - IndexInfoFindDataOffset((itup)->t_info);
+                               }
+                       }
+               }
+
+               UnlockReleaseBuffer(buffer);
+       }
+
+       index_close(index, AccessExclusiveLock);
+       totalPages--;
+
+       snprintf(res, sizeof(res),
+                        "totalPages:            %u\n"
+                        "dataPages:             %u\n"
+                        "dataInnerPages:        %u\n"
+                        "dataLeafPages:         %u\n"
+                        "dataInnerFreeSpace:    " INT64_FORMAT "\n"
+                        "dataLeafFreeSpace:     " INT64_FORMAT "\n"
+                        "dataInnerTuplesCount:  " INT64_FORMAT "\n"
+                        "dataLeafIptrsCount:    " INT64_FORMAT "\n"
+                        "entryPages:            %u\n"
+                        "entryInnerPages:       %u\n"
+                        "entryLeafPages:        %u\n"
+                        "entryInnerFreeSpace:   " INT64_FORMAT "\n"
+                        "entryLeafFreeSpace:    " INT64_FORMAT "\n"
+                        "entryInnerTuplesCount: " INT64_FORMAT "\n"
+                        "entryLeafTuplesCount:  " INT64_FORMAT "\n"
+                        "entryPostingSize:      " INT64_FORMAT "\n"
+                        "entryPostingCount:     " INT64_FORMAT "\n"
+                        "entryAttrSize:         " INT64_FORMAT "\n"
+                        ,
+                        totalPages,
+                        dataPages,
+                        dataInnerPages,
+                        dataLeafPages,
+                        dataInnerFreeSpace,
+                        dataLeafFreeSpace,
+                        dataInnerTuplesCount,
+                        dataLeafIptrsCount,
+                        entryPages,
+                        entryInnerPages,
+                        entryLeafPages,
+                        entryInnerFreeSpace,
+                        entryLeafFreeSpace,
+                        entryInnerTuplesCount,
+                        entryLeafTuplesCount,
+                        entryPostingSize,
+                        entryPostingCount,
+                        entryAttrSize
+                        );
+
+       PG_RETURN_TEXT_P(CStringGetTextDatum(res));
+#endif
+}
+