#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
-#include <utils/tqual.h>
+#if PG_VERSION_NUM >= 100000
+#include <utils/regproc.h>
+#include <utils/varlena.h>
+#endif
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/datum.h"
#include <access/heapam.h>
#include <catalog/pg_type.h>
#include <access/relscan.h>
+#if PG_VERSION_NUM >= 120000
+#include <access/nbtree.h>
+#include <access/brin.h>
+#include <access/brin_revmap.h>
+#include <access/brin_page.h>
+#include <access/brin_tuple.h>
+#endif
-#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
+#if PG_VERSION_NUM < 90200
Oid relOid = RangeVarGetRelid(relvar, false);
#else
Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
static Relation
gin_index_open(RangeVar *relvar) {
-#if PG_VERSION_NUM < 90200
+#if PG_VERSION_NUM < 90200
Oid relOid = RangeVarGetRelid(relvar, false);
#else
Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
#define gin_index_close(r) index_close((r), AccessShareLock)
+#if PG_VERSION_NUM >= 120000
+static Relation
+btree_index_open(RangeVar *relvar) {
+ Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
+ return checkOpenedRelation(
+ index_open(relOid, AccessExclusiveLock), BTREE_AM_OID);
+}
+
+#define btree_index_close(r) index_close((r), AccessExclusiveLock)
+
+static Relation
+brin_index_open(RangeVar *relvar)
+{
+ Oid relOid = RangeVarGetRelid(relvar, NoLock, false);
+ return checkOpenedRelation(
+ index_open(relOid, AccessExclusiveLock), BRIN_AM_OID);
+}
+
+#define brin_index_close(r) index_close((r), AccessExclusiveLock)
+#endif
+
#else /* <8.2 */
static Relation
index_close(rel);
}
+static Relation
+btree_index_open(RangeVar *relvar) {
+ Relation rel = index_openrv(relvar);
+
+ LockRelation(rel, AccessExclusiveLock);
+ return checkOpenedRelation(rel, BTREE_AM_OID);
+}
+
+static void
+btree_index_close(Relation rel) {
+ UnlockRelation(rel, AccessExclusiveLock);
+ index_close(rel);
+}
+
#endif
#if PG_VERSION_NUM >= 80300
#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: %u 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,
+ coff,
+ level,
blk,
- (int) maxoff,
- (int) PageGetFreeSpace(page),
+ (int) maxoff,
+ (int) PageGetFreeSpace(page),
100.0*(((float)PAGESIZE)-(float)PageGetFreeSpace(page))/((float)PAGESIZE),
GistPageGetOpaque(page)->rightlink,
( GistPageGetOpaque(page)->rightlink == InvalidBlockNumber ) ? "InvalidBlockNumber" : "OK" );
text *name=PG_GETARG_TEXT_P(0);
char *relname=t2c(name);
RangeVar *relvar;
- Relation index;
- List *relname_list;
+ Relation index;
+ List *relname_list;
IdxInfo info;
relname_list = stringToQualifiedNameList(relname, "gist_tree");
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;
text *name=PG_GETARG_TEXT_P(0);
char *relname=t2c(name);
RangeVar *relvar;
- Relation index;
- List *relname_list;
+ Relation index;
+ List *relname_list;
IdxStat info;
text *out=(text*)palloc(1024);
char *ptr=((char*)out)+VARHDRSZ;
gist_index_close(index);
pfree(relname);
- sprintf(ptr,
- "Number of levels: %d\n"
- "Number of pages: %d\n"
- "Number of leaf pages: %d\n"
- "Number of tuples: %d\n"
+ sprintf(ptr,
+ "Number of levels: %d\n"
+ "Number of pages: %d\n"
+ "Number of leaf pages: %d\n"
+ "Number of tuples: %d\n"
"Number of invalid tuples: %d\n"
- "Number of leaf tuples: %d\n"
- "Total size of tuples: "INT64_FORMAT" bytes\n"
+ "Number of leaf tuples: %d\n"
+ "Total size of tuples: "INT64_FORMAT" bytes\n"
"Total size of leaf tuples: "INT64_FORMAT" bytes\n"
- "Total size of index: "INT64_FORMAT" bytes\n",
+ "Total size of index: "INT64_FORMAT" bytes\n",
info.level+1,
info.numpages,
info.numleafpages,
info.totalsize);
ptr=strchr(ptr,'\0');
-
+
SET_VARSIZE(out, ptr-((char*)out));
PG_RETURN_POINTER(out);
}
typedef struct {
List *relname_list;
RangeVar *relvar;
- Relation index;
+ Relation index;
Datum *dvalues;
+#if PG_VERSION_NUM >= 90600
+ bool *nulls;
+#else
char *nulls;
+#endif
GPItem *item;
} TypeStorage;
static GPItem*
openGPPage( FuncCallContext *funcctx, BlockNumber blk ) {
GPItem *nitem;
- MemoryContext oldcontext;
+ 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;
}
+#if PG_VERSION_NUM >= 110000
+#define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)].v
+#else
+#define TS_GET_TYPEVAL(s, i, v) (s)->index->rd_att->attrs[(i)]->v
+#endif
+
static void
setup_firstcall(FuncCallContext *funcctx, text *name) {
- MemoryContext oldcontext;
- TypeStorage *st;
+ MemoryContext oldcontext;
+ TypeStorage *st;
char *relname=t2c(name);
- TupleDesc tupdesc;
- char attname[NAMEDATALEN];
+ TupleDesc tupdesc;
+ char attname[NAMEDATALEN];
int i;
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
st->index = gist_index_open(st->relvar);
funcctx->user_fctx = (void*)st;
- tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2, false);
+#if PG_VERSION_NUM >= 120000
+ tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
+#else
+ tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
+#endif
TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
for (i = 0; i < st->index->rd_att->natts; i++) {
tupdesc,
i+3,
attname,
- st->index->rd_att->attrs[i]->atttypid,
- st->index->rd_att->attrs[i]->atttypmod,
- st->index->rd_att->attrs[i]->attndims
+ TS_GET_TYPEVAL(st, i, atttypid),
+ TS_GET_TYPEVAL(st, i, atttypmod),
+ TS_GET_TYPEVAL(st, i, attndims)
);
}
st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
- st->nulls = (char *) palloc((tupdesc->natts+2) * sizeof(char));
+ st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
- funcctx->slot = TupleDescGetSlot(tupdesc);
+#if PG_VERSION_NUM >= 120000
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+#else
+ funcctx->slot = TupleDescGetSlot(tupdesc);
+#endif
MemoryContextSwitchTo(oldcontext);
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);
}
Datum
gist_print(PG_FUNCTION_ARGS) {
FuncCallContext *funcctx;
- TypeStorage *st;
+ TypeStorage *st;
Datum result=(Datum)0;
- ItemId iid;
- IndexTuple ituple;
- HeapTuple htuple;
+ ItemId iid;
+ IndexTuple ituple;
+ HeapTuple htuple;
int i;
bool isnull;
if (SRF_IS_FIRSTCALL()) {
- text *name=PG_GETARG_TEXT_P(0);
+ text *name=PG_GETARG_TEXT_P(0);
funcctx = SRF_FIRSTCALL_INIT();
setup_firstcall(funcctx, name);
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] = ' ';
- for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++) {
- if ( !GistPageIsLeaf(st->item->page) && GistTupleIsInvalid(ituple) ) {
+ 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;
}
}
htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
+#if PG_VERSION_NUM >= 120000
+ result = HeapTupleGetDatum(htuple);
+#else
result = TupleGetDatum(funcctx->slot, htuple);
+#endif
st->item->offset = OffsetNumberNext(st->item->offset);
if ( !GistPageIsLeaf(st->item->page) )
openGPPage(funcctx, ItemPointerGetBlockNumber(&(ituple->t_tid)) );
GinNullCategory category;
#endif
Datum dvalues[2];
+#if PG_VERSION_NUM >= 110000
+ bool nulls[2];
+#else
char nulls[2];
+#endif
} GinStatState;
static bool
/*
* 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;
+ int cmp;
#if PG_VERSION_NUM >= 90100
GinNullCategory category;
#elif PG_VERSION_NUM < 80400
bool isnull = false;
#endif
Datum datum;
- IndexTuple itup;
+ 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);
+ 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);
#endif
if ( cmp == 0 )
{
- if ( !st->index->rd_att->attrs[st->attnum]->attbyval )
+ if ( st->curval && !TS_GET_TYPEVAL(st, st->attnum, attbyval) )
pfree( (void*) st->curval );
return true;
}
static void
gin_setup_firstcall(FuncCallContext *funcctx, text *name, int attnum) {
- MemoryContext oldcontext;
- GinStatState *st;
+ MemoryContext oldcontext;
+ GinStatState *st;
char *relname=t2c(name);
- TupleDesc tupdesc;
+ TupleDesc tupdesc;
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
funcctx->user_fctx = (void*)st;
+#if PG_VERSION_NUM >= 120000
+ tupdesc = CreateTemplateTupleDesc(2);
+#else
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);
+#endif
+ TupleDescInitEntry(tupdesc, 1, "value",
+ TS_GET_TYPEVAL(st, st->attnum, atttypid),
+ TS_GET_TYPEVAL(st, st->attnum, atttypmod),
+ TS_GET_TYPEVAL(st, 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);
+#if PG_VERSION_NUM >= 120000
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+#else
+ funcctx->slot = TupleDescGetSlot(tupdesc);
+#endif
MemoryContextSwitchTo(oldcontext);
pfree(relname);
static void
processTuple( FuncCallContext *funcctx, GinStatState *st, IndexTuple itup ) {
- MemoryContext oldcontext;
-#if PG_VERSION_NUM < 80400
+ MemoryContext oldcontext;
+#if PG_VERSION_NUM >= 90100
+ Datum key;
+#elif PG_VERSION_NUM < 80400
bool isnull;
#endif
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
- gintuple_get_key(&st->ginstate, itup, &st->category),
+ key,
#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 );
+ TS_GET_TYPEVAL(st, st->attnum, attbyval),
+ TS_GET_TYPEVAL(st, 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';
+ st->nulls[0] = (st->category == GIN_CAT_NORM_KEY) ? ISNOTNULL : ISNULL;
#endif
-
+
if ( GinIsPostingTree(itup) ) {
BlockNumber rootblkno = GinGetPostingTree(itup);
#if PG_VERSION_NUM >= 90400
ItemPointer list;
#else
GinPostingTreeScan *gdi;
- Buffer entrybuffer;
+ Buffer entrybuffer;
#endif
- Page page;
+ Page page;
uint32 predictNumber;
LockBuffer(st->buffer, GIN_UNLOCK);
#if PG_VERSION_NUM >= 90400
- stack = ginScanBeginPostingTree(&btree, st->index, rootblkno);
+ 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);
predictNumber = gdi->stack->predictNumber;
-#endif
-
st->dvalues[1] = Int32GetDatum( predictNumber * GinPageGetOpaque(page)->maxoff );
+#endif
#if PG_VERSION_NUM < 90400
LockBuffer(entrybuffer, GIN_UNLOCK);
Datum
gin_stat(PG_FUNCTION_ARGS) {
FuncCallContext *funcctx;
- GinStatState *st;
+ GinStatState *st;
Datum result=(Datum)0;
- IndexTuple ituple;
- HeapTuple htuple;
+ IndexTuple ituple;
+ HeapTuple htuple;
Page page;
if (SRF_IS_FIRSTCALL()) {
- text *name=PG_GETARG_TEXT_P(0);
+ 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();
+ funcctx = SRF_PERCALL_SETUP();
st = (GinStatState*)(funcctx->user_fctx);
if ( refindPosition(st) == false ) {
for(;;) {
st->offset++;
-
- if (moveRightIfItNeeded(st)==false) {
+
+ if (moveRightIfItNeeded(st)==false) {
UnlockReleaseBuffer( st->buffer );
gin_index_close(st->index);
}
page = BufferGetPage(st->buffer);
- ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
+ ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, st->offset));
#if PG_VERSION_NUM >= 80400
if (st->attnum + 1 == gintuple_get_attrnum(&st->ginstate, ituple))
}
processTuple( funcctx, st, ituple );
-
+
htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
+#if PG_VERSION_NUM >= 120000
+ result = HeapTupleGetDatum(htuple);
+#else
result = TupleGetDatum(funcctx->slot, htuple);
-
+#endif
+
SRF_RETURN_NEXT(funcctx, result);
}
#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);
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"
+ "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,
+ "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))),
+ (((double) bufferSize) * ((double) totalPages) - usedSpace) / 1024,
+ 100.0 * (usedSpace / (((double) bufferSize) * ((double) totalPages))),
leafTuples, innerTuples, nAllTheSame,
nLeafPlaceholder, nInnerPlaceholder,
nLeafRedirect, nInnerRedirect);
#if PG_VERSION_NUM >= 90200
typedef struct SPGistPrintStackElem {
- ItemPointerData iptr;
+ 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 */];
+ Relation index;
+ Datum dvalues[8 /* see CreateTemplateTupleDesc call */];
+#if PG_VERSION_NUM >= 110000
+ bool nulls[8];
+#else
+ char nulls[8];
+#endif
List *stack;
} SPGistPrint;
e->iptr = *ip;
e->nlabel = 0;
e->level = level;
- prst->stack = lcons(e, prst->stack);
+ prst->stack = lcons(e, prst->stack);
MemoryContextSwitchTo(oldcontext);
}
MemoryContext oldcontext;
if (SRF_IS_FIRSTCALL()) {
- text *name=PG_GETARG_TEXT_P(0);
- RangeVar *relvar;
+ text *name=PG_GETARG_TEXT_P(0);
+ RangeVar *relvar;
Relation index;
ItemPointerData ipd;
TupleDesc tupdesc;
prst->index = index;
initSpGistState(&prst->state, index);
- tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */, false);
+#if PG_VERSION_NUM >= 120000
+ tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1);
+#else
+ tupdesc = CreateTemplateTupleDesc(3 /* types */ + 1 /* level */ + 1 /* nlabel */ + 2 /* tids */ + 1, false);
+#endif
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",
+ 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, 6, "label",
+ TupleDescInitEntry(tupdesc, 7, "label",
(prst->state.attLabelType.type == VOIDOID) ? INT4OID : prst->state.attLabelType.type, -1, 0);
- TupleDescInitEntry(tupdesc, 7, "leaf",
+ TupleDescInitEntry(tupdesc, 8, "leaf",
(prst->state.attType.type == VOIDOID) ? INT4OID : prst->state.attType.type, -1, 0);
- funcctx->slot = TupleDescGetSlot(tupdesc);
+#if PG_VERSION_NUM >= 120000
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+#else
+ funcctx->slot = TupleDescGetSlot(tupdesc);
+#endif
funcctx->user_fctx = (void*)prst;
PG_FREE_IF_COPY(name,0);
}
- funcctx = SRF_PERCALL_SETUP();
+ funcctx = SRF_PERCALL_SETUP();
prst = (SPGistPrint*)(funcctx->user_fctx);
next:
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] = ' ';
+ 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;
+ int i;
SpGistNodeTuple node;
SGITITERATE(innerTuple, i, node) {
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] = ' ';
+ 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[3] = PointerGetDatum(tid);
- prst->nulls[3] = ' ';
+ prst->dvalues[4] = PointerGetDatum(tid);
+ prst->nulls[5] = ISNOTNULL;
if (innerTuple->prefixSize > 0) {
- prst->dvalues[4] = datumCopy(SGITDATUM(innerTuple, &prst->state),
- prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
- prst->nulls[4] = ' ';
+ prst->dvalues[5] = datumCopy(SGITDATUM(innerTuple, &prst->state),
+ prst->state.attPrefixType.attbyval, prst->state.attPrefixType.attlen);
+ prst->nulls[5] = ISNOTNULL;
} else
- prst->nulls[4] = 'n';
+ prst->nulls[5] = ISNULL;
if (!IndexTupleHasNulls(node)) {
- prst->dvalues[5] = datumCopy(SGNTDATUM(node, &prst->state),
- prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
- prst->nulls[5] = ' ';
+ prst->dvalues[6] = datumCopy(SGNTDATUM(node, &prst->state),
+ prst->state.attLabelType.attbyval, prst->state.attLabelType.attlen);
+ prst->nulls[6] = ISNOTNULL;
} else
- prst->nulls[5] = 'n';
- prst->nulls[6] = 'n';
+ prst->nulls[6] = ISNULL;
+ prst->nulls[7] = ISNULL;
pushSPGistPrint(funcctx, prst, &node->t_tid, s->level + 1);
s->nlabel = i + 1;
} while(0);
htuple = heap_formtuple(funcctx->attinmeta->tupdesc, prst->dvalues, prst->nulls);
+#if PG_VERSION_NUM >= 120000
+ result = HeapTupleGetDatum(htuple);
+#else
result = TupleGetDatum(funcctx->slot, htuple);
+#endif
SRF_RETURN_NEXT(funcctx, result);
#endif
}
-
PG_FUNCTION_INFO_V1(gin_statpage);
Datum gin_statpage(PG_FUNCTION_ARGS);
Datum
BlockNumber blkno;
char res[1024];
uint32 totalPages,
+#if PG_VERSION_NUM >= 100000
+ deletedPages = 0,
+ emptyDataPages = 0,
+#endif
entryPages = 0,
dataPages = 0,
dataInnerPages = 0,
page = BufferGetPage(buffer);
header = (PageHeader)page;
+#if PG_VERSION_NUM >= 100000
+ if (GinPageIsDeleted(page))
+ {
+ deletedPages++;
+ }
+ else
+#endif
if (GinPageIsData(page))
{
dataPages++;
if (GinPageIsLeaf(page))
{
- ItemPointerData minItem;
+ ItemPointerData minItem, *ptr;
int nlist;
+
dataLeafPages++;
dataLeafFreeSpace += header->pd_upper - header->pd_lower;
ItemPointerSetMin(&minItem);
- pfree(GinDataLeafPageGetItems(page, &nlist, minItem));
- dataLeafIptrsCount += nlist;
+
+ ptr = GinDataLeafPageGetItems(page, &nlist, minItem);
+
+ if (ptr)
+ {
+ pfree(ptr);
+ dataLeafIptrsCount += nlist;
+ }
+ else
+ emptyDataPages++;
}
else
{
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"
+ "totalPages: %u\n"
+#if PG_VERSION_NUM >= 100000
+ "deletedPages: %u\n"
+ "emptyDataPages: %u\n"
+#endif
+ "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"
+ "dataLeafIptrsCount: " INT64_FORMAT "\n"
+ "entryPages: %u\n"
+ "entryInnerPages: %u\n"
+ "entryLeafPages: %u\n"
"entryInnerFreeSpace: " INT64_FORMAT "\n"
- "entryLeafFreeSpace: " 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"
+ "entryPostingSize: " INT64_FORMAT "\n"
+ "entryPostingCount: " INT64_FORMAT "\n"
+ "entryAttrSize: " INT64_FORMAT "\n"
,
totalPages,
+#if PG_VERSION_NUM >= 100000
+ deletedPages,
+ emptyDataPages,
+#endif
dataPages,
dataInnerPages,
dataLeafPages,
#endif
}
+#if PG_VERSION_NUM >= 120000
+typedef enum {stat, print} TreeCond;
+typedef struct
+{
+ IdxInfo idxInfo;
+ IdxStat idxStat;
+}BtreeIdxInfo;
+
+/*
+ * Depth-first search for btree
+ * using for statistic data collection
+ * and printing index values by level
+ */
+static void
+btree_deep_search(Relation rel, int level,
+ BlockNumber blk, BtreeIdxInfo *btreeIdxInfo, TreeCond cond)
+{
+ Page page;
+ IndexTuple itup;
+ ItemId iid;
+ OffsetNumber i,
+ maxoff;
+ BlockNumber cblk;
+ BTPageOpaque opaque;
+ Buffer buffer = _bt_getbuf(rel, blk, BT_READ);
+
+ page = (Page) BufferGetPage(buffer);
+ opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+ maxoff = PageGetMaxOffsetNumber(page);
+
+ switch (cond)
+ {
+ case stat:
+ {
+ btreeIdxInfo->idxStat.numpages++;
+ btreeIdxInfo->idxStat.tuplesize+=BTMaxItemSize(page)-PageGetFreeSpace(page);
+ btreeIdxInfo->idxStat.totalsize+=BLCKSZ;
+ btreeIdxInfo->idxStat.numtuple+=maxoff;
+
+ if (level > btreeIdxInfo->idxStat.level)
+ btreeIdxInfo->idxStat.level = level;
+
+ if (P_ISLEAF(opaque))
+ {
+ btreeIdxInfo->idxStat.numleafpages++;
+ btreeIdxInfo->idxStat.leaftuplesize+=BTMaxItemSize(page)-
+ PageGetFreeSpace(page);
+ btreeIdxInfo->idxStat.numleaftuple+=maxoff;
+ }
+ break;
+ }
+ case print:
+ {
+ while ( (btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt))
+ + level*4 + 128 >= btreeIdxInfo->idxInfo.len )
+ {
+ int dist=btreeIdxInfo->idxInfo.ptr-((char*)btreeIdxInfo->idxInfo.txt);
+ btreeIdxInfo->idxInfo.len *= 2;
+ btreeIdxInfo->idxInfo.txt=(text*)repalloc(btreeIdxInfo->idxInfo.txt,
+ btreeIdxInfo->idxInfo.len);
+ btreeIdxInfo->idxInfo.ptr = ((char*)btreeIdxInfo->idxInfo.txt)+dist;
+ }
+
+ sprintf(btreeIdxInfo->idxInfo.ptr, "lvl: %d, blk: %d, numTuples: %d\n",
+ level,
+ blk,
+ (int)maxoff);
+
+ btreeIdxInfo->idxInfo.ptr=strchr(btreeIdxInfo->idxInfo.ptr,'\0');
+ break;
+ }
+ }
+
+ if (!P_ISLEAF(opaque) && ((level < btreeIdxInfo->idxInfo.maxlevel)
+ ||(btreeIdxInfo->idxInfo.maxlevel<0)))
+ {
+ for (i = P_FIRSTDATAKEY(opaque); i <= maxoff; i = OffsetNumberNext(i))
+ {
+ iid = PageGetItemId(page, i);
+
+ if (!ItemIdIsValid(iid))
+ btreeIdxInfo->idxStat.numinvalidtuple++;
+
+ itup = (IndexTuple) PageGetItem(page, iid);
+ cblk = BTreeInnerTupleGetDownLink(itup);
+
+ btree_deep_search(rel, level + 1, cblk, btreeIdxInfo, cond);
+ }
+ }
+ UnlockReleaseBuffer(buffer);
+}
+
+/*
+ * Print some statistic about btree index
+ * This function shows information for live pages only
+ * and do not shows information about deleting pages
+ *
+ * SELECT btree_stat(INDEXNAME);
+ */
+PG_FUNCTION_INFO_V1(btree_stat);
+Datum btree_stat(PG_FUNCTION_ARGS);
+Datum
+btree_stat(PG_FUNCTION_ARGS)
+{
+ text *name=PG_GETARG_TEXT_PP(0);
+ RangeVar *relvar;
+ Relation index;
+ List *relname_list;
+ BtreeIdxInfo btreeIdxInfo;
+
+ Buffer metabuf;
+ Page metapg;
+ BTMetaPageData *metad;
+ BlockNumber rootBlk;
+
+ text *out=(text*)palloc(1024);
+ char *ptr=((char*)out)+VARHDRSZ;
+ relname_list = textToQualifiedNameList(name);
+ relvar = makeRangeVarFromNameList(relname_list);
+ index = btree_index_open(relvar);
+
+ memset(&btreeIdxInfo.idxStat, 0, sizeof(IdxStat));
+
+ /* Start dts from root */
+ metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
+ metapg = BufferGetPage(metabuf);
+ metad = BTPageGetMeta(metapg);
+ rootBlk = metad->btm_root;
+ UnlockReleaseBuffer(metabuf);
+
+ btree_deep_search(index, 0, rootBlk, &btreeIdxInfo,stat);
+
+ btree_index_close(index);
+
+ sprintf(ptr,
+ "Number of levels: %d\n"
+ "Number of pages: %d\n"
+ "Number of leaf pages: %d\n"
+ "Number of tuples: %d\n"
+ "Number of invalid tuples: %d\n"
+ "Number of leaf tuples: %d\n"
+ "Total size of tuples: "INT64_FORMAT" bytes\n"
+ "Total size of leaf tuples: "INT64_FORMAT" bytes\n"
+ "Total size of index: "INT64_FORMAT" bytes\n",
+ btreeIdxInfo.idxStat.level+1,
+ btreeIdxInfo.idxStat.numpages,
+ btreeIdxInfo.idxStat.numleafpages,
+ btreeIdxInfo.idxStat.numtuple,
+ btreeIdxInfo.idxStat.numinvalidtuple,
+ btreeIdxInfo.idxStat.numleaftuple,
+ btreeIdxInfo.idxStat.tuplesize,
+ btreeIdxInfo.idxStat.leaftuplesize,
+ btreeIdxInfo.idxStat.totalsize);
+
+ ptr=strchr(ptr,'\0');
+
+ SET_VARSIZE(out, ptr-((char*)out));
+ PG_RETURN_POINTER(out);
+}
+
+typedef struct BtPItem
+{
+ Buffer buffer;
+ Page page;
+ OffsetNumber offset;
+ int level;
+ struct BtPItem *next;
+} BtPItem;
+
+typedef struct
+{
+ List *relname_list;
+ RangeVar *relvar;
+ Relation index;
+ Datum *dvalues;
+ bool *nulls;
+ BtPItem *item;
+} BtTypeStorage;
+
+/*
+ * Open page in btree
+ * Returns nitem as a pointer to stack for btree levels stored.
+ * We process tuples from buffer in the top of nitem.
+ * After the complete processing of a top buffer
+ * we return to buffer on one level upper and go to next btree leaf.
+ */
+static BtPItem*
+openBtPPage( FuncCallContext *funcctx, BlockNumber blk )
+{
+ BtPItem *nitem;
+ MemoryContext oldcontext;
+
+ Relation index = ( (BtTypeStorage*)(funcctx->user_fctx) )->index;
+ BTPageOpaque opaque;
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ nitem = (BtPItem*)palloc( sizeof(BtPItem) );
+ memset(nitem,0,sizeof(BtPItem));
+
+ nitem->buffer = _bt_getbuf(index, blk, BT_READ);
+ Assert(BufferIsValid(nitem->buffer));
+ nitem->page = (Page) BufferGetPage(nitem->buffer);
+ opaque = (BTPageOpaque)PageGetSpecialPointer(nitem->page);
+ nitem->offset=P_FIRSTDATAKEY(opaque);
+ nitem->next = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
+ nitem->level = ( nitem->next ) ? nitem->next->level+1 : 1;
+ ( (BtTypeStorage*)(funcctx->user_fctx) )->item = nitem;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return nitem;
+}
+
+static BtPItem*
+closeBtPPage( FuncCallContext *funcctx )
+{
+ BtPItem *oitem = ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
+
+ ( (BtTypeStorage*)(funcctx->user_fctx) )->item = oitem->next;
+
+ UnlockReleaseBuffer(oitem->buffer);
+ pfree( oitem );
+ return ( (BtTypeStorage*)(funcctx->user_fctx) )->item;
+}
+
+static void
+btree_close_call( FuncCallContext *funcctx )
+{
+ BtTypeStorage *st = (BtTypeStorage*)(funcctx->user_fctx);
+
+ while(st->item && closeBtPPage(funcctx));
+
+ pfree(st->dvalues);
+ pfree(st->nulls);
+
+ btree_index_close(st->index);
+}
+
+/*
+ * Settings for first call of btree_print
+ * Sets the current memory context
+ */
+static void
+btree_setup_firstcall(FuncCallContext *funcctx, text *name)
+{
+ MemoryContext oldcontext;
+ BtTypeStorage *st;
+ TupleDesc tupdesc;
+ char attname[NAMEDATALEN];
+ int i;
+ BlockNumber blk;
+ Buffer buffer;
+
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ st=(BtTypeStorage*)palloc( sizeof(BtTypeStorage) );
+ memset(st,0,sizeof(BtTypeStorage));
+ st->relname_list = textToQualifiedNameList(name);
+ st->relvar = makeRangeVarFromNameList(st->relname_list);
+ st->index = btree_index_open(st->relvar);
+ st->item = NULL;
+ funcctx->user_fctx = (void*)st;
+
+ tupdesc = CreateTemplateTupleDesc(st->index->rd_att->natts+2);
+ TupleDescInitEntry(tupdesc, 1, "level", INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, 2, "valid", BOOLOID, -1, 0);
+ for (i = 0; i < st->index->rd_att->natts; i++)
+ {
+ sprintf(attname, "z%d", i+2);
+ TupleDescInitEntry(
+ tupdesc,
+ i+3,
+ attname,
+ TS_GET_TYPEVAL(st, i, atttypid),
+ TS_GET_TYPEVAL(st, i, atttypmod),
+ TS_GET_TYPEVAL(st, i, attndims) );
+ BlessTupleDesc(tupdesc);
+ }
+ BlessTupleDesc(tupdesc);
+
+
+ st->dvalues = (Datum *) palloc((tupdesc->natts+2) * sizeof(Datum));
+ st->nulls = palloc((tupdesc->natts+2) * sizeof(*st->nulls));
+
+ funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+
+ buffer = _bt_gettrueroot(st->index);
+ blk = BufferGetBlockNumber(buffer);
+ UnlockReleaseBuffer(buffer);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ st->item=openBtPPage(funcctx, blk);
+}
+
+/*
+ * Show index elements for btree from root to MAXLEVEL
+ * SELECT btree_tree(INDEXNAME[, MAXLEVEL]);
+ */
+PG_FUNCTION_INFO_V1(btree_tree);
+Datum btree_tree(PG_FUNCTION_ARGS);
+Datum
+btree_tree(PG_FUNCTION_ARGS)
+{
+ text *name=PG_GETARG_TEXT_PP(0);
+ RangeVar *relvar;
+ Relation index;
+ List *relname_list;
+ BtreeIdxInfo btreeIdxInfo;
+ Buffer metabuf;
+ Page metapg;
+ BTMetaPageData *metad;
+ BlockNumber rootBlk;
+
+ /*
+ * If we use MAXLEVEL is not used in SELECT btree_tree(INDEXNAME),
+ * info.maxlevel set to -1
+ * If MAXLEVEL is used in btree_tree call, set info.maxlevel = MAXLEVEL
+ */
+ btreeIdxInfo.idxInfo.maxlevel = ( PG_NARGS() > 1 ) ? PG_GETARG_INT32(1) : -1;
+ btreeIdxInfo.idxInfo.len=1024;
+ btreeIdxInfo.idxInfo.txt=(text*)palloc( btreeIdxInfo.idxInfo.len );
+ btreeIdxInfo.idxInfo.ptr=((char*)btreeIdxInfo.idxInfo.txt)+VARHDRSZ;
+
+ relname_list = textToQualifiedNameList(name);
+ relvar = makeRangeVarFromNameList(relname_list);
+ index = btree_index_open(relvar);
+
+ /* Start dts from root block */
+ metabuf = _bt_getbuf(index, BTREE_METAPAGE, BT_READ);
+ metapg = BufferGetPage(metabuf);
+ metad = BTPageGetMeta(metapg);
+ rootBlk = metad->btm_root;
+ UnlockReleaseBuffer(metabuf);
+
+ btree_deep_search(index, 0, rootBlk, &btreeIdxInfo, print);
+
+ btree_index_close(index);
+
+ btreeIdxInfo.idxInfo.ptr=strchr(btreeIdxInfo.idxInfo.ptr,'\0');
+
+ SET_VARSIZE(btreeIdxInfo.idxInfo.txt,
+ btreeIdxInfo.idxInfo.ptr-((char*)btreeIdxInfo.idxInfo.txt));
+ PG_RETURN_POINTER(btreeIdxInfo.idxInfo.txt);
+}
+
+/*
+ * Print objects stored in btree tuples
+ * works only if objects in index have textual representation
+ * select * from btree_print(INDEXNAME)
+ * as t(level int, valid bool, a box) where level =1;
+ */
+PG_FUNCTION_INFO_V1(btree_print);
+Datum btree_print(PG_FUNCTION_ARGS);
+Datum
+btree_print(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ BtTypeStorage *st;
+ ItemId iid;
+ IndexTuple ituple;
+ HeapTuple htuple;
+ int i;
+ bool isnull;
+ BTPageOpaque opaque;
+ Datum result=(Datum)0;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ text *name=PG_GETARG_TEXT_PP(0);
+ funcctx = SRF_FIRSTCALL_INIT();
+ btree_setup_firstcall(funcctx, name);
+ PG_FREE_IF_COPY(name,0);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ st = (BtTypeStorage*)(funcctx->user_fctx);
+
+ /*
+ * Looking through the stack of btree pages.
+ * If the item == NULL, then the search is over.
+ * For each btree page we look through all the tuples.
+ * If the offset is larger than the btree page size, the search is over.
+ */
+
+ if ( !st->item )
+ {
+ btree_close_call(funcctx);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* View all tuples on the page */
+ while( st->item->offset > PageGetMaxOffsetNumber( st->item->page ) )
+ {
+ if ( ! closeBtPPage(funcctx) ) {
+ btree_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] = ISNOTNULL;
+ opaque = (BTPageOpaque)PageGetSpecialPointer(st->item->page);
+ st->dvalues[1] = BoolGetDatum(P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)) ? false : true);
+ st->nulls[1] = ISNOTNULL;
+ for(i=2; i<funcctx->attinmeta->tupdesc->natts; i++)
+ {
+ if (!P_ISLEAF(opaque) && !ItemPointerIsValid(&(ituple->t_tid)))
+ {
+ st->dvalues[i] = (Datum)0;
+ st->nulls[i] = ISNULL;
+ }
+ else
+ {
+ st->dvalues[i] = index_getattr(ituple, i-1, st->index->rd_att, &isnull);
+ st->nulls[i] = ( isnull ) ? ISNULL : ISNOTNULL;
+ }
+ }
+
+ htuple = heap_formtuple(funcctx->attinmeta->tupdesc, st->dvalues, st->nulls);
+ result = TupleGetDatum(funcctx->slot, htuple);
+ result = HeapTupleGetDatum(htuple);
+ st->item->offset = OffsetNumberNext(st->item->offset);
+
+ if (!P_ISLEAF(opaque))
+ {
+ BlockNumber blk = BTreeInnerTupleGetDownLink(ituple);
+ openBtPPage(funcctx, blk );
+ }
+
+ SRF_RETURN_NEXT(funcctx, result);
+}
+
+/*
+ * Print some statistic about brin index
+ * SELECT brin_stat(INDEXNAME);
+ */
+PG_FUNCTION_INFO_V1(brin_stat);
+Datum brin_stat(PG_FUNCTION_ARGS);
+Datum
+brin_stat(PG_FUNCTION_ARGS)
+{
+ text *name=PG_GETARG_TEXT_PP(0);
+ RangeVar *relvar;
+ Relation index;
+ List *relname_list;
+
+ BlockNumber startBlk;
+ BlockNumber heapNumBlocks;
+ BlockNumber pagesPerRange;
+ BrinRevmap *revmap;
+ Relation heapRel;
+
+ Buffer buf;
+
+ uint32 numRevmapPages=0;
+ uint32 numTuples = 0;
+ uint32 numEmptyPages = 0;
+ uint32 numRegularPages = 0;
+ uint64 freeSpace = 0;
+ uint64 usedSpace = 0;
+
+ text *out=(text*)palloc(1024);
+ char *ptr=((char*)out)+VARHDRSZ;
+
+ relname_list = textToQualifiedNameList(name);
+ relvar = makeRangeVarFromNameList(relname_list);
+ index = brin_index_open(relvar);
+
+ revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
+ heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
+ AccessShareLock);
+ /* Determine range of pages to process */
+ heapNumBlocks = RelationGetNumberOfBlocks(heapRel); // total pages
+
+ table_close(heapRel, AccessShareLock);
+
+ /* The index is up to date, no update required */
+ startBlk = 0;
+
+ buf = InvalidBuffer;
+ /* Scan the revmap */
+ for (; startBlk < heapNumBlocks; startBlk += pagesPerRange)
+ {
+ BrinTuple *tup;
+ OffsetNumber off;
+ Page page;
+
+ numRevmapPages++;
+ /* Get on-disk tuple */
+ tup = brinGetTupleForHeapBlock(revmap, startBlk, &buf, &off, NULL,
+ BUFFER_LOCK_SHARE, NULL);
+
+ if (tup)
+ {
+ BrinDesc* bdesc;
+
+ numTuples++;
+ page = BufferGetPage(buf);
+
+ if (BRIN_IS_REGULAR_PAGE(page))
+ numRegularPages++;
+ freeSpace += PageGetFreeSpace(page);
+ bdesc = brin_build_desc(index);
+ usedSpace += MAXALIGN(sizeof(BrinMemTuple) +
+ sizeof(BrinValues) * bdesc->bd_tupdesc->natts);
+ }
+ else
+ /* If the revmap page points to void */
+ numEmptyPages++;
+
+ UnlockReleaseBuffer(buf);
+ }
+
+ brinRevmapTerminate(revmap);
+
+ sprintf(ptr,
+ "Number of revmap pages: %d\n"
+ "Number of empty revmap pages: %d\n"
+ "Number of regular pages: %d\n"
+ "Number of tuples: %d\n"
+ "Used space "INT64_FORMAT" bytes\n"
+ "Free space "INT64_FORMAT" bytes\n",
+ numRevmapPages,
+ numEmptyPages,
+ numRegularPages,
+ numTuples,
+ usedSpace,
+ freeSpace
+ );
+
+ ptr=strchr(ptr,'\0');
+
+ brin_index_close(index);
+ SET_VARSIZE(out, ptr-((char*)out));
+ PG_RETURN_POINTER(out);
+}
+
+/*
+ * Print numbers of heap blocks from revmap
+ * and numbers of end blocks from ranges
+ */
+PG_FUNCTION_INFO_V1(brin_print);
+Datum brin_print(PG_FUNCTION_ARGS);
+Datum
+brin_print(PG_FUNCTION_ARGS)
+{
+ text *name=PG_GETARG_TEXT_PP(0);
+ RangeVar *relvar;
+ Relation index;
+ List *relname_list;
+
+ BlockNumber heapBlk;
+ BlockNumber numBlocks;
+ BlockNumber pagesPerRange;
+ BrinRevmap *revmap;
+ Relation heapRel;
+ int len = 1024;
+
+ text *out=(text*)palloc(len);
+ char *ptr=((char*)out)+VARHDRSZ;
+
+ relname_list = textToQualifiedNameList(name);
+ relvar = makeRangeVarFromNameList(relname_list);
+ index = brin_index_open(relvar);
+
+ heapRel = table_open(IndexGetRelation(RelationGetRelid(index), false),
+ AccessShareLock);
+
+ /* Determine range of pages to process */
+ numBlocks = RelationGetNumberOfBlocks(heapRel);
+ table_close(heapRel, AccessShareLock);
+ revmap = brinRevmapInitialize(index, &pagesPerRange, NULL);
+
+ for(heapBlk = 0; heapBlk < numBlocks; heapBlk += pagesPerRange)
+ {
+ BlockNumber rangeEndBlk;
+ BlockNumber rangeBlk;
+
+ while ( (ptr-((char*)out)) + heapBlk*4 + 128 >= len )
+ {
+ int dist=ptr-((char*)out);
+ len *= 2;
+ out=(text*)repalloc(out, len);
+ ptr = ((char*)out)+dist;
+ Assert(0);
+ }
+
+ /* Compute rage end */
+ if (heapBlk + pagesPerRange > numBlocks)
+ rangeEndBlk = Min(RelationGetNumberOfBlocks(heapRel) - heapBlk,
+ pagesPerRange);
+ else
+ /* Easy case: range is known to be complete */
+ rangeEndBlk = pagesPerRange;
+ sprintf(ptr, "Start block: %d; end block: %d \n", heapBlk, rangeEndBlk);
+
+ for (rangeBlk = 0; rangeBlk < rangeEndBlk; rangeBlk++)
+ {
+ Page page;
+ /* Read buffer for open table */
+ Buffer buf = ReadBuffer(heapRel, rangeBlk);
+ page = BufferGetPage(buf);
+ sprintf(ptr, "Start block: %d; end block: %d; offset: %d, free: %d\n",
+ heapBlk,
+ rangeEndBlk,
+ (int) PageGetMaxOffsetNumber(page),
+ (int) PageGetFreeSpace(page));
+
+ ReleaseBuffer(buf);
+ }
+
+ ptr=strchr(ptr,'\0');
+ }
+
+ brinRevmapTerminate(revmap);
+
+ brin_index_close(index);
+ ptr=strchr(ptr,'\0');
+ SET_VARSIZE(out, ptr-((char*)out));
+ PG_RETURN_POINTER(out);
+}
+#endif