#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
#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"
+#if PG_VERSION_NUM >= 90600
+#include <catalog/pg_am.h>
+#endif
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "catalog/namespace.h"
#if PG_VERSION_NUM >= 80300
#include <tsearch/ts_utils.h>
#endif
+#if PG_VERSION_NUM >= 100000
+#include <utils/regproc.h>
+#include <utils/varlena.h>
+#endif
#include <utils/tqual.h>
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include <access/relscan.h>
-#define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
+#define PAGESIZE (BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + sizeof(ItemIdData)))
#ifndef PG_NARGS
#define PG_NARGS() (fcinfo->nargs)
#endif
+#if PG_VERSION_NUM >= 90600
+#define ISNULL true
+#define ISNOTNULL false
+#define heap_formtuple heap_form_tuple
+#else
+#define ISNULL 'n'
+#define ISNOTNULL ' '
+#endif
+
static char
*t2c(text* in) {
- char *out=palloc( VARSIZE(in) );
- memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
- out[ VARSIZE(in)-VARHDRSZ ] ='\0';
- return out;
+ char *out=palloc(VARSIZE(in));
+
+ memcpy(out, VARDATA(in), VARSIZE(in)-VARHDRSZ);
+ out[ VARSIZE(in)-VARHDRSZ ] ='\0';
+ return out;
}
typedef struct {
int maxlevel;
text *txt;
char *ptr;
- int len;
+ int len;
} IdxInfo;
static Relation checkOpenedRelation(Relation r, Oid PgAmOid);
#ifdef PG_MODULE_MAGIC
-/* >= 8.2 */
+/* >= 8.2 */
PG_MODULE_MAGIC;
static Relation
gist_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, AccessExclusiveLock), GIST_AM_OID);
}
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 SET_VARSIZE(p,l) VARATT_SIZEP(p)=(l)
#endif
-static Relation
+static Relation
checkOpenedRelation(Relation r, Oid PgAmOid) {
- if ( r->rd_am == NULL )
+ if ( r->rd_index == NULL )
elog(ERROR, "Relation %s.%s is not an index",
get_namespace_name(RelationGetNamespace(r)),
RelationGetRelationName(r)
get_namespace_name(RelationGetNamespace(r)),
RelationGetRelationName(r)
);
-
+
return r;
}
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,
- (int) maxoff,
- PageGetFreeSpace(page),
+ coff,
+ level,
+ blk,
+ (int) maxoff,
+ (int) PageGetFreeSpace(page),
100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
GistPageGetOpaque(page)->rightlink,
( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
index = gist_index_open(relvar);
PG_FREE_IF_COPY(name,0);
- info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
+ info.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
info.len=1024;
info.txt=(text*)palloc( info.len );
info.ptr=((char*)info.txt)+VARHDRSZ;
}
typedef struct {
- int level;
- int numpages;
- int numleafpages;
- int numtuple;
- int numinvalidtuple;
- int numleaftuple;
+ int level;
+ int numpages;
+ int numleafpages;
+ int numtuple;
+ int numinvalidtuple;
+ int numleaftuple;
uint64 tuplesize;
uint64 leaftuplesize;
uint64 totalsize;
gist_index_close(index);
pfree(relname);
- sprintf(ptr,
+ sprintf(ptr,
"Number of levels: %d\n"
"Number of pages: %d\n"
"Number of leaf pages: %d\n"
info.totalsize);
ptr=strchr(ptr,'\0');
-
+
SET_VARSIZE(out, ptr-((char*)out));
PG_RETURN_POINTER(out);
}
RangeVar *relvar;
Relation index;
Datum *dvalues;
+#if PG_VERSION_NUM >= 90600
+ bool *nulls;
+#else
char *nulls;
+#endif
GPItem *item;
} TypeStorage;
GPItem *nitem;
MemoryContext oldcontext;
Relation index = ( (TypeStorage*)(funcctx->user_fctx) )->index;
-
+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
nitem = (GPItem*)palloc( sizeof(GPItem) );
memset(nitem,0,sizeof(GPItem));
nitem->page = (Page) BufferGetPage(nitem->buffer);
nitem->offset=FirstOffsetNumber;
nitem->next = ( (TypeStorage*)(funcctx->user_fctx) )->item;
- nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
+ nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
( (TypeStorage*)(funcctx->user_fctx) )->item = nitem;
MemoryContextSwitchTo(oldcontext);
return nitem;
-}
+}
static GPItem*
closeGPPage( FuncCallContext *funcctx ) {
GPItem *oitem = ( (TypeStorage*)(funcctx->user_fctx) )->item;
( (TypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
-
+
ReleaseBuffer(oitem->buffer);
pfree( oitem );
- return ( (TypeStorage*)(funcctx->user_fctx) )->item;
+ return ( (TypeStorage*)(funcctx->user_fctx) )->item;
}
static void
}
st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
- st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
+ st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(*st->nulls));
funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
pfree(relname);
st->item=openGPPage(funcctx, GIST_ROOT_BLKNO);
-}
+}
-static void
+static void
close_call( FuncCallContext *funcctx ) {
TypeStorage *st = (TypeStorage*)(funcctx->user_fctx);
-
- while( st->item && closeGPPage(funcctx) );
-
- pfree( st->dvalues );
- pfree( st->nulls );
+
+ while(st->item && closeGPPage(funcctx));
+
+ pfree(st->dvalues);
+ pfree(st->nulls);
gist_index_close(st->index);
}
PG_FREE_IF_COPY(name,0);
}
- funcctx = SRF_PERCALL_SETUP();
+ funcctx = SRF_PERCALL_SETUP();
st = (TypeStorage*)(funcctx->user_fctx);
if ( !st->item ) {
if ( ! closeGPPage(funcctx) ) {
close_call(funcctx);
SRF_RETURN_DONE(funcctx);
- }
+ }
}
iid = PageGetItemId( st->item->page, st->item->offset );
ituple = (IndexTuple) PageGetItem(st->item->page, iid);
st->dvalues[0] = Int32GetDatum( st->item->level );
- st->nulls[0] = ' ';
+ st->nulls[0] = ISNOTNULL;
st->dvalues[1] = BoolGetDatum( (!GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple)) ? false : true );
- st->nulls[1] = ' ';
+ st->nulls[1] = ISNOTNULL;
for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
st->dvalues[i] = (Datum)0;
- st->nulls[i] = 'n';
+ st->nulls[i] = ISNULL;
} else {
- st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
- st->nulls[i] = ( isnull ) ? 'n' : ' ';
+ st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
+ st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
}
}
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;
/*
* We scaned the whole page, so we should take right page
*/
- BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
+ BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
if ( GinPageRightMost(page) )
return false; /* no more page */
}
/*
- * Refinds a previois position, at returns it has correctly
+ * Refinds a previois position, at returns it has correctly
* set offset and buffer is locked
*/
static bool
refindPosition(GinStatState *st)
{
- Page page;
+ Page page;
/* find left if needed (it causes only for first search) */
for (;;) {
}
for(;;) {
- int cmp;
- bool isnull;
- Datum datum;
- IndexTuple itup;
+ 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->ginstate.tupdesc->attrs[0]->attbyval )
+ if ( st->curval && !st->index->rd_att->attrs[st->attnum]->attbyval )
pfree( (void*) st->curval );
return true;
}
}
static void
-gin_setup_firstcall(FuncCallContext *funcctx, text *name) {
+gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
MemoryContext oldcontext;
GinStatState *st;
char *relname=t2c(name);
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[0]->atttypid,
- st->index->rd_att->attrs[0]->atttypmod,
- st->index->rd_att->attrs[0]->attndims);
+ 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) );
+ memset( st->nulls, ISNOTNULL, 2*sizeof(*st->nulls) );
funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
static void
processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
- MemoryContext oldcontext;
- Datum datum;
+ MemoryContext oldcontext;
+#if PG_VERSION_NUM >= 90100
+ Datum key;
+#elif PG_VERSION_NUM < 80400
bool isnull;
+#endif
- datum = index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull);
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+#if PG_VERSION_NUM >= 90100
+ key = gintuple_get_key(&st->ginstate, itup, &st->category);
+
+ if (st->category != GIN_CAT_NORM_KEY)
+ st->curval = (Datum)0;
+ else
+#endif
st->curval = datumCopy(
+#if PG_VERSION_NUM >= 90100
+ key,
+#elif PG_VERSION_NUM >= 80400
+ gin_index_getattr(&st->ginstate, itup),
+#else
index_getattr(itup, FirstOffsetNumber, st->ginstate.tupdesc, &isnull),
- st->ginstate.tupdesc->attrs[0]->attbyval,
- st->ginstate.tupdesc->attrs[0]->attlen );
+#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) ? ISNOTNULL : ISNULL;
+#endif
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;
+ Buffer entrybuffer;
+#endif
Page page;
+ uint32 predictNumber;
LockBuffer(st->buffer, GIN_UNLOCK);
+#if PG_VERSION_NUM >= 90400
+ stack = ginScanBeginPostingTree(&btree, st->index, rootblkno
+#if PG_VERSION_NUM >= 90600
+ , NULL
+#endif
+ );
+ page = BufferGetPage(stack->buffer);
+ ItemPointerSetMin(&minItem);
+ list = GinDataLeafPageGetItems(page, &nlist, minItem);
+ pfree(list);
+ predictNumber = stack->predictNumber;
+ st->dvalues[1] = Int32GetDatum( predictNumber * nlist );
+#elif PG_VERSION_NUM >= 90100
+ gdi = ginPrepareScanPostingTree(st->index, rootblkno, TRUE);
+ entrybuffer = ginScanBeginPostingTree(gdi);
+ page = BufferGetPage(entrybuffer);
+ predictNumber = gdi->stack->predictNumber;
+ st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
+#else
gdi = prepareScanPostingTree(st->index, rootblkno, TRUE);
entrybuffer = scanBeginPostingTree(gdi);
-
page = BufferGetPage(entrybuffer);
- st->dvalues[1] = Int32GetDatum( gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff );
+ predictNumber = gdi->stack->predictNumber;
+ st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
+#endif
+#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);
if (SRF_IS_FIRSTCALL()) {
text *name=PG_GETARG_TEXT_P(0);
funcctx = SRF_FIRSTCALL_INIT();
- gin_setup_firstcall(funcctx, name);
+ gin_setup_firstcall(funcctx, name, (PG_NARGS()==2) ? PG_GETARG_INT32(1) : 0 );
PG_FREE_IF_COPY(name,0);
}
- funcctx = SRF_PERCALL_SETUP();
+ funcctx = SRF_PERCALL_SETUP();
st = (GinStatState*)(funcctx->user_fctx);
if ( refindPosition(st) == false ) {
SRF_RETURN_DONE(funcctx);
}
- st->offset++;
-
- if (moveRightIfItNeeded(st)==false) {
- UnlockReleaseBuffer( st->buffer );
- gin_index_close(st->index);
+ for(;;) {
+ st->offset++;
- SRF_RETURN_DONE(funcctx);
- }
+ 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));
+ 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);
#if PG_VERSION_NUM >= 80300
Datum
gin_count_estimate(PG_FUNCTION_ARGS) {
- text *name=PG_GETARG_TEXT_P(0);
- Relation index;
+ text *name=PG_GETARG_TEXT_P(0);
+ Relation index;
IndexScanDesc scan;
int64 count = 0;
- char *relname=t2c(name);
+ char *relname=t2c(name);
ScanKeyData key;
#if PG_VERSION_NUM >= 80400
- TIDBitmap *bitmap = tbm_create(work_mem * 1024L);
+ TIDBitmap *bitmap = tbm_create(work_mem * 1024L
+#if PG_VERSION_NUM >= 100000
+ , NULL
+#endif
+ );
#else
#define MAXTIDS 1024
ItemPointerData tids[MAXTIDS];
elog(ERROR, "Column type is not a tsvector");
}
- key.sk_flags = 0;
- key.sk_attno = 1;
+ 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 >= 80400
+#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);
+ tbm_free(bitmap);
+#elif PG_VERSION_NUM >= 80400
scan = index_beginscan_bitmap(index, SnapshotNow, 1, &key);
count = index_getbitmap(scan, bitmap);
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,
+ usedLeafSpace = 0.0,
+ usedInnerSpace = 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 (SpGistPageIsLeaf(page))
+ usedLeafSpace += bufferSize - pageFree;
+ else
+ usedInnerSpace += 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"
+ "usedInnerSpace: %.2f kbytes\n"
+ "usedLeafSpace: %.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,
+ usedInnerSpace / 1024.0,
+ usedLeafSpace / 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[8 /* see CreateTemplateTupleDesc call */];
+ char nulls[8 /* 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 */ + 1, false);
+ TupleDescInitEntry(tupdesc, 1, "tid", TIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, 2, "allthesame", BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, 3, "node", INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, 4, "level", INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, 5, "tid_pointer", TIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, 6, "prefix",
+ (prst->state.attPrefixType.type == VOIDOID) ? INT4OID : prst->state.attPrefixType.type, -1, 0);
+ TupleDescInitEntry(tupdesc, 7, "label",
+ (prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
+ TupleDescInitEntry(tupdesc, 8, "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] = ISNOTNULL;
+ prst->nulls[1] = ISNULL;
+ prst->nulls[2] = ISNULL;
+ prst->dvalues[3] = s->level;
+ prst->nulls[3] = ISNOTNULL;
+ prst->nulls[4] = ISNULL;
+ prst->nulls[5] = ISNULL;
+ prst->nulls[6] = ISNULL;
+ prst->dvalues[7] = datumCopy(SGLTDATUM(leafTuple, &prst->state),
+ prst->state.attType.attbyval, prst->state.attType.attlen);
+ prst->nulls[7] = ISNOTNULL;
+ } 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] = ISNOTNULL;
+ prst->dvalues[1] = innerTuple->allTheSame;
+ prst->nulls[1] = ISNOTNULL;
+ prst->dvalues[2] = Int32GetDatum(s->nlabel);
+ prst->nulls[2] = ISNOTNULL;
+ prst->dvalues[3] = s->level;
+ prst->nulls[3] = ISNOTNULL;
+ tid = palloc(sizeof(ItemPointerData));
+ *tid = node->t_tid;
+ prst->dvalues[4] = PointerGetDatum(tid);
+ prst->nulls[5] = ISNOTNULL;
+ if (innerTuple->prefixSize > 0) {
+ prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
+ prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
+ prst->nulls[5] = ISNOTNULL;
+ } else
+ prst->nulls[5] = ISNULL;
+ if (!IndexTupleHasNulls(node)) {
+ prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
+ prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
+ prst->nulls[6] = ISNOTNULL;
+ } else
+ prst->nulls[6] = ISNULL;
+ prst->nulls[7] = ISNULL;
+
+ 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
+}
+