|
|
|
|
@ -8,7 +8,7 @@ |
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.8 2005/09/22 18:49:45 tgl Exp $ |
|
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.9 2005/09/22 20:44:36 momjian Exp $ |
|
|
|
|
* |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
@ -25,162 +25,198 @@ |
|
|
|
|
#include "storage/freespace.h" |
|
|
|
|
#include "storage/smgr.h" |
|
|
|
|
|
|
|
|
|
/* filled by gistbulkdelete, cleared by gistvacuumpcleanup */
|
|
|
|
|
static bool needFullVacuum = false;
|
|
|
|
|
/* filled by gistbulkdelete, cleared by gistvacuumpcleanup */ |
|
|
|
|
static bool needFullVacuum = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
typedef struct |
|
|
|
|
{ |
|
|
|
|
GISTSTATE giststate; |
|
|
|
|
Relation index; |
|
|
|
|
MemoryContext opCtx; |
|
|
|
|
IndexBulkDeleteResult *result; |
|
|
|
|
MemoryContext opCtx; |
|
|
|
|
IndexBulkDeleteResult *result; |
|
|
|
|
} GistVacuum; |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
IndexTuple *itup; |
|
|
|
|
int ituplen; |
|
|
|
|
typedef struct |
|
|
|
|
{ |
|
|
|
|
IndexTuple *itup; |
|
|
|
|
int ituplen; |
|
|
|
|
bool emptypage; |
|
|
|
|
} ArrayTuple; |
|
|
|
|
|
|
|
|
|
static ArrayTuple |
|
|
|
|
gistVacuumUpdate( GistVacuum *gv, BlockNumber blkno, bool needunion ) { |
|
|
|
|
gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) |
|
|
|
|
{ |
|
|
|
|
ArrayTuple res = {NULL, 0, false}; |
|
|
|
|
Buffer buffer; |
|
|
|
|
Page page; |
|
|
|
|
OffsetNumber i, maxoff; |
|
|
|
|
OffsetNumber i, |
|
|
|
|
maxoff; |
|
|
|
|
ItemId iid; |
|
|
|
|
int lenaddon=4, curlenaddon=0, ntodelete=0; |
|
|
|
|
IndexTuple idxtuple, *addon=NULL; |
|
|
|
|
bool needwrite=false; |
|
|
|
|
OffsetNumber todelete[MaxOffsetNumber]; |
|
|
|
|
ItemPointerData *completed=NULL; |
|
|
|
|
int ncompleted=0, lencompleted=16; |
|
|
|
|
int lenaddon = 4, |
|
|
|
|
curlenaddon = 0, |
|
|
|
|
ntodelete = 0; |
|
|
|
|
IndexTuple idxtuple, |
|
|
|
|
*addon = NULL; |
|
|
|
|
bool needwrite = false; |
|
|
|
|
OffsetNumber todelete[MaxOffsetNumber]; |
|
|
|
|
ItemPointerData *completed = NULL; |
|
|
|
|
int ncompleted = 0, |
|
|
|
|
lencompleted = 16; |
|
|
|
|
|
|
|
|
|
buffer = ReadBuffer(gv->index, blkno); |
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
maxoff = PageGetMaxOffsetNumber(page); |
|
|
|
|
|
|
|
|
|
if ( GistPageIsLeaf(page) ) { |
|
|
|
|
if ( GistTuplesDeleted(page) ) { |
|
|
|
|
if (GistPageIsLeaf(page)) |
|
|
|
|
{ |
|
|
|
|
if (GistTuplesDeleted(page)) |
|
|
|
|
{ |
|
|
|
|
needunion = needwrite = true; |
|
|
|
|
GistClearTuplesDeleted(page); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
completed = (ItemPointerData*)palloc( sizeof(ItemPointerData)*lencompleted ); |
|
|
|
|
addon=(IndexTuple*)palloc(sizeof(IndexTuple)*lenaddon); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
completed = (ItemPointerData *) palloc(sizeof(ItemPointerData) * lencompleted); |
|
|
|
|
addon = (IndexTuple *) palloc(sizeof(IndexTuple) * lenaddon); |
|
|
|
|
|
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { |
|
|
|
|
ArrayTuple chldtuple; |
|
|
|
|
bool needchildunion; |
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
|
|
|
|
{ |
|
|
|
|
ArrayTuple chldtuple; |
|
|
|
|
bool needchildunion; |
|
|
|
|
|
|
|
|
|
iid = PageGetItemId(page, i); |
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid); |
|
|
|
|
needchildunion = (GistTupleIsInvalid(idxtuple)) ? true : false; |
|
|
|
|
|
|
|
|
|
if ( needchildunion )
|
|
|
|
|
|
|
|
|
|
if (needchildunion) |
|
|
|
|
elog(DEBUG2, "gistVacuumUpdate: need union for block %u", |
|
|
|
|
ItemPointerGetBlockNumber(&(idxtuple->t_tid))); |
|
|
|
|
|
|
|
|
|
chldtuple = gistVacuumUpdate( gv, ItemPointerGetBlockNumber(&(idxtuple->t_tid)), |
|
|
|
|
needchildunion ); |
|
|
|
|
if ( chldtuple.ituplen || chldtuple.emptypage ) { |
|
|
|
|
|
|
|
|
|
chldtuple = gistVacuumUpdate(gv, ItemPointerGetBlockNumber(&(idxtuple->t_tid)), |
|
|
|
|
needchildunion); |
|
|
|
|
if (chldtuple.ituplen || chldtuple.emptypage) |
|
|
|
|
{ |
|
|
|
|
PageIndexTupleDelete(page, i); |
|
|
|
|
todelete[ ntodelete++ ] = i; |
|
|
|
|
i--; maxoff--; |
|
|
|
|
needwrite=needunion=true; |
|
|
|
|
|
|
|
|
|
if ( chldtuple.ituplen ) { |
|
|
|
|
while( curlenaddon + chldtuple.ituplen >= lenaddon ) { |
|
|
|
|
lenaddon*=2; |
|
|
|
|
addon=(IndexTuple*)repalloc( addon, sizeof(IndexTuple)*lenaddon ); |
|
|
|
|
todelete[ntodelete++] = i; |
|
|
|
|
i--; |
|
|
|
|
maxoff--; |
|
|
|
|
needwrite = needunion = true; |
|
|
|
|
|
|
|
|
|
if (chldtuple.ituplen) |
|
|
|
|
{ |
|
|
|
|
while (curlenaddon + chldtuple.ituplen >= lenaddon) |
|
|
|
|
{ |
|
|
|
|
lenaddon *= 2; |
|
|
|
|
addon = (IndexTuple *) repalloc(addon, sizeof(IndexTuple) * lenaddon); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
memcpy( addon + curlenaddon, chldtuple.itup, chldtuple.ituplen * sizeof(IndexTuple) ); |
|
|
|
|
memcpy(addon + curlenaddon, chldtuple.itup, chldtuple.ituplen * sizeof(IndexTuple)); |
|
|
|
|
|
|
|
|
|
curlenaddon += chldtuple.ituplen; |
|
|
|
|
|
|
|
|
|
if ( chldtuple.ituplen > 1 ) { |
|
|
|
|
/* child was splitted, so we need mark completion insert(split) */ |
|
|
|
|
int j; |
|
|
|
|
|
|
|
|
|
while( ncompleted + chldtuple.ituplen > lencompleted ) { |
|
|
|
|
lencompleted*=2; |
|
|
|
|
completed = (ItemPointerData*)repalloc(completed, sizeof(ItemPointerData) * lencompleted); |
|
|
|
|
}
|
|
|
|
|
for(j=0;j<chldtuple.ituplen;j++) { |
|
|
|
|
ItemPointerCopy( &(chldtuple.itup[j]->t_tid), completed + ncompleted );
|
|
|
|
|
ncompleted++;
|
|
|
|
|
if (chldtuple.ituplen > 1) |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* child was splitted, so we need mark completion |
|
|
|
|
* insert(split) |
|
|
|
|
*/ |
|
|
|
|
int j; |
|
|
|
|
|
|
|
|
|
while (ncompleted + chldtuple.ituplen > lencompleted) |
|
|
|
|
{ |
|
|
|
|
lencompleted *= 2; |
|
|
|
|
completed = (ItemPointerData *) repalloc(completed, sizeof(ItemPointerData) * lencompleted); |
|
|
|
|
} |
|
|
|
|
for (j = 0; j < chldtuple.ituplen; j++) |
|
|
|
|
{ |
|
|
|
|
ItemPointerCopy(&(chldtuple.itup[j]->t_tid), completed + ncompleted); |
|
|
|
|
ncompleted++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pfree( chldtuple.itup ); |
|
|
|
|
pfree(chldtuple.itup); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( curlenaddon ) { |
|
|
|
|
if (curlenaddon) |
|
|
|
|
{ |
|
|
|
|
/* insert updated tuples */ |
|
|
|
|
if (gistnospace(page, addon, curlenaddon)) { |
|
|
|
|
if (gistnospace(page, addon, curlenaddon)) |
|
|
|
|
{ |
|
|
|
|
/* there is no space on page to insert tuples */ |
|
|
|
|
IndexTuple *vec; |
|
|
|
|
SplitedPageLayout *dist=NULL,*ptr; |
|
|
|
|
int i; |
|
|
|
|
MemoryContext oldCtx = MemoryContextSwitchTo(gv->opCtx);
|
|
|
|
|
IndexTuple *vec; |
|
|
|
|
SplitedPageLayout *dist = NULL, |
|
|
|
|
*ptr; |
|
|
|
|
int i; |
|
|
|
|
MemoryContext oldCtx = MemoryContextSwitchTo(gv->opCtx); |
|
|
|
|
|
|
|
|
|
vec = gistextractbuffer(buffer, &(res.ituplen)); |
|
|
|
|
vec = gistjoinvector(vec, &(res.ituplen), addon, curlenaddon); |
|
|
|
|
res.itup = gistSplit(gv->index, buffer, vec, &(res.ituplen), &dist, &(gv->giststate));
|
|
|
|
|
res.itup = gistSplit(gv->index, buffer, vec, &(res.ituplen), &dist, &(gv->giststate)); |
|
|
|
|
MemoryContextSwitchTo(oldCtx); |
|
|
|
|
|
|
|
|
|
vec = (IndexTuple*)palloc( sizeof(IndexTuple) * res.ituplen ); |
|
|
|
|
for(i=0;i<res.ituplen;i++) { |
|
|
|
|
vec[i] = (IndexTuple)palloc( IndexTupleSize(res.itup[i]) ); |
|
|
|
|
memcpy( vec[i], res.itup[i], IndexTupleSize(res.itup[i]) ); |
|
|
|
|
vec = (IndexTuple *) palloc(sizeof(IndexTuple) * res.ituplen); |
|
|
|
|
for (i = 0; i < res.ituplen; i++) |
|
|
|
|
{ |
|
|
|
|
vec[i] = (IndexTuple) palloc(IndexTupleSize(res.itup[i])); |
|
|
|
|
memcpy(vec[i], res.itup[i], IndexTupleSize(res.itup[i])); |
|
|
|
|
} |
|
|
|
|
res.itup = vec;
|
|
|
|
|
res.itup = vec; |
|
|
|
|
|
|
|
|
|
if ( !gv->index->rd_istemp ) { |
|
|
|
|
XLogRecPtr recptr; |
|
|
|
|
XLogRecData *rdata; |
|
|
|
|
ItemPointerData key; /* set key for incomplete insert */ |
|
|
|
|
char *xlinfo; |
|
|
|
|
if (!gv->index->rd_istemp) |
|
|
|
|
{ |
|
|
|
|
XLogRecPtr recptr; |
|
|
|
|
XLogRecData *rdata; |
|
|
|
|
ItemPointerData key; /* set key for incomplete
|
|
|
|
|
* insert */ |
|
|
|
|
char *xlinfo; |
|
|
|
|
|
|
|
|
|
ItemPointerSet(&key, blkno, TUPLE_IS_VALID); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rdata = formSplitRdata(gv->index->rd_node, blkno, |
|
|
|
|
&key, dist); |
|
|
|
|
&key, dist); |
|
|
|
|
xlinfo = rdata->data; |
|
|
|
|
|
|
|
|
|
START_CRIT_SECTION(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata); |
|
|
|
|
ptr = dist; |
|
|
|
|
while(ptr) { |
|
|
|
|
while (ptr) |
|
|
|
|
{ |
|
|
|
|
PageSetLSN(BufferGetPage(ptr->buffer), recptr); |
|
|
|
|
PageSetTLI(BufferGetPage(ptr->buffer), ThisTimeLineID); |
|
|
|
|
ptr=ptr->next; |
|
|
|
|
ptr = ptr->next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
END_CRIT_SECTION(); |
|
|
|
|
pfree( xlinfo ); |
|
|
|
|
pfree( rdata ); |
|
|
|
|
} else { |
|
|
|
|
pfree(xlinfo); |
|
|
|
|
pfree(rdata); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
ptr = dist; |
|
|
|
|
while(ptr) { |
|
|
|
|
while (ptr) |
|
|
|
|
{ |
|
|
|
|
PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp); |
|
|
|
|
ptr=ptr->next; |
|
|
|
|
ptr = ptr->next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ptr = dist; |
|
|
|
|
while(ptr) { |
|
|
|
|
if ( BufferGetBlockNumber(ptr->buffer) != blkno ) |
|
|
|
|
LockBuffer( ptr->buffer, GIST_UNLOCK ); |
|
|
|
|
while (ptr) |
|
|
|
|
{ |
|
|
|
|
if (BufferGetBlockNumber(ptr->buffer) != blkno) |
|
|
|
|
LockBuffer(ptr->buffer, GIST_UNLOCK); |
|
|
|
|
WriteBuffer(ptr->buffer); |
|
|
|
|
ptr=ptr->next; |
|
|
|
|
ptr = ptr->next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( blkno == GIST_ROOT_BLKNO ) {
|
|
|
|
|
ItemPointerData key; /* set key for incomplete insert */ |
|
|
|
|
if (blkno == GIST_ROOT_BLKNO) |
|
|
|
|
{ |
|
|
|
|
ItemPointerData key; /* set key for incomplete
|
|
|
|
|
* insert */ |
|
|
|
|
|
|
|
|
|
ItemPointerSet(&key, blkno, TUPLE_IS_VALID); |
|
|
|
|
|
|
|
|
|
@ -191,82 +227,98 @@ gistVacuumUpdate( GistVacuum *gv, BlockNumber blkno, bool needunion ) { |
|
|
|
|
WriteNoReleaseBuffer(buffer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
needwrite=false; |
|
|
|
|
|
|
|
|
|
needwrite = false; |
|
|
|
|
|
|
|
|
|
MemoryContextReset(gv->opCtx); |
|
|
|
|
|
|
|
|
|
needunion = false; /* gistSplit already forms unions */ |
|
|
|
|
} else { |
|
|
|
|
needunion = false; /* gistSplit already forms unions */ |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
/* enough free space */ |
|
|
|
|
gistfillbuffer(gv->index, page, addon, curlenaddon, InvalidOffsetNumber);
|
|
|
|
|
}
|
|
|
|
|
gistfillbuffer(gv->index, page, addon, curlenaddon, InvalidOffsetNumber); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( needunion ) { |
|
|
|
|
/* forms union for page or check empty*/ |
|
|
|
|
if ( PageIsEmpty(page) ) { |
|
|
|
|
if ( blkno == GIST_ROOT_BLKNO ) { |
|
|
|
|
needwrite=true; |
|
|
|
|
GistPageSetLeaf( page ); |
|
|
|
|
} else { |
|
|
|
|
needwrite=true; |
|
|
|
|
res.emptypage=true; |
|
|
|
|
GistPageSetDeleted( page ); |
|
|
|
|
if (needunion) |
|
|
|
|
{ |
|
|
|
|
/* forms union for page or check empty */ |
|
|
|
|
if (PageIsEmpty(page)) |
|
|
|
|
{ |
|
|
|
|
if (blkno == GIST_ROOT_BLKNO) |
|
|
|
|
{ |
|
|
|
|
needwrite = true; |
|
|
|
|
GistPageSetLeaf(page); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
needwrite = true; |
|
|
|
|
res.emptypage = true; |
|
|
|
|
GistPageSetDeleted(page); |
|
|
|
|
gv->result->pages_deleted++; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
IndexTuple *vec, tmp; |
|
|
|
|
int veclen=0; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
IndexTuple *vec, |
|
|
|
|
tmp; |
|
|
|
|
int veclen = 0; |
|
|
|
|
MemoryContext oldCtx = MemoryContextSwitchTo(gv->opCtx); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vec = gistextractbuffer(buffer, &veclen); |
|
|
|
|
tmp = gistunion(gv->index, vec, veclen, &(gv->giststate)); |
|
|
|
|
tmp = gistunion(gv->index, vec, veclen, &(gv->giststate)); |
|
|
|
|
MemoryContextSwitchTo(oldCtx); |
|
|
|
|
|
|
|
|
|
res.itup=(IndexTuple*)palloc( sizeof(IndexTuple) ); |
|
|
|
|
res.itup = (IndexTuple *) palloc(sizeof(IndexTuple)); |
|
|
|
|
res.ituplen = 1; |
|
|
|
|
res.itup[0] = (IndexTuple)palloc( IndexTupleSize(tmp) ); |
|
|
|
|
memcpy( res.itup[0], tmp, IndexTupleSize(tmp) ); |
|
|
|
|
res.itup[0] = (IndexTuple) palloc(IndexTupleSize(tmp)); |
|
|
|
|
memcpy(res.itup[0], tmp, IndexTupleSize(tmp)); |
|
|
|
|
|
|
|
|
|
ItemPointerSetBlockNumber(&(res.itup[0]->t_tid), blkno); |
|
|
|
|
GistTupleSetValid( res.itup[0] );
|
|
|
|
|
|
|
|
|
|
GistTupleSetValid(res.itup[0]); |
|
|
|
|
|
|
|
|
|
MemoryContextReset(gv->opCtx); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( needwrite ) { |
|
|
|
|
if ( !gv->index->rd_istemp ) { |
|
|
|
|
if (needwrite) |
|
|
|
|
{ |
|
|
|
|
if (!gv->index->rd_istemp) |
|
|
|
|
{ |
|
|
|
|
XLogRecData *rdata; |
|
|
|
|
XLogRecPtr recptr; |
|
|
|
|
char *xlinfo; |
|
|
|
|
char *xlinfo; |
|
|
|
|
|
|
|
|
|
rdata = formUpdateRdata(gv->index->rd_node, blkno, todelete, ntodelete,
|
|
|
|
|
res.emptypage, addon, curlenaddon, NULL ); |
|
|
|
|
rdata = formUpdateRdata(gv->index->rd_node, blkno, todelete, ntodelete, |
|
|
|
|
res.emptypage, addon, curlenaddon, NULL); |
|
|
|
|
xlinfo = rdata->data; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
START_CRIT_SECTION(); |
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_ENTRY_UPDATE, rdata); |
|
|
|
|
PageSetLSN(page, recptr); |
|
|
|
|
PageSetTLI(page, ThisTimeLineID); |
|
|
|
|
END_CRIT_SECTION(); |
|
|
|
|
|
|
|
|
|
pfree( xlinfo ); |
|
|
|
|
pfree( rdata ); |
|
|
|
|
} else
|
|
|
|
|
pfree(xlinfo); |
|
|
|
|
pfree(rdata); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
PageSetLSN(page, XLogRecPtrForTemp); |
|
|
|
|
WriteBuffer( buffer ); |
|
|
|
|
} else |
|
|
|
|
ReleaseBuffer( buffer ); |
|
|
|
|
WriteBuffer(buffer); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
ReleaseBuffer(buffer); |
|
|
|
|
|
|
|
|
|
if ( ncompleted && !gv->index->rd_istemp ) |
|
|
|
|
gistxlogInsertCompletion( gv->index->rd_node, completed, ncompleted ); |
|
|
|
|
if (ncompleted && !gv->index->rd_istemp) |
|
|
|
|
gistxlogInsertCompletion(gv->index->rd_node, completed, ncompleted); |
|
|
|
|
|
|
|
|
|
for(i=0;i<curlenaddon;i++) |
|
|
|
|
pfree( addon[i] ); |
|
|
|
|
if (addon) pfree(addon); |
|
|
|
|
if (completed) pfree(completed);
|
|
|
|
|
for (i = 0; i < curlenaddon; i++) |
|
|
|
|
pfree(addon[i]); |
|
|
|
|
if (addon) |
|
|
|
|
pfree(addon); |
|
|
|
|
if (completed) |
|
|
|
|
pfree(completed); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -278,17 +330,23 @@ gistVacuumUpdate( GistVacuum *gv, BlockNumber blkno, bool needunion ) { |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
Datum |
|
|
|
|
gistvacuumcleanup(PG_FUNCTION_ARGS) { |
|
|
|
|
gistvacuumcleanup(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
Relation rel = (Relation) PG_GETARG_POINTER(0); |
|
|
|
|
IndexVacuumCleanupInfo *info = (IndexVacuumCleanupInfo *) PG_GETARG_POINTER(1); |
|
|
|
|
IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(2); |
|
|
|
|
BlockNumber npages, blkno; |
|
|
|
|
BlockNumber nFreePages, *freePages, maxFreePages; |
|
|
|
|
BlockNumber lastBlock = GIST_ROOT_BLKNO, lastFilledBlock = GIST_ROOT_BLKNO;
|
|
|
|
|
bool needLock; |
|
|
|
|
BlockNumber npages, |
|
|
|
|
blkno; |
|
|
|
|
BlockNumber nFreePages, |
|
|
|
|
*freePages, |
|
|
|
|
maxFreePages; |
|
|
|
|
BlockNumber lastBlock = GIST_ROOT_BLKNO, |
|
|
|
|
lastFilledBlock = GIST_ROOT_BLKNO; |
|
|
|
|
bool needLock; |
|
|
|
|
|
|
|
|
|
/* gistVacuumUpdate may cause hard work */ |
|
|
|
|
if ( info->vacuum_full ) { |
|
|
|
|
if (info->vacuum_full) |
|
|
|
|
{ |
|
|
|
|
GistVacuum gv; |
|
|
|
|
ArrayTuple res; |
|
|
|
|
|
|
|
|
|
@ -300,17 +358,20 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) { |
|
|
|
|
gv.result = stats; |
|
|
|
|
|
|
|
|
|
/* walk through the entire index for update tuples */ |
|
|
|
|
res = gistVacuumUpdate( &gv, GIST_ROOT_BLKNO, false ); |
|
|
|
|
/* cleanup */ |
|
|
|
|
if (res.itup) { |
|
|
|
|
int i; |
|
|
|
|
for(i=0;i<res.ituplen;i++) |
|
|
|
|
pfree( res.itup[i] ); |
|
|
|
|
pfree( res.itup ); |
|
|
|
|
res = gistVacuumUpdate(&gv, GIST_ROOT_BLKNO, false); |
|
|
|
|
/* cleanup */ |
|
|
|
|
if (res.itup) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < res.ituplen; i++) |
|
|
|
|
pfree(res.itup[i]); |
|
|
|
|
pfree(res.itup); |
|
|
|
|
} |
|
|
|
|
freeGISTstate(&(gv.giststate)); |
|
|
|
|
MemoryContextDelete(gv.opCtx); |
|
|
|
|
} else if (needFullVacuum) |
|
|
|
|
freeGISTstate(&(gv.giststate)); |
|
|
|
|
MemoryContextDelete(gv.opCtx); |
|
|
|
|
} |
|
|
|
|
else if (needFullVacuum) |
|
|
|
|
ereport(NOTICE, |
|
|
|
|
(errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery", |
|
|
|
|
RelationGetRelationName(rel)))); |
|
|
|
|
@ -318,8 +379,8 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) { |
|
|
|
|
needFullVacuum = false; |
|
|
|
|
|
|
|
|
|
needLock = !RELATION_IS_LOCAL(rel); |
|
|
|
|
if ( info->vacuum_full ) |
|
|
|
|
needLock = false; /* relation locked with AccessExclusiveLock */ |
|
|
|
|
if (info->vacuum_full) |
|
|
|
|
needLock = false; /* relation locked with AccessExclusiveLock */ |
|
|
|
|
|
|
|
|
|
/* try to find deleted pages */ |
|
|
|
|
if (needLock) |
|
|
|
|
@ -329,45 +390,52 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) { |
|
|
|
|
UnlockRelationForExtension(rel, ExclusiveLock); |
|
|
|
|
|
|
|
|
|
maxFreePages = npages; |
|
|
|
|
if ( maxFreePages > MaxFSMPages ) |
|
|
|
|
if (maxFreePages > MaxFSMPages) |
|
|
|
|
maxFreePages = MaxFSMPages; |
|
|
|
|
|
|
|
|
|
nFreePages = 0; |
|
|
|
|
freePages = (BlockNumber*) palloc (sizeof(BlockNumber) * maxFreePages); |
|
|
|
|
for(blkno=GIST_ROOT_BLKNO+1;blkno<npages;blkno++) { |
|
|
|
|
Buffer buffer = ReadBuffer(rel, blkno); |
|
|
|
|
Page page;
|
|
|
|
|
|
|
|
|
|
LockBuffer( buffer, GIST_SHARE ); |
|
|
|
|
page=(Page)BufferGetPage(buffer); |
|
|
|
|
|
|
|
|
|
if ( GistPageIsDeleted(page) ) { |
|
|
|
|
if (nFreePages < maxFreePages) { |
|
|
|
|
freePages[ nFreePages ] = blkno; |
|
|
|
|
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages); |
|
|
|
|
for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++) |
|
|
|
|
{ |
|
|
|
|
Buffer buffer = ReadBuffer(rel, blkno); |
|
|
|
|
Page page; |
|
|
|
|
|
|
|
|
|
LockBuffer(buffer, GIST_SHARE); |
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
|
|
|
|
|
if (GistPageIsDeleted(page)) |
|
|
|
|
{ |
|
|
|
|
if (nFreePages < maxFreePages) |
|
|
|
|
{ |
|
|
|
|
freePages[nFreePages] = blkno; |
|
|
|
|
nFreePages++; |
|
|
|
|
} |
|
|
|
|
} else |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
lastFilledBlock = blkno; |
|
|
|
|
LockBuffer( buffer, GIST_UNLOCK ); |
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK); |
|
|
|
|
ReleaseBuffer(buffer); |
|
|
|
|
} |
|
|
|
|
lastBlock = npages-1; |
|
|
|
|
|
|
|
|
|
if ( info->vacuum_full && nFreePages>0 ) { /* try to truncate index */ |
|
|
|
|
int i; |
|
|
|
|
for(i=0;i<nFreePages;i++) |
|
|
|
|
if ( freePages[i] >= lastFilledBlock ) { |
|
|
|
|
lastBlock = npages - 1; |
|
|
|
|
|
|
|
|
|
if (info->vacuum_full && nFreePages > 0) |
|
|
|
|
{ /* try to truncate index */ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < nFreePages; i++) |
|
|
|
|
if (freePages[i] >= lastFilledBlock) |
|
|
|
|
{ |
|
|
|
|
nFreePages = i; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( lastBlock > lastFilledBlock )
|
|
|
|
|
RelationTruncate( rel, lastFilledBlock+1 ); |
|
|
|
|
|
|
|
|
|
if (lastBlock > lastFilledBlock) |
|
|
|
|
RelationTruncate(rel, lastFilledBlock + 1); |
|
|
|
|
stats->pages_removed = lastBlock - lastFilledBlock; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RecordIndexFreeSpace( &rel->rd_node, nFreePages, freePages ); |
|
|
|
|
pfree( freePages );
|
|
|
|
|
|
|
|
|
|
RecordIndexFreeSpace(&rel->rd_node, nFreePages, freePages); |
|
|
|
|
pfree(freePages); |
|
|
|
|
|
|
|
|
|
/* return statistics */ |
|
|
|
|
stats->pages_free = nFreePages; |
|
|
|
|
@ -378,33 +446,37 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) { |
|
|
|
|
UnlockRelationForExtension(rel, ExclusiveLock); |
|
|
|
|
|
|
|
|
|
if (info->vacuum_full) |
|
|
|
|
UnlockRelation(rel, AccessExclusiveLock);
|
|
|
|
|
UnlockRelation(rel, AccessExclusiveLock); |
|
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(stats); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct GistBDItem { |
|
|
|
|
typedef struct GistBDItem |
|
|
|
|
{ |
|
|
|
|
GistNSN parentlsn; |
|
|
|
|
BlockNumber blkno; |
|
|
|
|
struct GistBDItem *next;
|
|
|
|
|
BlockNumber blkno; |
|
|
|
|
struct GistBDItem *next; |
|
|
|
|
} GistBDItem; |
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
pushStackIfSplited(Page page, GistBDItem *stack) { |
|
|
|
|
pushStackIfSplited(Page page, GistBDItem *stack) |
|
|
|
|
{ |
|
|
|
|
GISTPageOpaque opaque = GistPageGetOpaque(page); |
|
|
|
|
|
|
|
|
|
if ( stack->blkno!=GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid( stack->parentlsn ) &&
|
|
|
|
|
XLByteLT( stack->parentlsn, opaque->nsn) &&
|
|
|
|
|
opaque->rightlink != InvalidBlockNumber /* sanity check */ ) { |
|
|
|
|
if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) && |
|
|
|
|
XLByteLT(stack->parentlsn, opaque->nsn) && |
|
|
|
|
opaque->rightlink != InvalidBlockNumber /* sanity check */ ) |
|
|
|
|
{ |
|
|
|
|
/* split page detected, install right link to the stack */ |
|
|
|
|
|
|
|
|
|
GistBDItem *ptr = (GistBDItem*) palloc(sizeof(GistBDItem)); |
|
|
|
|
GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem)); |
|
|
|
|
|
|
|
|
|
ptr->blkno = opaque->rightlink; |
|
|
|
|
ptr->parentlsn = stack->parentlsn; |
|
|
|
|
ptr->next = stack->next; |
|
|
|
|
stack->next = ptr; |
|
|
|
|
} |
|
|
|
|
}
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@ -416,38 +488,44 @@ pushStackIfSplited(Page page, GistBDItem *stack) { |
|
|
|
|
* Result: a palloc'd struct containing statistical info for VACUUM displays. |
|
|
|
|
*/ |
|
|
|
|
Datum |
|
|
|
|
gistbulkdelete(PG_FUNCTION_ARGS) { |
|
|
|
|
gistbulkdelete(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
Relation rel = (Relation) PG_GETARG_POINTER(0); |
|
|
|
|
IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(1); |
|
|
|
|
void* callback_state = (void *) PG_GETARG_POINTER(2); |
|
|
|
|
IndexBulkDeleteResult *result = (IndexBulkDeleteResult*)palloc0(sizeof(IndexBulkDeleteResult));
|
|
|
|
|
GistBDItem *stack, *ptr; |
|
|
|
|
bool needLock; |
|
|
|
|
|
|
|
|
|
stack = (GistBDItem*) palloc0(sizeof(GistBDItem)); |
|
|
|
|
void *callback_state = (void *) PG_GETARG_POINTER(2); |
|
|
|
|
IndexBulkDeleteResult *result = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); |
|
|
|
|
GistBDItem *stack, |
|
|
|
|
*ptr; |
|
|
|
|
bool needLock; |
|
|
|
|
|
|
|
|
|
stack = (GistBDItem *) palloc0(sizeof(GistBDItem)); |
|
|
|
|
|
|
|
|
|
stack->blkno = GIST_ROOT_BLKNO; |
|
|
|
|
needFullVacuum = false; |
|
|
|
|
|
|
|
|
|
while( stack ) { |
|
|
|
|
Buffer buffer = ReadBuffer(rel, stack->blkno); |
|
|
|
|
Page page; |
|
|
|
|
OffsetNumber i, maxoff; |
|
|
|
|
while (stack) |
|
|
|
|
{ |
|
|
|
|
Buffer buffer = ReadBuffer(rel, stack->blkno); |
|
|
|
|
Page page; |
|
|
|
|
OffsetNumber i, |
|
|
|
|
maxoff; |
|
|
|
|
IndexTuple idxtuple; |
|
|
|
|
ItemId iid; |
|
|
|
|
|
|
|
|
|
LockBuffer(buffer, GIST_SHARE);
|
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
LockBuffer(buffer, GIST_SHARE); |
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
|
|
|
|
|
if ( GistPageIsLeaf(page) ) { |
|
|
|
|
if (GistPageIsLeaf(page)) |
|
|
|
|
{ |
|
|
|
|
OffsetNumber todelete[MaxOffsetNumber]; |
|
|
|
|
int ntodelete = 0; |
|
|
|
|
int ntodelete = 0; |
|
|
|
|
|
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK); |
|
|
|
|
LockBuffer(buffer, GIST_EXCLUSIVE); |
|
|
|
|
|
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
if ( stack->blkno==GIST_ROOT_BLKNO && !GistPageIsLeaf(page) ) { |
|
|
|
|
page = (Page) BufferGetPage(buffer); |
|
|
|
|
if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page)) |
|
|
|
|
{ |
|
|
|
|
/* the only root can become non-leaf during relock */ |
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK); |
|
|
|
|
ReleaseBuffer(buffer); |
|
|
|
|
@ -455,37 +533,46 @@ gistbulkdelete(PG_FUNCTION_ARGS) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* check for split proceeded after look at parent,
|
|
|
|
|
we should check it after relock */ |
|
|
|
|
/*
|
|
|
|
|
* check for split proceeded after look at parent, we should check |
|
|
|
|
* it after relock |
|
|
|
|
*/ |
|
|
|
|
pushStackIfSplited(page, stack); |
|
|
|
|
|
|
|
|
|
maxoff = PageGetMaxOffsetNumber(page); |
|
|
|
|
|
|
|
|
|
for(i=FirstOffsetNumber;i<=maxoff;i=OffsetNumberNext(i)) { |
|
|
|
|
iid = PageGetItemId(page, i);
|
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
|
|
|
|
{ |
|
|
|
|
iid = PageGetItemId(page, i); |
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid); |
|
|
|
|
|
|
|
|
|
if ( callback(&(idxtuple->t_tid), callback_state) ) { |
|
|
|
|
if (callback(&(idxtuple->t_tid), callback_state)) |
|
|
|
|
{ |
|
|
|
|
PageIndexTupleDelete(page, i); |
|
|
|
|
todelete[ ntodelete ] = i; |
|
|
|
|
i--; maxoff--; ntodelete++; |
|
|
|
|
todelete[ntodelete] = i; |
|
|
|
|
i--; |
|
|
|
|
maxoff--; |
|
|
|
|
ntodelete++; |
|
|
|
|
result->tuples_removed += 1; |
|
|
|
|
Assert( maxoff == PageGetMaxOffsetNumber(page) ); |
|
|
|
|
} else
|
|
|
|
|
Assert(maxoff == PageGetMaxOffsetNumber(page)); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
result->num_index_tuples += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ( ntodelete ) { |
|
|
|
|
if (ntodelete) |
|
|
|
|
{ |
|
|
|
|
GistMarkTuplesDeleted(page); |
|
|
|
|
|
|
|
|
|
if (!rel->rd_istemp ) { |
|
|
|
|
if (!rel->rd_istemp) |
|
|
|
|
{ |
|
|
|
|
XLogRecData *rdata; |
|
|
|
|
XLogRecPtr recptr; |
|
|
|
|
XLogRecPtr recptr; |
|
|
|
|
gistxlogEntryUpdate *xlinfo; |
|
|
|
|
|
|
|
|
|
rdata = formUpdateRdata(rel->rd_node, stack->blkno, todelete, ntodelete, |
|
|
|
|
false, NULL, 0, NULL); |
|
|
|
|
xlinfo = (gistxlogEntryUpdate*)rdata->data; |
|
|
|
|
false, NULL, 0, NULL); |
|
|
|
|
xlinfo = (gistxlogEntryUpdate *) rdata->data; |
|
|
|
|
|
|
|
|
|
START_CRIT_SECTION(); |
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_ENTRY_UPDATE, rdata); |
|
|
|
|
@ -493,39 +580,43 @@ gistbulkdelete(PG_FUNCTION_ARGS) { |
|
|
|
|
PageSetTLI(page, ThisTimeLineID); |
|
|
|
|
END_CRIT_SECTION(); |
|
|
|
|
|
|
|
|
|
pfree( xlinfo ); |
|
|
|
|
pfree( rdata ); |
|
|
|
|
} else |
|
|
|
|
pfree(xlinfo); |
|
|
|
|
pfree(rdata); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
PageSetLSN(page, XLogRecPtrForTemp); |
|
|
|
|
WriteNoReleaseBuffer( buffer ); |
|
|
|
|
WriteNoReleaseBuffer(buffer); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
/* check for split proceeded after look at parent */ |
|
|
|
|
pushStackIfSplited(page, stack); |
|
|
|
|
|
|
|
|
|
maxoff = PageGetMaxOffsetNumber(page); |
|
|
|
|
|
|
|
|
|
for(i=FirstOffsetNumber;i<=maxoff;i=OffsetNumberNext(i)) { |
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
|
|
|
|
{ |
|
|
|
|
iid = PageGetItemId(page, i); |
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid); |
|
|
|
|
|
|
|
|
|
ptr = (GistBDItem*) palloc(sizeof(GistBDItem)); |
|
|
|
|
ptr->blkno = ItemPointerGetBlockNumber( &(idxtuple->t_tid) ); |
|
|
|
|
ptr->parentlsn = PageGetLSN( page ); |
|
|
|
|
ptr = (GistBDItem *) palloc(sizeof(GistBDItem)); |
|
|
|
|
ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid)); |
|
|
|
|
ptr->parentlsn = PageGetLSN(page); |
|
|
|
|
ptr->next = stack->next; |
|
|
|
|
stack->next = ptr; |
|
|
|
|
|
|
|
|
|
if ( GistTupleIsInvalid(idxtuple) ) |
|
|
|
|
if (GistTupleIsInvalid(idxtuple)) |
|
|
|
|
needFullVacuum = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LockBuffer( buffer, GIST_UNLOCK ); |
|
|
|
|
ReleaseBuffer( buffer ); |
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK); |
|
|
|
|
ReleaseBuffer(buffer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ptr = stack->next; |
|
|
|
|
pfree( stack ); |
|
|
|
|
pfree(stack); |
|
|
|
|
stack = ptr; |
|
|
|
|
|
|
|
|
|
vacuum_delay_point(); |
|
|
|
|
@ -539,6 +630,5 @@ gistbulkdelete(PG_FUNCTION_ARGS) { |
|
|
|
|
if (needLock) |
|
|
|
|
UnlockRelationForExtension(rel, ExclusiveLock); |
|
|
|
|
|
|
|
|
|
PG_RETURN_POINTER( result ); |
|
|
|
|
PG_RETURN_POINTER(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|