Commit 0ad7db4b authored by Marc G. Fournier's avatar Marc G. Fournier

New feature:
   1. Support of variable size keys - new algorithm of insertion to tree
      (GLI - gist layrered insertion). Previous algorithm was implemented
      as described in paper by Joseph M. Hellerstein et.al
      "Generalized Search Trees for Database Systems".  This (old)
      algorithm was not suitable for variable size keys and could be
      not effective ( walking up-down ) in case of multiple levels split
Bug fixed:
   1. fixed bug in gistPageAddItem - key values were written to disk
      uncompressed. This caused failure if decompression function
      does real job.
   2. NULLs handling - we keep NULLs in tree. Right way is to remove them,
      but we don't know how to inform vacuum about index statistics. This is
      just cosmetic warning message (like in case with R-Tree),
      but I'm not sure how to recognize real problem if we remove NULLs
      and suppress this warning as Tom suggested.
   3. various memory leaks

This work was done by Teodor Sigaev (teodor@stack.net) and
Oleg Bartunov (oleg@sai.msu.su).
parent 7cd97118
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.67 2000/11/30 08:46:20 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.68 2001/01/12 00:12:58 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -25,37 +25,61 @@ ...@@ -25,37 +25,61 @@
#include "access/xlogutils.h" #include "access/xlogutils.h"
/* result's status */
#define INSERTED 0x01
#define SPLITED 0x02
/* non-export function prototypes */ /* non-export function prototypes */
static InsertIndexResult gistdoinsert(Relation r, IndexTuple itup, static void gistdoinsert(Relation r,
GISTSTATE *GISTstate); IndexTuple itup,
static InsertIndexResult gistentryinsert(Relation r, GISTSTACK *stk, InsertIndexResult *res,
IndexTuple tup, GISTSTATE *GISTstate);
GISTSTATE *giststate); static int gistlayerinsert( Relation r, BlockNumber blkno,
static void gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup, IndexTuple **itup,
IndexTuple rtup, GISTSTATE *giststate); int *len,
static void gistAdjustKeys(Relation r, GISTSTACK *stk, BlockNumber blk, InsertIndexResult *res,
char *datum, int att_size, GISTSTATE *giststate); GISTSTATE *giststate );
static void gistintinsert(Relation r, GISTSTACK *stk, IndexTuple ltup, static OffsetNumber gistwritebuffer( Relation r,
IndexTuple rtup, GISTSTATE *giststate); Page page,
static InsertIndexResult gistSplit(Relation r, Buffer buffer, IndexTuple *itup,
GISTSTACK *stack, IndexTuple itup, int len,
GISTSTATE *giststate); OffsetNumber off,
static void gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, GISTSTATE *giststate );
IndexTuple rt); static int gistnospace( Page page,
IndexTuple *itvec, int len );
static IndexTuple * gistreadbuffer( Relation r,
Buffer buffer, int *len );
static IndexTuple * gistjoinvector(
IndexTuple *itvec, int *len,
IndexTuple *additvec, int addlen );
static IndexTuple gistunion( Relation r, IndexTuple *itvec,
int len, GISTSTATE *giststate );
static IndexTuple gistgetadjusted( Relation r,
IndexTuple oldtup,
IndexTuple addtup,
GISTSTATE *giststate );
static IndexTuple * gistSplit(Relation r,
Buffer buffer,
IndexTuple *itup,
int *len,
GISTSTATE *giststate,
InsertIndexResult *res);
static void gistnewroot(GISTSTATE *giststate, Relation r,
IndexTuple *itup, int len);
static void GISTInitBuffer(Buffer b, uint32 f); static void GISTInitBuffer(Buffer b, uint32 f);
static BlockNumber gistChooseSubtree(Relation r, IndexTuple itup, int level, static OffsetNumber gistchoose(Relation r, Page p,
GISTSTATE *giststate, IndexTuple it,
GISTSTACK **retstack, Buffer *leafbuf); GISTSTATE *giststate);
static OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, static IndexTuple gist_tuple_replacekey(Relation r,
GISTSTATE *giststate); GISTENTRY entry, IndexTuple t);
static int gistnospace(Page p, IndexTuple it); static void gistcentryinit(GISTSTATE *giststate,
static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t); GISTENTRY *e, char *pr,
static void gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, Page pg,
Relation r, Page pg, OffsetNumber o, int b, bool l); OffsetNumber o, int b, bool l);
#undef GISTDEBUG
#ifdef GISTDEBUG #ifdef GISTDEBUG
static char *int_range_out(INTRANGE *r); static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff);
#endif #endif
/* /*
...@@ -86,7 +110,6 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -86,7 +110,6 @@ gistbuild(PG_FUNCTION_ARGS)
TupleTableSlot *slot; TupleTableSlot *slot;
#endif #endif
ExprContext *econtext; ExprContext *econtext;
InsertIndexResult res = NULL;
GISTSTATE giststate; GISTSTATE giststate;
GISTENTRY tmpcentry; GISTENTRY tmpcentry;
Buffer buffer = InvalidBuffer; Buffer buffer = InvalidBuffer;
...@@ -223,14 +246,13 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -223,14 +246,13 @@ gistbuild(PG_FUNCTION_ARGS)
* not when you're initializing the whole index at once. * not when you're initializing the whole index at once.
*/ */
res = gistdoinsert(index, itup, &giststate); gistdoinsert(index, itup, NULL, &giststate);
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
if (compvec[i]) if (compvec[i])
pfree(DatumGetPointer(attdata[i])); pfree(DatumGetPointer(attdata[i]));
pfree(itup); pfree(itup);
pfree(res);
} }
/* okay, all heap tuples are indexed */ /* okay, all heap tuples are indexed */
...@@ -274,6 +296,10 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -274,6 +296,10 @@ gistbuild(PG_FUNCTION_ARGS)
} }
} }
#ifdef GISTDEBUG
gist_dumptree(index, 0, GISTP_ROOT, 0);
#endif
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -324,7 +350,8 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -324,7 +350,8 @@ gistinsert(PG_FUNCTION_ARGS)
* RelationSetLockForWrite(r); * RelationSetLockForWrite(r);
*/ */
res = gistdoinsert(r, itup, &giststate); res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
gistdoinsert(r, itup, &res, &giststate);
for (i = 0; i < r->rd_att->natts; i++) for (i = 0; i < r->rd_att->natts; i++)
if (compvec[i] == TRUE) if (compvec[i] == TRUE)
pfree((char *) datum[i]); pfree((char *) datum[i]);
...@@ -353,6 +380,7 @@ gistPageAddItem(GISTSTATE *giststate, ...@@ -353,6 +380,7 @@ gistPageAddItem(GISTSTATE *giststate,
{ {
GISTENTRY tmpcentry; GISTENTRY tmpcentry;
IndexTuple itup = (IndexTuple) item; IndexTuple itup = (IndexTuple) item;
OffsetNumber retval;
/* /*
* recompress the item given that we now know the exact page and * recompress the item given that we now know the exact page and
...@@ -364,295 +392,353 @@ gistPageAddItem(GISTSTATE *giststate, ...@@ -364,295 +392,353 @@ gistPageAddItem(GISTSTATE *giststate,
IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE); IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE);
gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page, gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page,
offsetNumber, dentry->bytes, FALSE); offsetNumber, dentry->bytes, FALSE);
*newtup = gist_tuple_replacekey(r, *dentry, itup); *newtup = gist_tuple_replacekey(r, tmpcentry, itup);
retval = PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup),
offsetNumber, flags);
/* be tidy */ /* be tidy */
if (tmpcentry.pred != dentry->pred if (tmpcentry.pred && tmpcentry.pred != dentry->pred
&& tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData))) && tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
pfree(tmpcentry.pred); pfree(tmpcentry.pred);
return (retval);
return (PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup),
offsetNumber, flags));
} }
static void
gistdoinsert( Relation r,
IndexTuple itup,
InsertIndexResult *res,
GISTSTATE *giststate ) {
IndexTuple *instup;
int i,ret,len = 1;
instup = ( IndexTuple* ) palloc( sizeof(IndexTuple) );
instup[0] = ( IndexTuple ) palloc( IndexTupleSize( itup ) );
memcpy( instup[0], itup, IndexTupleSize( itup ) );
ret = gistlayerinsert(r, GISTP_ROOT, &instup, &len, res, giststate);
if ( ret & SPLITED )
gistnewroot( giststate, r, instup, len );
static InsertIndexResult for(i=0;i<len;i++)
gistdoinsert(Relation r, pfree( instup[i] );
IndexTuple itup, /* itup contains compressed entry */ pfree( instup );
GISTSTATE *giststate) }
{
GISTENTRY tmpdentry;
InsertIndexResult res;
OffsetNumber l;
GISTSTACK *stack;
Buffer buffer;
BlockNumber blk;
Page page;
OffsetNumber off;
IndexTuple newtup;
/* 3rd arg is ignored for now */ static int
blk = gistChooseSubtree(r, itup, 0, giststate, &stack, &buffer); gistlayerinsert( Relation r, BlockNumber blkno,
page = (Page) BufferGetPage(buffer); IndexTuple **itup, /* in - out, has compressed entry */
int *len , /* in - out */
InsertIndexResult *res, /* out */
GISTSTATE *giststate ) {
Buffer buffer;
Page page;
OffsetNumber child;
int ret;
GISTPageOpaque opaque;
if (gistnospace(page, itup)) buffer = ReadBuffer(r, blkno);
{ page = (Page) BufferGetPage(buffer);
/* need to do a split */ opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
res = gistSplit(r, buffer, stack, itup, giststate);
gistfreestack(stack);
WriteBuffer(buffer); /* don't forget to release buffer! */
return res;
}
if (PageIsEmpty(page)) if (!(opaque->flags & F_LEAF)) {
off = FirstOffsetNumber; /* internal page, so we must walk on tree */
else /* len IS equial 1 */
off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); ItemId iid;
BlockNumber nblkno;
ItemPointerData oldtid;
IndexTuple oldtup;
child = gistchoose( r, page, *(*itup), giststate );
iid = PageGetItemId(page, child);
oldtup = (IndexTuple) PageGetItem(page, iid);
nblkno = ItemPointerGetBlockNumber(&(oldtup->t_tid));
/*
* After this call:
* 1. if child page was splited, then itup contains
* keys for each page
* 2. if child page wasn't splited, then itup contains
* additional for adjustement of current key
*/
ret = gistlayerinsert( r, nblkno, itup, len, res, giststate );
/* add the item and write the buffer */ /* nothing inserted in child */
l = gistPageAddItem(giststate, r, page, (Item) itup, IndexTupleSize(itup), if ( ! (ret & INSERTED) ) {
off, LP_USED, &tmpdentry, &newtup); ReleaseBuffer(buffer);
WriteBuffer(buffer); return 0x00;
}
/* now expand the page boundary in the parent to include the new child */ /* child does not splited */
gistAdjustKeys(r, stack, blk, tmpdentry.pred, tmpdentry.bytes, giststate); if ( ! (ret & SPLITED) ) {
gistfreestack(stack); IndexTuple newtup = gistgetadjusted( r, oldtup, (*itup)[0], giststate );
if ( ! newtup ) {
/* not need to update key */
ReleaseBuffer(buffer);
return 0x00;
}
/* be tidy */ pfree( (*itup)[0] ); /* !!! */
if (itup != newtup) (*itup)[0] = newtup;
pfree(newtup); }
if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
pfree(tmpdentry.pred);
/* build and return an InsertIndexResult for this insertion */ /* key is modified, so old version must be deleted */
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); ItemPointerSet(&oldtid, blkno, child);
ItemPointerSet(&(res->pointerData), blk, l); DirectFunctionCall2(gistdelete,
PointerGetDatum(r),
PointerGetDatum(&oldtid));
}
return res; ret = INSERTED;
}
if ( gistnospace(page, (*itup), *len) ) {
/* no space for insertion */
IndexTuple *itvec;
int tlen;
ret |= SPLITED;
itvec = gistreadbuffer( r, buffer, &tlen );
itvec = gistjoinvector( itvec, &tlen, (*itup), *len );
pfree( (*itup) );
(*itup) = gistSplit( r, buffer, itvec, &tlen, giststate,
(opaque->flags & F_LEAF) ? res : NULL ); /*res only for inserting in leaf*/
ReleaseBuffer( buffer );
pfree( itvec );
*len = tlen; /* now tlen >= 2 */
} else {
/* enogth space */
OffsetNumber off, l;
off = ( PageIsEmpty(page) ) ?
FirstOffsetNumber
:
OffsetNumberNext(PageGetMaxOffsetNumber(page));
l = gistwritebuffer( r, page, (*itup), *len, off, giststate );
WriteBuffer(buffer);
/* set res if insert into leaf page, in
this case, len = 1 always */
if ( res && (opaque->flags & F_LEAF) )
ItemPointerSet(&((*res)->pointerData), blkno, l);
if ( *len > 1 ) { /* previos insert ret & SPLITED != 0 */
int i;
/* child was splited, so we must form union
* for insertion in parent */
IndexTuple newtup = gistunion(r, (*itup), *len, giststate);
for(i=0; i<*len; i++)
pfree( (*itup)[i] );
(*itup)[0] = newtup;
*len = 1;
}
}
return ret;
}
static BlockNumber /*
gistChooseSubtree(Relation r, IndexTuple itup, /* itup has compressed * Write itup vector to page, has no control of free space
* entry */ */
int level, static OffsetNumber
GISTSTATE *giststate, gistwritebuffer( Relation r, Page page, IndexTuple *itup,
GISTSTACK **retstack /* out */ , int len, OffsetNumber off, GISTSTATE *giststate) {
Buffer *leafbuf /* out */ ) OffsetNumber l = InvalidOffsetNumber;
{ int i;
Buffer buffer; GISTENTRY tmpdentry;
BlockNumber blk; IndexTuple newtup;
GISTSTACK *stack;
Page page; for(i=0; i<len; i++) {
GISTPageOpaque opaque; l = gistPageAddItem(giststate, r, page,
IndexTuple which; (Item) itup[i], IndexTupleSize(itup[i]),
off, LP_USED, &tmpdentry, &newtup);
off = OffsetNumberNext( off );
if (tmpdentry.pred != (((char *) itup[i]) + sizeof(IndexTupleData)) && tmpdentry.pred)
pfree(tmpdentry.pred);
if (itup[i] != newtup)
pfree(newtup);
}
return l;
}
blk = GISTP_ROOT; /*
buffer = InvalidBuffer; * Check space for itup vector on page
stack = (GISTSTACK *) NULL; */
static int
gistnospace( Page page, IndexTuple *itvec, int len ) {
int size = 0;
int i;
for(i=0; i<len; i++)
size += IndexTupleSize( itvec[i] )+4; /* ??? */
do return (PageGetFreeSpace(page) < size);
{ }
/* let go of current buffer before getting next */
if (buffer != InvalidBuffer)
ReleaseBuffer(buffer);
/* get next buffer */ /*
buffer = ReadBuffer(r, blk); * Read buffer into itup vector
page = (Page) BufferGetPage(buffer); */
static IndexTuple *
gistreadbuffer( Relation r, Buffer buffer, int *len /*out*/) {
OffsetNumber i, maxoff;
IndexTuple *itvec;
Page p = (Page) BufferGetPage(buffer);
opaque = (GISTPageOpaque) PageGetSpecialPointer(page); *len=0;
if (!(opaque->flags & F_LEAF)) maxoff = PageGetMaxOffsetNumber(p);
{ itvec = palloc( sizeof(IndexTuple) * maxoff );
GISTSTACK *n; for(i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
ItemId iid; itvec[ (*len)++ ] = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
n = (GISTSTACK *) palloc(sizeof(GISTSTACK));
n->gs_parent = stack;
n->gs_blk = blk;
n->gs_child = gistchoose(r, page, itup, giststate);
stack = n;
iid = PageGetItemId(page, n->gs_child);
which = (IndexTuple) PageGetItem(page, iid);
blk = ItemPointerGetBlockNumber(&(which->t_tid));
}
} while (!(opaque->flags & F_LEAF));
*retstack = stack; return itvec;
*leafbuf = buffer; }
return blk; /*
* join two vectors into one
*/
static IndexTuple *
gistjoinvector( IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen ) {
itvec = (IndexTuple*) repalloc( (void*)itvec, sizeof(IndexTuple) * ( (*len) + addlen ) );
memmove( &itvec[*len], additvec, sizeof(IndexTuple) * addlen );
*len += addlen;
return itvec;
} }
/*
* return union of itup vector
*/
static IndexTuple
gistunion( Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate ) {
bytea *evec;
char *datum;
int datumsize, i;
GISTENTRY centry;
char isnull;
IndexTuple newtup;
evec = (bytea *) palloc(len * sizeof(GISTENTRY) + VARHDRSZ);
VARATT_SIZEP(evec) = len * sizeof(GISTENTRY) + VARHDRSZ;
for ( i = 0 ; i< len ; i++ )
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[i],
(char*) itvec[i] + sizeof(IndexTupleData),
(Relation)NULL, (Page)NULL, (OffsetNumber)NULL,
IndexTupleSize((IndexTuple)itvec[i]) - sizeof(IndexTupleData), FALSE);
static void datum = (char *)
gistAdjustKeys(Relation r, DatumGetPointer(FunctionCall2(&giststate->unionFn,
GISTSTACK *stk, PointerGetDatum(evec),
BlockNumber blk, PointerGetDatum(&datumsize)));
char *datum, /* datum is uncompressed */
int att_size,
GISTSTATE *giststate)
{
char *oldud;
Page p;
Buffer b;
bool result;
bytea *evec;
GISTENTRY centry,
*ev0p,
*ev1p;
int size,
datumsize;
IndexTuple tid;
if (stk == (GISTSTACK *) NULL) for ( i = 0 ; i< len ; i++ )
return; if ( ((GISTENTRY *) VARDATA(evec))[i].pred &&
((GISTENTRY *) VARDATA(evec))[i].pred !=
((char*)( itvec[i] )+ sizeof(IndexTupleData)) )
pfree( ((GISTENTRY *) VARDATA(evec))[i].pred );
pfree( evec );
b = ReadBuffer(r, stk->gs_blk); gistcentryinit(giststate, &centry, datum,
p = BufferGetPage(b); (Relation)NULL, (Page)NULL, (OffsetNumber)NULL,
datumsize, FALSE);
isnull = (centry.pred) ? ' ' : 'n';
newtup = (IndexTuple) index_formtuple( r->rd_att, (Datum *) &centry.pred, &isnull );
if (centry.pred != datum)
pfree( datum );
oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->gs_child)); return newtup;
tid = (IndexTuple) oldud; }
size = IndexTupleSize((IndexTuple) oldud) - sizeof(IndexTupleData);
oldud += sizeof(IndexTupleData);
/*
* Forms union of oldtup and addtup, if union == oldtup then return NULL
*/
static IndexTuple
gistgetadjusted( Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate ) {
bytea *evec;
char *datum;
int datumsize;
bool result;
char isnull;
GISTENTRY centry, *ev0p, *ev1p;
IndexTuple newtup = NULL;
evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ); evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ);
VARATT_SIZEP(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ; VARATT_SIZEP(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ;
/* insert decompressed oldud into entry vector */
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0], gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0],
oldud, r, p, stk->gs_child, (char*) oldtup + sizeof(IndexTupleData), (Relation) NULL,
size, FALSE); (Page) NULL, (OffsetNumber) 0,
IndexTupleSize((IndexTuple)oldtup) - sizeof(IndexTupleData), FALSE);
ev0p = &((GISTENTRY *) VARDATA(evec))[0]; ev0p = &((GISTENTRY *) VARDATA(evec))[0];
/* insert datum entry into entry vector */ gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[1],
gistentryinit(((GISTENTRY *) VARDATA(evec))[1], datum, (char*) addtup + sizeof(IndexTupleData), (Relation) NULL,
(Relation) NULL, (Page) NULL, (OffsetNumber) 0, att_size, FALSE); (Page) NULL, (OffsetNumber) 0,
IndexTupleSize((IndexTuple)addtup) - sizeof(IndexTupleData), FALSE);
ev1p = &((GISTENTRY *) VARDATA(evec))[1]; ev1p = &((GISTENTRY *) VARDATA(evec))[1];
/* form union of decompressed entries */
datum = (char *) datum = (char *)
DatumGetPointer(FunctionCall2(&giststate->unionFn, DatumGetPointer(FunctionCall2(&giststate->unionFn,
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize))); PointerGetDatum(&datumsize)));
/* did union leave decompressed version of oldud unchanged? */ if ( ! ( ev0p->pred && ev1p->pred ) ) {
FunctionCall3(&giststate->equalFn, result = ( ev0p->pred == NULL && ev1p->pred == NULL );
PointerGetDatum(ev0p->pred), } else {
PointerGetDatum(datum), FunctionCall3(&giststate->equalFn,
PointerGetDatum(&result)); PointerGetDatum(ev0p->pred),
if (!result) PointerGetDatum(datum),
{ PointerGetDatum(&result));
TupleDesc td = RelationGetDescr(r); }
/* compress datum for storage on page */ if ( result ) {
/* not need to update key */
pfree( datum );
} else {
gistcentryinit(giststate, &centry, datum, ev0p->rel, ev0p->page, gistcentryinit(giststate, &centry, datum, ev0p->rel, ev0p->page,
ev0p->offset, datumsize, FALSE); ev0p->offset, datumsize, FALSE);
if (td->attrs[0]->attlen >= 0)
{
memmove(oldud, centry.pred, att_size);
gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
giststate);
}
else if (VARSIZE(centry.pred) == VARSIZE(oldud))
{
memmove(oldud, centry.pred, VARSIZE(centry.pred));
gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
giststate);
}
else
{
/*
* * new datum is not the same size as the old. * We have to
* delete the old entry and insert the new * one. Note that
* this may cause a split here!
*/
IndexTuple newtup;
ItemPointerData oldtid;
char *isnull;
TupleDesc tupDesc;
InsertIndexResult res;
/* delete old tuple */
ItemPointerSet(&oldtid, stk->gs_blk, stk->gs_child);
DirectFunctionCall2(gistdelete,
PointerGetDatum(r),
PointerGetDatum(&oldtid));
/* generate and insert new tuple */
tupDesc = r->rd_att;
isnull = (char *) palloc(r->rd_rel->relnatts);
MemSet(isnull, ' ', r->rd_rel->relnatts);
newtup = (IndexTuple) index_formtuple(tupDesc,
(Datum *) &centry.pred, isnull);
pfree(isnull);
/* set pointer in new tuple to point to current child */
ItemPointerSet(&oldtid, blk, 1);
newtup->t_tid = oldtid;
/* inserting the new entry also adjust keys above */
res = gistentryinsert(r, stk, newtup, giststate);
/* in stack, set info to point to new tuple */
stk->gs_blk = ItemPointerGetBlockNumber(&(res->pointerData));
stk->gs_child = ItemPointerGetOffsetNumber(&(res->pointerData));
pfree(res);
}
WriteBuffer(b);
isnull = (centry.pred) ? ' ' : 'n';
newtup = (IndexTuple) index_formtuple( r->rd_att, (Datum *) &centry.pred, &isnull );
newtup->t_tid = oldtup->t_tid;
if (centry.pred != datum) if (centry.pred != datum)
pfree(datum); pfree( datum );
} }
else
ReleaseBuffer(b);
pfree(evec);
}
if ( ev0p->pred &&
ev0p->pred != (char*) oldtup + sizeof(IndexTupleData) )
pfree( ev0p->pred );
if ( ev1p->pred &&
ev1p->pred != (char*) addtup + sizeof(IndexTupleData) )
pfree( ev1p->pred );
pfree( evec );
return newtup;
}
/* /*
* gistSplit -- split a page in the tree. * gistSplit -- split a page in the tree.
*
*/ */
static InsertIndexResult static IndexTuple *
gistSplit(Relation r, gistSplit(Relation r,
Buffer buffer, Buffer buffer,
GISTSTACK *stack, IndexTuple *itup, /* contains compressed entry */
IndexTuple itup, /* contains compressed entry */ int *len,
GISTSTATE *giststate) GISTSTATE *giststate,
InsertIndexResult *res)
{ {
Page p; Page p;
Buffer leftbuf, Buffer leftbuf, rightbuf;
rightbuf; Page left, right;
Page left, OffsetNumber *spl_left, *spl_right;
right; IndexTuple *lvectup, *rvectup, *newtup;
ItemId itemid; int leftoff, rightoff;
IndexTuple item; BlockNumber lbknum, rbknum;
IndexTuple ltup,
rtup,
newtup;
OffsetNumber maxoff;
OffsetNumber i;
OffsetNumber leftoff,
rightoff;
BlockNumber lbknum,
rbknum;
BlockNumber bufblock;
GISTPageOpaque opaque; GISTPageOpaque opaque;
int blank; char isnull;
InsertIndexResult res;
char *isnull;
GIST_SPLITVEC v; GIST_SPLITVEC v;
TupleDesc tupDesc;
bytea *entryvec; bytea *entryvec;
bool *decompvec; bool *decompvec;
IndexTuple item_1; GISTENTRY tmpentry;
GISTENTRY tmpdentry, int i, nlen;
tmpentry;
isnull = (char *) palloc(r->rd_rel->relnatts);
for (blank = 0; blank < r->rd_rel->relnatts; blank++)
isnull[blank] = ' ';
p = (Page) BufferGetPage(buffer); p = (Page) BufferGetPage(buffer);
opaque = (GISTPageOpaque) PageGetSpecialPointer(p); opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
...@@ -684,313 +770,138 @@ gistSplit(Relation r, ...@@ -684,313 +770,138 @@ gistSplit(Relation r,
right = (Page) BufferGetPage(rightbuf); right = (Page) BufferGetPage(rightbuf);
/* generate the item array */ /* generate the item array */
maxoff = PageGetMaxOffsetNumber(p); entryvec = (bytea *) palloc(VARHDRSZ + (*len+1) * sizeof(GISTENTRY));
entryvec = (bytea *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(GISTENTRY)); decompvec = (bool *) palloc(VARHDRSZ + (*len+1) * sizeof(bool));
decompvec = (bool *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(bool)); VARATT_SIZEP(entryvec) = (*len+1) * sizeof(GISTENTRY) + VARHDRSZ;
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) for (i = 1; i <= *len; i++)
{ {
item_1 = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i], gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i],
(((char *) item_1) + sizeof(IndexTupleData)), (((char *) itup[i-1]) + sizeof(IndexTupleData)),
r, p, i, r, p, i,
IndexTupleSize(item_1) - sizeof(IndexTupleData), FALSE); IndexTupleSize(itup[i-1]) - sizeof(IndexTupleData), FALSE);
if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred) if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred)
== (((char *) item_1) + sizeof(IndexTupleData))) == (((char *) itup[i-1]) + sizeof(IndexTupleData)))
decompvec[i] = FALSE; decompvec[i] = FALSE;
else else
decompvec[i] = TRUE; decompvec[i] = TRUE;
} }
/* add the new datum as the last entry */
gistdentryinit(giststate, &(((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]),
(((char *) itup) + sizeof(IndexTupleData)),
(Relation) NULL, (Page) NULL,
(OffsetNumber) 0, tmpentry.bytes, FALSE);
if ((char *) (((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]).pred !=
(((char *) itup) + sizeof(IndexTupleData)))
decompvec[maxoff + 1] = TRUE;
else
decompvec[maxoff + 1] = FALSE;
VARATT_SIZEP(entryvec) = (maxoff + 2) * sizeof(GISTENTRY) + VARHDRSZ;
/* now let the user-defined picksplit function set up the split vector */ /* now let the user-defined picksplit function set up the split vector */
FunctionCall2(&giststate->picksplitFn, FunctionCall2(&giststate->picksplitFn,
PointerGetDatum(entryvec), PointerGetDatum(entryvec),
PointerGetDatum(&v)); PointerGetDatum(&v));
/* compress ldatum and rdatum */
gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL,
(Page) NULL, (OffsetNumber) 0,
((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
if (v.spl_ldatum != tmpentry.pred)
pfree(v.spl_ldatum);
v.spl_ldatum = tmpentry.pred;
gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL,
(Page) NULL, (OffsetNumber) 0,
((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
if (v.spl_rdatum != tmpentry.pred)
pfree(v.spl_rdatum);
v.spl_rdatum = tmpentry.pred;
/* clean up the entry vector: its preds need to be deleted, too */ /* clean up the entry vector: its preds need to be deleted, too */
for (i = FirstOffsetNumber; i <= maxoff + 1; i = OffsetNumberNext(i)) for (i = 1; i <= *len; i++)
if (decompvec[i]) if (decompvec[i] && ((GISTENTRY *) VARDATA(entryvec))[i].pred)
pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred); pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred);
pfree(entryvec); pfree(entryvec);
pfree(decompvec); pfree(decompvec);
leftoff = rightoff = FirstOffsetNumber; spl_left = v.spl_left; spl_right = v.spl_right;
maxoff = PageGetMaxOffsetNumber(p);
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) /* form left and right vector */
{ lvectup = (IndexTuple*) palloc( sizeof( IndexTuple )*v.spl_nleft );
itemid = PageGetItemId(p, i); rvectup = (IndexTuple*) palloc( sizeof( IndexTuple )*v.spl_nright );
item = (IndexTuple) PageGetItem(p, itemid); leftoff = rightoff = 0;
for( i=1; i <= *len; i++ ) {
if (i == *(v.spl_left)) if (i == *(spl_left) || ( i==*len && *(spl_left) != FirstOffsetNumber ) ) {
{ lvectup[ leftoff++ ] = itup[ i-1 ];
gistPageAddItem(giststate, r, left, (Item) item, spl_left++;
IndexTupleSize(item), } else {
leftoff, LP_USED, &tmpdentry, &newtup); rvectup[ rightoff++ ] = itup[ i-1 ];
leftoff = OffsetNumberNext(leftoff); spl_right++;
v.spl_left++; /* advance in left split vector */
/* be tidy */
if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
pfree(tmpdentry.pred);
if ((IndexTuple) item != newtup)
pfree(newtup);
}
else
{
gistPageAddItem(giststate, r, right, (Item) item,
IndexTupleSize(item),
rightoff, LP_USED, &tmpdentry, &newtup);
rightoff = OffsetNumberNext(rightoff);
v.spl_right++; /* advance in right split vector */
/* be tidy */
if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
pfree(tmpdentry.pred);
if (item != newtup)
pfree(newtup);
} }
} }
/* build an InsertIndexResult for this insertion */ /* write on disk (may be need another split) */
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); if ( gistnospace(right, rvectup, v.spl_nright) ) {
nlen = v.spl_nright;
/* now insert the new index tuple */ newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate,
if (*(v.spl_left) != FirstOffsetNumber) ( res && rvectup[ nlen-1 ] == itup[ *len - 1 ] ) ? res : NULL );
{ ReleaseBuffer( rightbuf );
gistPageAddItem(giststate, r, left, (Item) itup, } else {
IndexTupleSize(itup), OffsetNumber l;
leftoff, LP_USED, &tmpdentry, &newtup);
leftoff = OffsetNumberNext(leftoff); l = gistwritebuffer( r, right, rvectup, v.spl_nright, FirstOffsetNumber, giststate );
ItemPointerSet(&(res->pointerData), lbknum, leftoff); WriteBuffer(rightbuf);
/* be tidy */
if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) if ( res )
pfree(tmpdentry.pred); ItemPointerSet(&((*res)->pointerData), rbknum, l);
if (itup != newtup) gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL,
pfree(newtup); (Page) NULL, (OffsetNumber) 0,
} -1, FALSE);
else if (v.spl_rdatum != tmpentry.pred)
{ pfree(v.spl_rdatum);
gistPageAddItem(giststate, r, right, (Item) itup, v.spl_rdatum = tmpentry.pred;
IndexTupleSize(itup),
rightoff, LP_USED, &tmpdentry, &newtup); nlen = 1;
rightoff = OffsetNumberNext(rightoff); newtup = (IndexTuple*) palloc( sizeof(IndexTuple) * 1);
ItemPointerSet(&(res->pointerData), rbknum, rightoff); isnull = ( v.spl_rdatum ) ? ' ' : 'n';
/* be tidy */ newtup[0] = (IndexTuple) index_formtuple(r->rd_att, (Datum *) &(v.spl_rdatum), &isnull);
if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1);
pfree(tmpdentry.pred);
if (itup != newtup)
pfree(newtup);
} }
if ((bufblock = BufferGetBlockNumber(buffer)) != GISTP_ROOT) if ( gistnospace(left, lvectup, v.spl_nleft) ) {
PageRestoreTempPage(left, p); int llen = v.spl_nleft;
WriteBuffer(leftbuf); IndexTuple *lntup;
WriteBuffer(rightbuf);
lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate,
/* ( res && lvectup[ llen-1 ] == itup[ *len - 1 ] ) ? res : NULL );
* Okay, the page is split. We have three things left to do: ReleaseBuffer( leftbuf );
*
* 1) Adjust any active scans on this index to cope with changes we newtup = gistjoinvector( newtup, &nlen, lntup, llen );
* introduced in its structure by splitting this page. pfree( lntup );
* } else {
* 2) "Tighten" the bounding box of the pointer to the left page in the OffsetNumber l;
* parent node in the tree, if any. Since we moved a bunch of stuff
* off the left page, we expect it to get smaller. This happens in l = gistwritebuffer( r, left, lvectup, v.spl_nleft, FirstOffsetNumber, giststate );
* the internal insertion routine. if ( BufferGetBlockNumber(buffer) != GISTP_ROOT)
* PageRestoreTempPage(left, p);
* 3) Insert a pointer to the right page in the parent. This may cause
* the parent to split. If it does, we need to repeat steps one and WriteBuffer(leftbuf);
* two for each split node in the tree.
*/ if ( res )
ItemPointerSet(&((*res)->pointerData), lbknum, l);
/* adjust active scans */ gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL,
gistadjscans(r, GISTOP_SPLIT, bufblock, FirstOffsetNumber); (Page) NULL, (OffsetNumber) 0,
-1, FALSE);
tupDesc = r->rd_att; if (v.spl_ldatum != tmpentry.pred)
pfree(v.spl_ldatum);
ltup = (IndexTuple) index_formtuple(tupDesc, v.spl_ldatum = tmpentry.pred;
(Datum *) &(v.spl_ldatum), isnull);
rtup = (IndexTuple) index_formtuple(tupDesc, nlen += 1;
(Datum *) &(v.spl_rdatum), isnull); newtup = (IndexTuple*) repalloc( (void*)newtup, sizeof(IndexTuple) * nlen);
pfree(isnull); isnull = ( v.spl_ldatum ) ? ' ' : 'n';
newtup[nlen-1] = (IndexTuple) index_formtuple(r->rd_att, (Datum *) &(v.spl_ldatum), &isnull);
/* set pointers to new child pages in the internal index tuples */ ItemPointerSet(&(newtup[nlen-1]->t_tid), lbknum, 1);
ItemPointerSet(&(ltup->t_tid), lbknum, 1);
ItemPointerSet(&(rtup->t_tid), rbknum, 1);
gistintinsert(r, stack, ltup, rtup, giststate);
pfree(ltup);
pfree(rtup);
return res;
}
/*
** After a split, we need to overwrite the old entry's key in the parent,
** and install install an entry for the new key into the parent.
*/
static void
gistintinsert(Relation r,
GISTSTACK *stk,
IndexTuple ltup, /* new version of entry for old page */
IndexTuple rtup, /* entry for new page */
GISTSTATE *giststate)
{
ItemPointerData ltid;
if (stk == (GISTSTACK *) NULL)
{
gistnewroot(giststate, r, ltup, rtup);
return;
} }
/* remove old left pointer, insert the 2 new entries */
ItemPointerSet(&ltid, stk->gs_blk, stk->gs_child);
DirectFunctionCall2(gistdelete,
PointerGetDatum(r),
PointerGetDatum(&ltid));
gistentryinserttwo(r, stk, ltup, rtup, giststate);
}
/*
** Insert two entries onto one page, handling a split for either one!
*/
static void
gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup,
IndexTuple rtup, GISTSTATE *giststate)
{
Buffer b;
Page p;
InsertIndexResult res;
GISTENTRY tmpentry;
IndexTuple newtup;
b = ReadBuffer(r, stk->gs_blk);
p = BufferGetPage(b);
if (gistnospace(p, ltup))
{
res = gistSplit(r, b, stk->gs_parent, ltup, giststate);
WriteBuffer(b); /* don't forget to release buffer! -
* 01/31/94 */
pfree(res);
gistdoinsert(r, rtup, giststate);
}
else
{
gistPageAddItem(giststate, r, p, (Item) ltup,
IndexTupleSize(ltup), InvalidOffsetNumber,
LP_USED, &tmpentry, &newtup);
WriteBuffer(b);
gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
tmpentry.bytes, giststate);
/* be tidy */
if (tmpentry.pred != (((char *) ltup) + sizeof(IndexTupleData)))
pfree(tmpentry.pred);
if (ltup != newtup)
pfree(newtup);
gistentryinsert(r, stk, rtup, giststate);
}
}
/* adjust active scans */
gistadjscans(r, GISTOP_SPLIT, BufferGetBlockNumber(buffer), FirstOffsetNumber);
/* /* !!! pfree */
** Insert an entry onto a page pfree( rvectup );
*/ pfree( lvectup );
static InsertIndexResult pfree( v.spl_left );
gistentryinsert(Relation r, GISTSTACK *stk, IndexTuple tup, pfree( v.spl_right );
GISTSTATE *giststate)
{
Buffer b;
Page p;
InsertIndexResult res;
OffsetNumber off;
GISTENTRY tmpentry;
IndexTuple newtup;
b = ReadBuffer(r, stk->gs_blk);
p = BufferGetPage(b);
if (gistnospace(p, tup)) *len = nlen;
{ return newtup;
res = gistSplit(r, b, stk->gs_parent, tup, giststate);
WriteBuffer(b); /* don't forget to release buffer! -
* 01/31/94 */
return res;
}
else
{
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
off = gistPageAddItem(giststate, r, p, (Item) tup, IndexTupleSize(tup),
InvalidOffsetNumber, LP_USED, &tmpentry, &newtup);
WriteBuffer(b);
ItemPointerSet(&(res->pointerData), stk->gs_blk, off);
gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
tmpentry.bytes, giststate);
/* be tidy */
if (tmpentry.pred != (((char *) tup) + sizeof(IndexTupleData)))
pfree(tmpentry.pred);
if (tup != newtup)
pfree(newtup);
return res;
}
} }
static void static void
gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, IndexTuple rt) gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple *itup, int len)
{ {
Buffer b; Buffer b;
Page p; Page p;
GISTENTRY tmpentry;
IndexTuple newtup;
b = ReadBuffer(r, GISTP_ROOT); b = ReadBuffer(r, GISTP_ROOT);
GISTInitBuffer(b, 0); GISTInitBuffer(b, 0);
p = BufferGetPage(b); p = BufferGetPage(b);
gistPageAddItem(giststate, r, p, (Item) lt, IndexTupleSize(lt),
FirstOffsetNumber, gistwritebuffer( r, p, itup, len, FirstOffsetNumber, giststate );
LP_USED, &tmpentry, &newtup);
/* be tidy */
if (tmpentry.pred != (((char *) lt) + sizeof(IndexTupleData)))
pfree(tmpentry.pred);
if (lt != newtup)
pfree(newtup);
gistPageAddItem(giststate, r, p, (Item) rt, IndexTupleSize(rt),
OffsetNumberNext(FirstOffsetNumber), LP_USED,
&tmpentry, &newtup);
/* be tidy */
if (tmpentry.pred != (((char *) rt) + sizeof(IndexTupleData)))
pfree(tmpentry.pred);
if (rt != newtup)
pfree(newtup);
WriteBuffer(b); WriteBuffer(b);
} }
...@@ -1057,21 +968,15 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ ...@@ -1057,21 +968,15 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
if (which_grow == 0) if (which_grow == 0)
break; break;
} }
if (entry.pred != datum) if (entry.pred && entry.pred != datum)
pfree(entry.pred); pfree(entry.pred);
} }
if (identry.pred != id) if (identry.pred && identry.pred != id)
pfree(identry.pred); pfree(identry.pred);
return which; return which;
} }
static int
gistnospace(Page p, IndexTuple it)
{
return PageGetFreeSpace(p) < IndexTupleSize(it);
}
void void
gistfreestack(GISTSTACK *s) gistfreestack(GISTSTACK *s)
{ {
...@@ -1193,7 +1098,7 @@ gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t) ...@@ -1193,7 +1098,7 @@ gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t)
char *datum = (((char *) t) + sizeof(IndexTupleData)); char *datum = (((char *) t) + sizeof(IndexTupleData));
/* if new entry fits in index tuple, copy it in */ /* if new entry fits in index tuple, copy it in */
if ((Size) entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData)) if ((Size) entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData) || (Size) entry.bytes == 0 )
{ {
memcpy(datum, entry.pred, entry.bytes); memcpy(datum, entry.pred, entry.bytes);
/* clear out old size */ /* clear out old size */
...@@ -1208,17 +1113,13 @@ gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t) ...@@ -1208,17 +1113,13 @@ gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t)
/* generate a new index tuple for the compressed entry */ /* generate a new index tuple for the compressed entry */
TupleDesc tupDesc = r->rd_att; TupleDesc tupDesc = r->rd_att;
IndexTuple newtup; IndexTuple newtup;
char *isnull; char isnull;
int blank;
isnull = (char *) palloc(r->rd_rel->relnatts); isnull = ( entry.pred ) ? ' ' : 'n';
for (blank = 0; blank < r->rd_rel->relnatts; blank++)
isnull[blank] = ' ';
newtup = (IndexTuple) index_formtuple(tupDesc, newtup = (IndexTuple) index_formtuple(tupDesc,
(Datum *) &(entry.pred), (Datum *) &(entry.pred),
isnull); &isnull);
newtup->t_tid = t->t_tid; newtup->t_tid = t->t_tid;
pfree(isnull);
return newtup; return newtup;
} }
} }
...@@ -1269,81 +1170,46 @@ gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, ...@@ -1269,81 +1170,46 @@ gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
} }
} }
#ifdef GISTDEBUG #ifdef GISTDEBUG
static void
/* gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber coff)
** sloppy debugging support routine, requires recompilation with appropriate
** "out" method for the index keys. Could be fixed to find that info
** in the catalogs...
*/
void
_gistdump(Relation r)
{ {
Buffer buf; Buffer buffer;
Page page; Page page;
OffsetNumber offnum, GISTPageOpaque opaque;
maxoff; IndexTuple which;
BlockNumber blkno; ItemId iid;
BlockNumber nblocks; OffsetNumber i,maxoff;
GISTPageOpaque po; BlockNumber cblk;
IndexTuple itup; char *pred;
BlockNumber itblkno;
OffsetNumber itoffno;
char *datum;
char *itkey;
nblocks = RelationGetNumberOfBlocks(r); pred = (char*) palloc( sizeof(char)*level+1 );
for (blkno = 0; blkno < nblocks; blkno++) MemSet(pred, '\t', level);
{ pred[level]='\0';
buf = ReadBuffer(r, blkno);
page = BufferGetPage(buf);
po = (GISTPageOpaque) PageGetSpecialPointer(page);
maxoff = PageGetMaxOffsetNumber(page);
printf("Page %d maxoff %d <%s>\n", blkno, maxoff,
(po->flags & F_LEAF ? "LEAF" : "INTERNAL"));
if (PageIsEmpty(page))
{
ReleaseBuffer(buf);
continue;
}
for (offnum = FirstOffsetNumber; buffer = ReadBuffer(r, blk);
offnum <= maxoff; page = (Page) BufferGetPage(buffer);
offnum = OffsetNumberNext(offnum)) opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); maxoff = PageGetMaxOffsetNumber( page );
itblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid)); elog(NOTICE,"%sPage: %d %s blk: %d maxoff: %d free: %d", pred, coff, ( opaque->flags & F_LEAF ) ? "LEAF" : "INTE", (int)blk, (int)maxoff, PageGetFreeSpace(page));
datum = ((char *) itup);
datum += sizeof(IndexTupleData); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
/* get out function for type of key, and out it! */ iid = PageGetItemId(page, i);
itkey = (char *) int_range_out((INTRANGE *) datum); which = (IndexTuple) PageGetItem(page, iid);
/* itkey = " unable to print"; */ cblk = ItemPointerGetBlockNumber(&(which->t_tid));
printf("\t[%d] size %d heap <%d,%d> key:%s\n", #ifdef PRINTTUPLE
offnum, IndexTupleSize(itup), itblkno, itoffno, itkey); elog(NOTICE,"%s Tuple. blk: %d size: %d", pred, (int)cblk, IndexTupleSize( which ) );
pfree(itkey); #endif
if ( ! ( opaque->flags & F_LEAF ) ) {
gist_dumptree( r, level+1, cblk, i );
} }
ReleaseBuffer(buf);
} }
ReleaseBuffer(buffer);
pfree(pred);
} }
static char *
int_range_out(INTRANGE *r)
{
char *result;
if (r == NULL)
return NULL;
result = (char *) palloc(80);
snprintf(result, 80, "[%d,%d): %d", r->lower, r->upper, r->flag);
return result;
}
#endif /* defined GISTDEBUG */ #endif /* defined GISTDEBUG */
void void
...@@ -1362,3 +1228,4 @@ void ...@@ -1362,3 +1228,4 @@ void
gist_desc(char *buf, uint8 xl_info, char* rec) gist_desc(char *buf, uint8 xl_info, char* rec)
{ {
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment