mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
pgindent run for 8.2.
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginarrayproc.c
|
||||
* support functions for GIN's indexing of any array
|
||||
* support functions for GIN's indexing of any array
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginarrayproc.c,v 1.5 2006/09/10 20:14:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginarrayproc.c,v 1.6 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
@@ -23,64 +23,73 @@
|
||||
#define GinContainedStrategy 3
|
||||
#define GinEqualStrategy 4
|
||||
|
||||
#define ARRAYCHECK(x) do { \
|
||||
#define ARRAYCHECK(x) do { \
|
||||
if ( ARR_HASNULL(x) ) \
|
||||
ereport(ERROR, \
|
||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \
|
||||
errmsg("array must not contain nulls"))); \
|
||||
} while(0)
|
||||
ereport(ERROR, \
|
||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \
|
||||
errmsg("array must not contain nulls"))); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/*
|
||||
* Function used as extractValue and extractQuery both
|
||||
*/
|
||||
Datum
|
||||
ginarrayextract(PG_FUNCTION_ARGS) {
|
||||
ArrayType *array;
|
||||
uint32 *nentries = (uint32*)PG_GETARG_POINTER(1);
|
||||
Datum *entries = NULL;
|
||||
int16 elmlen;
|
||||
bool elmbyval;
|
||||
char elmalign;
|
||||
ginarrayextract(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *array;
|
||||
uint32 *nentries = (uint32 *) PG_GETARG_POINTER(1);
|
||||
Datum *entries = NULL;
|
||||
int16 elmlen;
|
||||
bool elmbyval;
|
||||
char elmalign;
|
||||
|
||||
/* we should guarantee that array will not be destroyed during all operation */
|
||||
/*
|
||||
* we should guarantee that array will not be destroyed during all
|
||||
* operation
|
||||
*/
|
||||
array = PG_GETARG_ARRAYTYPE_P_COPY(0);
|
||||
|
||||
ARRAYCHECK(array);
|
||||
|
||||
get_typlenbyvalalign(ARR_ELEMTYPE(array),
|
||||
&elmlen, &elmbyval, &elmalign);
|
||||
&elmlen, &elmbyval, &elmalign);
|
||||
|
||||
deconstruct_array(array,
|
||||
ARR_ELEMTYPE(array),
|
||||
elmlen, elmbyval, elmalign,
|
||||
&entries, NULL, (int*)nentries);
|
||||
ARR_ELEMTYPE(array),
|
||||
elmlen, elmbyval, elmalign,
|
||||
&entries, NULL, (int *) nentries);
|
||||
|
||||
/* we should not free array, entries[i] points into it */
|
||||
PG_RETURN_POINTER(entries);
|
||||
}
|
||||
|
||||
Datum
|
||||
ginarrayconsistent(PG_FUNCTION_ARGS) {
|
||||
bool *check = (bool*)PG_GETARG_POINTER(0);
|
||||
StrategyNumber strategy = PG_GETARG_UINT16(1);
|
||||
ArrayType *query = PG_GETARG_ARRAYTYPE_P(2);
|
||||
int res, i, nentries;
|
||||
ginarrayconsistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool *check = (bool *) PG_GETARG_POINTER(0);
|
||||
StrategyNumber strategy = PG_GETARG_UINT16(1);
|
||||
ArrayType *query = PG_GETARG_ARRAYTYPE_P(2);
|
||||
int res,
|
||||
i,
|
||||
nentries;
|
||||
|
||||
/* ARRAYCHECK was already done by previous ginarrayextract call */
|
||||
|
||||
switch( strategy ) {
|
||||
switch (strategy)
|
||||
{
|
||||
case GinOverlapStrategy:
|
||||
case GinContainedStrategy:
|
||||
/* at least one element in check[] is true, so result = true */
|
||||
/* at least one element in check[] is true, so result = true */
|
||||
res = TRUE;
|
||||
break;
|
||||
case GinContainsStrategy:
|
||||
case GinEqualStrategy:
|
||||
nentries=ArrayGetNItems(ARR_NDIM(query), ARR_DIMS(query));
|
||||
nentries = ArrayGetNItems(ARR_NDIM(query), ARR_DIMS(query));
|
||||
res = TRUE;
|
||||
for(i=0;i<nentries;i++)
|
||||
if ( !check[i] ) {
|
||||
for (i = 0; i < nentries; i++)
|
||||
if (!check[i])
|
||||
{
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginbtree.c
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginbtree.c,v 1.4 2006/07/14 14:52:16 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginbtree.c,v 1.5 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -20,24 +20,29 @@
|
||||
* Locks buffer by needed method for search.
|
||||
*/
|
||||
static int
|
||||
ginTraverseLock(Buffer buffer, bool searchMode) {
|
||||
Page page;
|
||||
int access=GIN_SHARE;
|
||||
ginTraverseLock(Buffer buffer, bool searchMode)
|
||||
{
|
||||
Page page;
|
||||
int access = GIN_SHARE;
|
||||
|
||||
LockBuffer(buffer, GIN_SHARE);
|
||||
page = BufferGetPage( buffer );
|
||||
if ( GinPageIsLeaf(page) ) {
|
||||
if ( searchMode == FALSE ) {
|
||||
page = BufferGetPage(buffer);
|
||||
if (GinPageIsLeaf(page))
|
||||
{
|
||||
if (searchMode == FALSE)
|
||||
{
|
||||
/* we should relock our page */
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
LockBuffer(buffer, GIN_EXCLUSIVE);
|
||||
|
||||
/* But root can become non-leaf during relock */
|
||||
if ( !GinPageIsLeaf(page) ) {
|
||||
/* resore old lock type (very rare) */
|
||||
if (!GinPageIsLeaf(page))
|
||||
{
|
||||
/* resore old lock type (very rare) */
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
LockBuffer(buffer, GIN_SHARE);
|
||||
} else
|
||||
}
|
||||
else
|
||||
access = GIN_EXCLUSIVE;
|
||||
}
|
||||
}
|
||||
@@ -45,9 +50,10 @@ ginTraverseLock(Buffer buffer, bool searchMode) {
|
||||
return access;
|
||||
}
|
||||
|
||||
GinBtreeStack*
|
||||
ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno) {
|
||||
GinBtreeStack *stack = (GinBtreeStack*)palloc(sizeof(GinBtreeStack));
|
||||
GinBtreeStack *
|
||||
ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno)
|
||||
{
|
||||
GinBtreeStack *stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
|
||||
|
||||
stack->blkno = blkno;
|
||||
stack->buffer = ReadBuffer(btree->index, stack->blkno);
|
||||
@@ -62,63 +68,73 @@ ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno) {
|
||||
/*
|
||||
* Locates leaf page contained tuple
|
||||
*/
|
||||
GinBtreeStack*
|
||||
ginFindLeafPage(GinBtree btree, GinBtreeStack *stack) {
|
||||
bool isfirst=TRUE;
|
||||
GinBtreeStack *
|
||||
ginFindLeafPage(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
bool isfirst = TRUE;
|
||||
BlockNumber rootBlkno;
|
||||
|
||||
if ( !stack )
|
||||
if (!stack)
|
||||
stack = ginPrepareFindLeafPage(btree, GIN_ROOT_BLKNO);
|
||||
rootBlkno = stack->blkno;
|
||||
|
||||
for(;;) {
|
||||
Page page;
|
||||
for (;;)
|
||||
{
|
||||
Page page;
|
||||
BlockNumber child;
|
||||
int access=GIN_SHARE;
|
||||
int access = GIN_SHARE;
|
||||
|
||||
stack->off = InvalidOffsetNumber;
|
||||
|
||||
page = BufferGetPage( stack->buffer );
|
||||
|
||||
if ( isfirst ) {
|
||||
if ( GinPageIsLeaf(page) && !btree->searchMode )
|
||||
page = BufferGetPage(stack->buffer);
|
||||
|
||||
if (isfirst)
|
||||
{
|
||||
if (GinPageIsLeaf(page) && !btree->searchMode)
|
||||
access = GIN_EXCLUSIVE;
|
||||
isfirst = FALSE;
|
||||
} else
|
||||
}
|
||||
else
|
||||
access = ginTraverseLock(stack->buffer, btree->searchMode);
|
||||
|
||||
/* ok, page is correctly locked, we should check to move right ..,
|
||||
root never has a right link, so small optimization */
|
||||
while( btree->fullScan==FALSE && stack->blkno != rootBlkno && btree->isMoveRight(btree, page) ) {
|
||||
/*
|
||||
* ok, page is correctly locked, we should check to move right ..,
|
||||
* root never has a right link, so small optimization
|
||||
*/
|
||||
while (btree->fullScan == FALSE && stack->blkno != rootBlkno && btree->isMoveRight(btree, page))
|
||||
{
|
||||
BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
if ( rightlink==InvalidBlockNumber )
|
||||
if (rightlink == InvalidBlockNumber)
|
||||
/* rightmost page */
|
||||
break;
|
||||
|
||||
stack->blkno = rightlink;
|
||||
LockBuffer(stack->buffer, GIN_UNLOCK);
|
||||
stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
|
||||
LockBuffer(stack->buffer, access);
|
||||
page = BufferGetPage( stack->buffer );
|
||||
LockBuffer(stack->buffer, access);
|
||||
page = BufferGetPage(stack->buffer);
|
||||
}
|
||||
|
||||
if ( GinPageIsLeaf(page) ) /* we found, return locked page */
|
||||
if (GinPageIsLeaf(page)) /* we found, return locked page */
|
||||
return stack;
|
||||
|
||||
/* now we have correct buffer, try to find child */
|
||||
child = btree->findChildPage(btree, stack);
|
||||
|
||||
LockBuffer(stack->buffer, GIN_UNLOCK);
|
||||
Assert( child != InvalidBlockNumber );
|
||||
Assert( stack->blkno != child );
|
||||
Assert(child != InvalidBlockNumber);
|
||||
Assert(stack->blkno != child);
|
||||
|
||||
if ( btree->searchMode ) {
|
||||
if (btree->searchMode)
|
||||
{
|
||||
/* in search mode we may forget path to leaf */
|
||||
stack->blkno = child;
|
||||
stack->buffer = ReleaseAndReadBuffer( stack->buffer, btree->index, stack->blkno );
|
||||
} else {
|
||||
GinBtreeStack *ptr = (GinBtreeStack*)palloc(sizeof(GinBtreeStack));
|
||||
stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
|
||||
}
|
||||
else
|
||||
{
|
||||
GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
|
||||
|
||||
ptr->parent = stack;
|
||||
stack = ptr;
|
||||
@@ -133,93 +149,110 @@ ginFindLeafPage(GinBtree btree, GinBtreeStack *stack) {
|
||||
}
|
||||
|
||||
void
|
||||
freeGinBtreeStack( GinBtreeStack *stack ) {
|
||||
while(stack) {
|
||||
GinBtreeStack *tmp = stack->parent;
|
||||
if ( stack->buffer != InvalidBuffer )
|
||||
freeGinBtreeStack(GinBtreeStack *stack)
|
||||
{
|
||||
while (stack)
|
||||
{
|
||||
GinBtreeStack *tmp = stack->parent;
|
||||
|
||||
if (stack->buffer != InvalidBuffer)
|
||||
ReleaseBuffer(stack->buffer);
|
||||
|
||||
pfree( stack );
|
||||
pfree(stack);
|
||||
stack = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find parent for current stack position, returns correct
|
||||
* Try to find parent for current stack position, returns correct
|
||||
* parent and child's offset in stack->parent.
|
||||
* Function should never release root page to prevent conflicts
|
||||
* with vacuum process
|
||||
*/
|
||||
void
|
||||
findParents( GinBtree btree, GinBtreeStack *stack,
|
||||
BlockNumber rootBlkno) {
|
||||
findParents(GinBtree btree, GinBtreeStack *stack,
|
||||
BlockNumber rootBlkno)
|
||||
{
|
||||
|
||||
Page page;
|
||||
Buffer buffer;
|
||||
BlockNumber blkno, leftmostBlkno;
|
||||
Page page;
|
||||
Buffer buffer;
|
||||
BlockNumber blkno,
|
||||
leftmostBlkno;
|
||||
OffsetNumber offset;
|
||||
GinBtreeStack *root = stack->parent;
|
||||
GinBtreeStack *ptr;
|
||||
GinBtreeStack *root = stack->parent;
|
||||
GinBtreeStack *ptr;
|
||||
|
||||
if ( !root ) {
|
||||
if (!root)
|
||||
{
|
||||
/* XLog mode... */
|
||||
root = (GinBtreeStack*)palloc(sizeof(GinBtreeStack));
|
||||
root = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
|
||||
root->blkno = rootBlkno;
|
||||
root->buffer = ReadBuffer(btree->index, rootBlkno);
|
||||
LockBuffer(root->buffer, GIN_EXCLUSIVE);
|
||||
root->parent = NULL;
|
||||
} else {
|
||||
/* find root, we should not release root page until update is finished!! */
|
||||
while( root->parent ) {
|
||||
ReleaseBuffer( root->buffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* find root, we should not release root page until update is
|
||||
* finished!!
|
||||
*/
|
||||
while (root->parent)
|
||||
{
|
||||
ReleaseBuffer(root->buffer);
|
||||
root = root->parent;
|
||||
}
|
||||
|
||||
Assert( root->blkno == rootBlkno );
|
||||
Assert( BufferGetBlockNumber(root->buffer) == rootBlkno );
|
||||
Assert(root->blkno == rootBlkno);
|
||||
Assert(BufferGetBlockNumber(root->buffer) == rootBlkno);
|
||||
LockBuffer(root->buffer, GIN_EXCLUSIVE);
|
||||
}
|
||||
root->off = InvalidOffsetNumber;
|
||||
|
||||
page = BufferGetPage(root->buffer);
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
|
||||
/* check trivial case */
|
||||
if ( (root->off = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) != InvalidOffsetNumber ) {
|
||||
if ((root->off = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) != InvalidOffsetNumber)
|
||||
{
|
||||
stack->parent = root;
|
||||
return;
|
||||
}
|
||||
|
||||
leftmostBlkno = blkno = btree->getLeftMostPage(btree, page);
|
||||
LockBuffer(root->buffer, GIN_UNLOCK );
|
||||
Assert( blkno!=InvalidBlockNumber );
|
||||
LockBuffer(root->buffer, GIN_UNLOCK);
|
||||
Assert(blkno != InvalidBlockNumber);
|
||||
|
||||
|
||||
for(;;) {
|
||||
for (;;)
|
||||
{
|
||||
buffer = ReadBuffer(btree->index, blkno);
|
||||
LockBuffer(buffer, GIN_EXCLUSIVE);
|
||||
page = BufferGetPage(buffer);
|
||||
if ( GinPageIsLeaf(page) )
|
||||
if (GinPageIsLeaf(page))
|
||||
elog(ERROR, "Lost path");
|
||||
|
||||
leftmostBlkno = btree->getLeftMostPage(btree, page);
|
||||
|
||||
while( (offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber))==InvalidOffsetNumber ) {
|
||||
while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber)
|
||||
{
|
||||
blkno = GinPageGetOpaque(page)->rightlink;
|
||||
LockBuffer(buffer,GIN_UNLOCK);
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
ReleaseBuffer(buffer);
|
||||
if ( blkno == InvalidBlockNumber )
|
||||
if (blkno == InvalidBlockNumber)
|
||||
break;
|
||||
buffer = ReadBuffer(btree->index, blkno);
|
||||
LockBuffer(buffer, GIN_EXCLUSIVE);
|
||||
page = BufferGetPage(buffer);
|
||||
}
|
||||
|
||||
if ( blkno != InvalidBlockNumber ) {
|
||||
ptr = (GinBtreeStack*)palloc(sizeof(GinBtreeStack));
|
||||
if (blkno != InvalidBlockNumber)
|
||||
{
|
||||
ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
|
||||
ptr->blkno = blkno;
|
||||
ptr->buffer = buffer;
|
||||
ptr->parent = root; /* it's may be wrong, but in next call we will correct */
|
||||
ptr->parent = root; /* it's may be wrong, but in next call we will
|
||||
* correct */
|
||||
ptr->off = offset;
|
||||
stack->parent = ptr;
|
||||
return;
|
||||
@@ -233,79 +266,94 @@ findParents( GinBtree btree, GinBtreeStack *stack,
|
||||
* Insert value (stored in GinBtree) to tree descibed by stack
|
||||
*/
|
||||
void
|
||||
ginInsertValue(GinBtree btree, GinBtreeStack *stack) {
|
||||
GinBtreeStack *parent = stack;
|
||||
BlockNumber rootBlkno = InvalidBuffer;
|
||||
Page page, rpage, lpage;
|
||||
ginInsertValue(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
GinBtreeStack *parent = stack;
|
||||
BlockNumber rootBlkno = InvalidBuffer;
|
||||
Page page,
|
||||
rpage,
|
||||
lpage;
|
||||
|
||||
/* remember root BlockNumber */
|
||||
while( parent ) {
|
||||
while (parent)
|
||||
{
|
||||
rootBlkno = parent->blkno;
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
while( stack ) {
|
||||
while (stack)
|
||||
{
|
||||
XLogRecData *rdata;
|
||||
BlockNumber savedRightLink;
|
||||
BlockNumber savedRightLink;
|
||||
|
||||
page = BufferGetPage( stack->buffer );
|
||||
page = BufferGetPage(stack->buffer);
|
||||
savedRightLink = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
if ( btree->isEnoughSpace( btree, stack->buffer, stack->off ) ) {
|
||||
if (btree->isEnoughSpace(btree, stack->buffer, stack->off))
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
btree->placeToPage( btree, stack->buffer, stack->off, &rdata );
|
||||
btree->placeToPage(btree, stack->buffer, stack->off, &rdata);
|
||||
|
||||
if (!btree->index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
if (!btree->index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT, rdata);
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
}
|
||||
|
||||
MarkBufferDirty( stack->buffer );
|
||||
MarkBufferDirty(stack->buffer);
|
||||
UnlockReleaseBuffer(stack->buffer);
|
||||
END_CRIT_SECTION();
|
||||
|
||||
freeGinBtreeStack(stack->parent);
|
||||
return;
|
||||
} else {
|
||||
Buffer rbuffer = GinNewBuffer(btree->index);
|
||||
Page newlpage;
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer rbuffer = GinNewBuffer(btree->index);
|
||||
Page newlpage;
|
||||
|
||||
/* newlpage is a pointer to memory page, it does'nt assosiates with buffer,
|
||||
stack->buffer shoud be untouched */
|
||||
newlpage = btree->splitPage( btree, stack->buffer, rbuffer, stack->off, &rdata );
|
||||
/*
|
||||
* newlpage is a pointer to memory page, it does'nt assosiates
|
||||
* with buffer, stack->buffer shoud be untouched
|
||||
*/
|
||||
newlpage = btree->splitPage(btree, stack->buffer, rbuffer, stack->off, &rdata);
|
||||
|
||||
|
||||
((ginxlogSplit*)(rdata->data))->rootBlkno = rootBlkno;
|
||||
((ginxlogSplit *) (rdata->data))->rootBlkno = rootBlkno;
|
||||
|
||||
parent = stack->parent;
|
||||
|
||||
if ( parent == NULL ) {
|
||||
/* split root, so we need to allocate new left page and
|
||||
place pointer on root to left and right page */
|
||||
Buffer lbuffer = GinNewBuffer(btree->index);
|
||||
if (parent == NULL)
|
||||
{
|
||||
/*
|
||||
* split root, so we need to allocate new left page and place
|
||||
* pointer on root to left and right page
|
||||
*/
|
||||
Buffer lbuffer = GinNewBuffer(btree->index);
|
||||
|
||||
((ginxlogSplit*)(rdata->data))->isRootSplit = TRUE;
|
||||
((ginxlogSplit*)(rdata->data))->rrlink = InvalidBlockNumber;
|
||||
((ginxlogSplit *) (rdata->data))->isRootSplit = TRUE;
|
||||
((ginxlogSplit *) (rdata->data))->rrlink = InvalidBlockNumber;
|
||||
|
||||
|
||||
page = BufferGetPage( stack->buffer );
|
||||
lpage = BufferGetPage( lbuffer );
|
||||
rpage = BufferGetPage( rbuffer );
|
||||
page = BufferGetPage(stack->buffer);
|
||||
lpage = BufferGetPage(lbuffer);
|
||||
rpage = BufferGetPage(rbuffer);
|
||||
|
||||
GinPageGetOpaque(rpage)->rightlink = InvalidBlockNumber;
|
||||
GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
||||
((ginxlogSplit*)(rdata->data))->lblkno = BufferGetBlockNumber(lbuffer);
|
||||
((ginxlogSplit *) (rdata->data))->lblkno = BufferGetBlockNumber(lbuffer);
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
GinInitBuffer( stack->buffer, GinPageGetOpaque(newlpage)->flags & ~GIN_LEAF );
|
||||
PageRestoreTempPage( newlpage, lpage );
|
||||
btree->fillRoot( btree, stack->buffer, lbuffer, rbuffer );
|
||||
if (!btree->index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
GinInitBuffer(stack->buffer, GinPageGetOpaque(newlpage)->flags & ~GIN_LEAF);
|
||||
PageRestoreTempPage(newlpage, lpage);
|
||||
btree->fillRoot(btree, stack->buffer, lbuffer, rbuffer);
|
||||
if (!btree->index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
|
||||
PageSetLSN(page, recptr);
|
||||
@@ -324,23 +372,26 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack) {
|
||||
UnlockReleaseBuffer(stack->buffer);
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
return;
|
||||
} else {
|
||||
/* split non-root page */
|
||||
((ginxlogSplit*)(rdata->data))->isRootSplit = FALSE;
|
||||
((ginxlogSplit*)(rdata->data))->rrlink = savedRightLink;
|
||||
|
||||
lpage = BufferGetPage( stack->buffer );
|
||||
rpage = BufferGetPage( rbuffer );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* split non-root page */
|
||||
((ginxlogSplit *) (rdata->data))->isRootSplit = FALSE;
|
||||
((ginxlogSplit *) (rdata->data))->rrlink = savedRightLink;
|
||||
|
||||
lpage = BufferGetPage(stack->buffer);
|
||||
rpage = BufferGetPage(rbuffer);
|
||||
|
||||
GinPageGetOpaque(rpage)->rightlink = savedRightLink;
|
||||
GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
||||
|
||||
START_CRIT_SECTION();
|
||||
PageRestoreTempPage( newlpage, lpage );
|
||||
if (!btree->index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
PageRestoreTempPage(newlpage, lpage);
|
||||
if (!btree->index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
|
||||
PageSetLSN(lpage, recptr);
|
||||
@@ -350,7 +401,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack) {
|
||||
}
|
||||
MarkBufferDirty(rbuffer);
|
||||
UnlockReleaseBuffer(rbuffer);
|
||||
MarkBufferDirty( stack->buffer );
|
||||
MarkBufferDirty(stack->buffer);
|
||||
END_CRIT_SECTION();
|
||||
}
|
||||
}
|
||||
@@ -361,31 +412,33 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack) {
|
||||
LockBuffer(parent->buffer, GIN_EXCLUSIVE);
|
||||
|
||||
/* move right if it's needed */
|
||||
page = BufferGetPage( parent->buffer );
|
||||
while( (parent->off=btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber ) {
|
||||
page = BufferGetPage(parent->buffer);
|
||||
while ((parent->off = btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber)
|
||||
{
|
||||
BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
LockBuffer(parent->buffer, GIN_UNLOCK);
|
||||
|
||||
if ( rightlink==InvalidBlockNumber ) {
|
||||
/* rightmost page, but we don't find parent, we should
|
||||
use plain search... */
|
||||
if (rightlink == InvalidBlockNumber)
|
||||
{
|
||||
/*
|
||||
* rightmost page, but we don't find parent, we should use
|
||||
* plain search...
|
||||
*/
|
||||
findParents(btree, stack, rootBlkno);
|
||||
parent=stack->parent;
|
||||
page = BufferGetPage( parent->buffer );
|
||||
parent = stack->parent;
|
||||
page = BufferGetPage(parent->buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
parent->blkno = rightlink;
|
||||
parent->buffer = ReleaseAndReadBuffer(parent->buffer, btree->index, parent->blkno);
|
||||
LockBuffer(parent->buffer, GIN_EXCLUSIVE);
|
||||
page = BufferGetPage( parent->buffer );
|
||||
LockBuffer(parent->buffer, GIN_EXCLUSIVE);
|
||||
page = BufferGetPage(parent->buffer);
|
||||
}
|
||||
|
||||
UnlockReleaseBuffer(stack->buffer);
|
||||
pfree( stack );
|
||||
pfree(stack);
|
||||
stack = parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginbulk.c
|
||||
* routines for fast build of inverted index
|
||||
* routines for fast build of inverted index
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.5 2006/08/29 14:05:44 teodor Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.6 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
#define DEF_NPTR 4
|
||||
|
||||
void
|
||||
ginInitBA(BuildAccumulator *accum) {
|
||||
ginInitBA(BuildAccumulator *accum)
|
||||
{
|
||||
accum->maxdepth = 1;
|
||||
accum->stackpos = 0;
|
||||
accum->entries = NULL;
|
||||
@@ -31,11 +32,13 @@ ginInitBA(BuildAccumulator *accum) {
|
||||
accum->entryallocator = NULL;
|
||||
}
|
||||
|
||||
static EntryAccumulator*
|
||||
EAAllocate( BuildAccumulator *accum ) {
|
||||
if ( accum->entryallocator == NULL || accum->length>=DEF_NENTRY ) {
|
||||
accum->entryallocator = palloc(sizeof(EntryAccumulator)*DEF_NENTRY);
|
||||
accum->allocatedMemory += sizeof(EntryAccumulator)*DEF_NENTRY;
|
||||
static EntryAccumulator *
|
||||
EAAllocate(BuildAccumulator *accum)
|
||||
{
|
||||
if (accum->entryallocator == NULL || accum->length >= DEF_NENTRY)
|
||||
{
|
||||
accum->entryallocator = palloc(sizeof(EntryAccumulator) * DEF_NENTRY);
|
||||
accum->allocatedMemory += sizeof(EntryAccumulator) * DEF_NENTRY;
|
||||
accum->length = 0;
|
||||
}
|
||||
|
||||
@@ -48,24 +51,27 @@ EAAllocate( BuildAccumulator *accum ) {
|
||||
* item pointer are ordered
|
||||
*/
|
||||
static void
|
||||
ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr) {
|
||||
if ( entry->number >= entry->length ) {
|
||||
ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr)
|
||||
{
|
||||
if (entry->number >= entry->length)
|
||||
{
|
||||
accum->allocatedMemory += sizeof(ItemPointerData) * entry->length;
|
||||
entry->length *= 2;
|
||||
entry->list = (ItemPointerData*)repalloc(entry->list,
|
||||
sizeof(ItemPointerData)*entry->length);
|
||||
entry->list = (ItemPointerData *) repalloc(entry->list,
|
||||
sizeof(ItemPointerData) * entry->length);
|
||||
}
|
||||
|
||||
if ( entry->shouldSort==FALSE ) {
|
||||
int res = compareItemPointers( entry->list + entry->number - 1, heapptr );
|
||||
if (entry->shouldSort == FALSE)
|
||||
{
|
||||
int res = compareItemPointers(entry->list + entry->number - 1, heapptr);
|
||||
|
||||
Assert( res != 0 );
|
||||
Assert(res != 0);
|
||||
|
||||
if ( res > 0 )
|
||||
entry->shouldSort=TRUE;
|
||||
if (res > 0)
|
||||
entry->shouldSort = TRUE;
|
||||
}
|
||||
|
||||
entry->list[ entry->number ] = *heapptr;
|
||||
entry->list[entry->number] = *heapptr;
|
||||
entry->number++;
|
||||
}
|
||||
|
||||
@@ -74,7 +80,8 @@ ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heap
|
||||
* to avoid computing the datum size twice.
|
||||
*/
|
||||
static Datum
|
||||
getDatumCopy(BuildAccumulator *accum, Datum value) {
|
||||
getDatumCopy(BuildAccumulator *accum, Datum value)
|
||||
{
|
||||
Form_pg_attribute *att = accum->ginstate->tupdesc->attrs;
|
||||
Datum res;
|
||||
|
||||
@@ -100,51 +107,58 @@ getDatumCopy(BuildAccumulator *accum, Datum value) {
|
||||
* Find/store one entry from indexed value.
|
||||
*/
|
||||
static void
|
||||
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry) {
|
||||
EntryAccumulator *ea = accum->entries, *pea = NULL;
|
||||
int res = 0;
|
||||
uint32 depth = 1;
|
||||
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
|
||||
{
|
||||
EntryAccumulator *ea = accum->entries,
|
||||
*pea = NULL;
|
||||
int res = 0;
|
||||
uint32 depth = 1;
|
||||
|
||||
while( ea ) {
|
||||
while (ea)
|
||||
{
|
||||
res = compareEntries(accum->ginstate, entry, ea->value);
|
||||
if ( res == 0 )
|
||||
break; /* found */
|
||||
else {
|
||||
if (res == 0)
|
||||
break; /* found */
|
||||
else
|
||||
{
|
||||
pea = ea;
|
||||
if ( res < 0 )
|
||||
if (res < 0)
|
||||
ea = ea->left;
|
||||
else
|
||||
ea = ea->right;
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
|
||||
if ( depth > accum->maxdepth )
|
||||
|
||||
if (depth > accum->maxdepth)
|
||||
accum->maxdepth = depth;
|
||||
|
||||
if ( ea == NULL ) {
|
||||
if (ea == NULL)
|
||||
{
|
||||
ea = EAAllocate(accum);
|
||||
|
||||
ea->left = ea->right = NULL;
|
||||
ea->value = getDatumCopy(accum, entry);
|
||||
ea->value = getDatumCopy(accum, entry);
|
||||
ea->length = DEF_NPTR;
|
||||
ea->number = 1;
|
||||
ea->shouldSort = FALSE;
|
||||
ea->list = (ItemPointerData*)palloc(sizeof(ItemPointerData)*DEF_NPTR);
|
||||
ea->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
|
||||
ea->list[0] = *heapptr;
|
||||
accum->allocatedMemory += sizeof(ItemPointerData)*DEF_NPTR;
|
||||
accum->allocatedMemory += sizeof(ItemPointerData) * DEF_NPTR;
|
||||
|
||||
if ( pea == NULL )
|
||||
if (pea == NULL)
|
||||
accum->entries = ea;
|
||||
else {
|
||||
Assert( res != 0 );
|
||||
if ( res < 0 )
|
||||
else
|
||||
{
|
||||
Assert(res != 0);
|
||||
if (res < 0)
|
||||
pea->left = ea;
|
||||
else
|
||||
pea->right = ea;
|
||||
}
|
||||
} else
|
||||
ginInsertData( accum, ea, heapptr );
|
||||
}
|
||||
else
|
||||
ginInsertData(accum, ea, heapptr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -152,22 +166,23 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry) {
|
||||
* then calls itself for each parts
|
||||
*/
|
||||
static void
|
||||
ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry,
|
||||
uint32 low, uint32 high, uint32 offset) {
|
||||
uint32 pos;
|
||||
uint32 middle = (low+high)>>1;
|
||||
ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry,
|
||||
uint32 low, uint32 high, uint32 offset)
|
||||
{
|
||||
uint32 pos;
|
||||
uint32 middle = (low + high) >> 1;
|
||||
|
||||
pos = (low+middle)>>1;
|
||||
if ( low!=middle && pos>=offset && pos-offset < nentry )
|
||||
ginInsertEntry( accum, heapptr, entries[ pos-offset ]);
|
||||
pos = (high+middle+1)>>1;
|
||||
if ( middle+1 != high && pos>=offset && pos-offset < nentry )
|
||||
ginInsertEntry( accum, heapptr, entries[ pos-offset ]);
|
||||
pos = (low + middle) >> 1;
|
||||
if (low != middle && pos >= offset && pos - offset < nentry)
|
||||
ginInsertEntry(accum, heapptr, entries[pos - offset]);
|
||||
pos = (high + middle + 1) >> 1;
|
||||
if (middle + 1 != high && pos >= offset && pos - offset < nentry)
|
||||
ginInsertEntry(accum, heapptr, entries[pos - offset]);
|
||||
|
||||
if ( low!=middle )
|
||||
ginChooseElem(accum, heapptr, entries, nentry, low, middle, offset );
|
||||
if ( high!=middle+1 )
|
||||
ginChooseElem(accum, heapptr, entries, nentry, middle+1, high, offset );
|
||||
if (low != middle)
|
||||
ginChooseElem(accum, heapptr, entries, nentry, low, middle, offset);
|
||||
if (high != middle + 1)
|
||||
ginChooseElem(accum, heapptr, entries, nentry, middle + 1, high, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -176,56 +191,71 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
|
||||
* next middle on left part and middle of right part.
|
||||
*/
|
||||
void
|
||||
ginInsertRecordBA( BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry ) {
|
||||
uint32 i, nbit=0, offset;
|
||||
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry)
|
||||
{
|
||||
uint32 i,
|
||||
nbit = 0,
|
||||
offset;
|
||||
|
||||
if (nentry==0)
|
||||
if (nentry == 0)
|
||||
return;
|
||||
|
||||
i=nentry-1;
|
||||
for(;i>0;i>>=1) nbit++;
|
||||
i = nentry - 1;
|
||||
for (; i > 0; i >>= 1)
|
||||
nbit++;
|
||||
|
||||
nbit = 1<<nbit;
|
||||
offset = (nbit-nentry)/2;
|
||||
nbit = 1 << nbit;
|
||||
offset = (nbit - nentry) / 2;
|
||||
|
||||
ginInsertEntry( accum, heapptr, entries[ (nbit>>1)-offset ]);
|
||||
ginInsertEntry(accum, heapptr, entries[(nbit >> 1) - offset]);
|
||||
ginChooseElem(accum, heapptr, entries, nentry, 0, nbit, offset);
|
||||
}
|
||||
|
||||
static int
|
||||
qsortCompareItemPointers( const void *a, const void *b ) {
|
||||
int res = compareItemPointers( (ItemPointer)a, (ItemPointer)b );
|
||||
Assert( res!=0 );
|
||||
static int
|
||||
qsortCompareItemPointers(const void *a, const void *b)
|
||||
{
|
||||
int res = compareItemPointers((ItemPointer) a, (ItemPointer) b);
|
||||
|
||||
Assert(res != 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* walk on binary tree and returns ordered nodes
|
||||
*/
|
||||
static EntryAccumulator*
|
||||
walkTree( BuildAccumulator *accum ) {
|
||||
EntryAccumulator *entry = accum->stack[ accum->stackpos ];
|
||||
* walk on binary tree and returns ordered nodes
|
||||
*/
|
||||
static EntryAccumulator *
|
||||
walkTree(BuildAccumulator *accum)
|
||||
{
|
||||
EntryAccumulator *entry = accum->stack[accum->stackpos];
|
||||
|
||||
if ( entry->list != NULL ) {
|
||||
if (entry->list != NULL)
|
||||
{
|
||||
/* return entry itself: we already was at left sublink */
|
||||
return entry;
|
||||
} else if ( entry->right && entry->right != accum->stack[ accum->stackpos+1 ] ) {
|
||||
}
|
||||
else if (entry->right && entry->right != accum->stack[accum->stackpos + 1])
|
||||
{
|
||||
/* go on right sublink */
|
||||
accum->stackpos++;
|
||||
entry = entry->right;
|
||||
|
||||
/* find most-left value */
|
||||
for(;;) {
|
||||
accum->stack[ accum->stackpos ] = entry;
|
||||
if ( entry->left ) {
|
||||
for (;;)
|
||||
{
|
||||
accum->stack[accum->stackpos] = entry;
|
||||
if (entry->left)
|
||||
{
|
||||
accum->stackpos++;
|
||||
entry = entry->left;
|
||||
} else
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we already return all left subtree, itself and right subtree */
|
||||
if ( accum->stackpos == 0 )
|
||||
if (accum->stackpos == 0)
|
||||
return 0;
|
||||
accum->stackpos--;
|
||||
return walkTree(accum);
|
||||
@@ -234,47 +264,53 @@ walkTree( BuildAccumulator *accum ) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
ItemPointerData*
|
||||
ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n) {
|
||||
EntryAccumulator *entry;
|
||||
ItemPointerData *
|
||||
ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
|
||||
{
|
||||
EntryAccumulator *entry;
|
||||
ItemPointerData *list;
|
||||
|
||||
|
||||
if ( accum->stack == NULL ) {
|
||||
if (accum->stack == NULL)
|
||||
{
|
||||
/* first call */
|
||||
accum->stack = palloc0(sizeof(EntryAccumulator*)*(accum->maxdepth+1));
|
||||
accum->stack = palloc0(sizeof(EntryAccumulator *) * (accum->maxdepth + 1));
|
||||
entry = accum->entries;
|
||||
|
||||
if ( entry == NULL )
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
|
||||
/* find most-left value */
|
||||
for(;;) {
|
||||
accum->stack[ accum->stackpos ] = entry;
|
||||
if ( entry->left ) {
|
||||
for (;;)
|
||||
{
|
||||
accum->stack[accum->stackpos] = entry;
|
||||
if (entry->left)
|
||||
{
|
||||
accum->stackpos++;
|
||||
entry = entry->left;
|
||||
} else
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pfree( accum->stack[ accum->stackpos ]->list );
|
||||
accum->stack[ accum->stackpos ]->list = NULL;
|
||||
entry = walkTree( accum );
|
||||
}
|
||||
else
|
||||
{
|
||||
pfree(accum->stack[accum->stackpos]->list);
|
||||
accum->stack[accum->stackpos]->list = NULL;
|
||||
entry = walkTree(accum);
|
||||
}
|
||||
|
||||
if ( entry == NULL )
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
|
||||
*n = entry->number;
|
||||
*value = entry->value;
|
||||
list = entry->list;
|
||||
*n = entry->number;
|
||||
*value = entry->value;
|
||||
list = entry->list;
|
||||
|
||||
Assert(list != NULL);
|
||||
|
||||
if ( entry->shouldSort && entry->number > 1 )
|
||||
if (entry->shouldSort && entry->number > 1)
|
||||
qsort(list, *n, sizeof(ItemPointerData), qsortCompareItemPointers);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* gindatapage.c
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/gindatapage.c,v 1.3 2006/07/16 00:52:05 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/gindatapage.c,v 1.4 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -16,50 +16,56 @@
|
||||
#include "access/gin.h"
|
||||
|
||||
int
|
||||
compareItemPointers( ItemPointer a, ItemPointer b ) {
|
||||
if ( GinItemPointerGetBlockNumber(a) == GinItemPointerGetBlockNumber(b) ) {
|
||||
if ( GinItemPointerGetOffsetNumber(a) == GinItemPointerGetOffsetNumber(b) )
|
||||
compareItemPointers(ItemPointer a, ItemPointer b)
|
||||
{
|
||||
if (GinItemPointerGetBlockNumber(a) == GinItemPointerGetBlockNumber(b))
|
||||
{
|
||||
if (GinItemPointerGetOffsetNumber(a) == GinItemPointerGetOffsetNumber(b))
|
||||
return 0;
|
||||
return ( GinItemPointerGetOffsetNumber(a) > GinItemPointerGetOffsetNumber(b) ) ? 1 : -1;
|
||||
}
|
||||
return (GinItemPointerGetOffsetNumber(a) > GinItemPointerGetOffsetNumber(b)) ? 1 : -1;
|
||||
}
|
||||
|
||||
return ( GinItemPointerGetBlockNumber(a) > GinItemPointerGetBlockNumber(b) ) ? 1 : -1;
|
||||
return (GinItemPointerGetBlockNumber(a) > GinItemPointerGetBlockNumber(b)) ? 1 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge two ordered array of itempointer
|
||||
*/
|
||||
void
|
||||
MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb) {
|
||||
void
|
||||
MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb)
|
||||
{
|
||||
ItemPointerData *dptr = dst;
|
||||
ItemPointerData *aptr = a, *bptr = b;
|
||||
ItemPointerData *aptr = a,
|
||||
*bptr = b;
|
||||
|
||||
while( aptr - a < na && bptr - b < nb ) {
|
||||
if ( compareItemPointers(aptr, bptr) > 0 )
|
||||
while (aptr - a < na && bptr - b < nb)
|
||||
{
|
||||
if (compareItemPointers(aptr, bptr) > 0)
|
||||
*dptr++ = *bptr++;
|
||||
else
|
||||
*dptr++ = *aptr++;
|
||||
}
|
||||
|
||||
while( aptr - a < na )
|
||||
while (aptr - a < na)
|
||||
*dptr++ = *aptr++;
|
||||
|
||||
while( bptr - b < nb )
|
||||
while (bptr - b < nb)
|
||||
*dptr++ = *bptr++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks, should we move to right link...
|
||||
* Checks, should we move to right link...
|
||||
* Compares inserting itemp pointer with right bound of current page
|
||||
*/
|
||||
static bool
|
||||
dataIsMoveRight(GinBtree btree, Page page) {
|
||||
ItemPointer iptr = GinDataPageGetRightBound(page);
|
||||
dataIsMoveRight(GinBtree btree, Page page)
|
||||
{
|
||||
ItemPointer iptr = GinDataPageGetRightBound(page);
|
||||
|
||||
if ( GinPageRightMost(page) )
|
||||
return FALSE;
|
||||
if (GinPageRightMost(page))
|
||||
return FALSE;
|
||||
|
||||
return ( compareItemPointers( btree->items + btree->curitem, iptr ) > 0 ) ? TRUE : FALSE;
|
||||
return (compareItemPointers(btree->items + btree->curitem, iptr) > 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -67,94 +73,113 @@ dataIsMoveRight(GinBtree btree, Page page) {
|
||||
* page correctly choosen and searching value SHOULD be on page
|
||||
*/
|
||||
static BlockNumber
|
||||
dataLocateItem(GinBtree btree, GinBtreeStack *stack) {
|
||||
OffsetNumber low, high, maxoff;
|
||||
PostingItem *pitem=NULL;
|
||||
int result;
|
||||
Page page = BufferGetPage( stack->buffer );
|
||||
dataLocateItem(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
OffsetNumber low,
|
||||
high,
|
||||
maxoff;
|
||||
PostingItem *pitem = NULL;
|
||||
int result;
|
||||
Page page = BufferGetPage(stack->buffer);
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( btree->fullScan ) {
|
||||
if (btree->fullScan)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
stack->predictNumber *= GinPageGetOpaque(page)->maxoff;
|
||||
return btree->getLeftMostPage(btree, page);
|
||||
}
|
||||
|
||||
low = FirstOffsetNumber;
|
||||
maxoff = high = GinPageGetOpaque(page)->maxoff;
|
||||
Assert( high >= low );
|
||||
maxoff = high = GinPageGetOpaque(page)->maxoff;
|
||||
Assert(high >= low);
|
||||
|
||||
high++;
|
||||
|
||||
while (high > low) {
|
||||
while (high > low)
|
||||
{
|
||||
OffsetNumber mid = low + ((high - low) / 2);
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page,mid);
|
||||
|
||||
if ( mid == maxoff )
|
||||
/* Right infinity, page already correctly choosen
|
||||
with a help of dataIsMoveRight */
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, mid);
|
||||
|
||||
if (mid == maxoff)
|
||||
|
||||
/*
|
||||
* Right infinity, page already correctly choosen with a help of
|
||||
* dataIsMoveRight
|
||||
*/
|
||||
result = -1;
|
||||
else {
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page,mid);
|
||||
result = compareItemPointers( btree->items + btree->curitem, &( pitem->key ) );
|
||||
else
|
||||
{
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, mid);
|
||||
result = compareItemPointers(btree->items + btree->curitem, &(pitem->key));
|
||||
}
|
||||
|
||||
if ( result == 0 ) {
|
||||
if (result == 0)
|
||||
{
|
||||
stack->off = mid;
|
||||
return PostingItemGetBlockNumber(pitem);
|
||||
} else if ( result > 0 )
|
||||
}
|
||||
else if (result > 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
Assert( high>=FirstOffsetNumber && high <= maxoff );
|
||||
Assert(high >= FirstOffsetNumber && high <= maxoff);
|
||||
|
||||
stack->off = high;
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page,high);
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, high);
|
||||
return PostingItemGetBlockNumber(pitem);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Searches correct position for value on leaf page.
|
||||
* Page should be corrrectly choosen.
|
||||
* Page should be corrrectly choosen.
|
||||
* Returns true if value found on page.
|
||||
*/
|
||||
static bool
|
||||
dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack) {
|
||||
Page page = BufferGetPage( stack->buffer );
|
||||
OffsetNumber low, high;
|
||||
int result;
|
||||
dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
Page page = BufferGetPage(stack->buffer);
|
||||
OffsetNumber low,
|
||||
high;
|
||||
int result;
|
||||
|
||||
Assert( GinPageIsLeaf(page) );
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(GinPageIsLeaf(page));
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( btree->fullScan ) {
|
||||
if (btree->fullScan)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
low=FirstOffsetNumber;
|
||||
low = FirstOffsetNumber;
|
||||
high = GinPageGetOpaque(page)->maxoff;
|
||||
|
||||
if ( high < low ) {
|
||||
if (high < low)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
return false;
|
||||
}
|
||||
|
||||
high++;
|
||||
|
||||
while (high > low) {
|
||||
while (high > low)
|
||||
{
|
||||
OffsetNumber mid = low + ((high - low) / 2);
|
||||
|
||||
result = compareItemPointers( btree->items + btree->curitem, (ItemPointer)GinDataPageGetItem(page,mid) );
|
||||
result = compareItemPointers(btree->items + btree->curitem, (ItemPointer) GinDataPageGetItem(page, mid));
|
||||
|
||||
if ( result == 0 ) {
|
||||
if (result == 0)
|
||||
{
|
||||
stack->off = mid;
|
||||
return true;
|
||||
} else if ( result > 0 )
|
||||
}
|
||||
else if (result > 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
@@ -169,34 +194,41 @@ dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack) {
|
||||
* offset of PostingItem
|
||||
*/
|
||||
static OffsetNumber
|
||||
dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff) {
|
||||
OffsetNumber i, maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
|
||||
{
|
||||
OffsetNumber i,
|
||||
maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
PostingItem *pitem;
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
/* if page isn't changed, we returns storedOff */
|
||||
if ( storedOff>= FirstOffsetNumber && storedOff<=maxoff) {
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page, storedOff);
|
||||
if ( PostingItemGetBlockNumber(pitem) == blkno )
|
||||
if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
|
||||
{
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, storedOff);
|
||||
if (PostingItemGetBlockNumber(pitem) == blkno)
|
||||
return storedOff;
|
||||
|
||||
/* we hope, that needed pointer goes to right. It's true
|
||||
if there wasn't a deletion */
|
||||
for( i=storedOff+1 ; i <= maxoff ; i++ ) {
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page, i);
|
||||
if ( PostingItemGetBlockNumber(pitem) == blkno )
|
||||
/*
|
||||
* we hope, that needed pointer goes to right. It's true if there
|
||||
* wasn't a deletion
|
||||
*/
|
||||
for (i = storedOff + 1; i <= maxoff; i++)
|
||||
{
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, i);
|
||||
if (PostingItemGetBlockNumber(pitem) == blkno)
|
||||
return i;
|
||||
}
|
||||
|
||||
maxoff = storedOff-1;
|
||||
maxoff = storedOff - 1;
|
||||
}
|
||||
|
||||
/* last chance */
|
||||
for( i=FirstOffsetNumber; i <= maxoff ; i++ ) {
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page, i);
|
||||
if ( PostingItemGetBlockNumber(pitem) == blkno )
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
||||
{
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, i);
|
||||
if (PostingItemGetBlockNumber(pitem) == blkno)
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -207,14 +239,15 @@ dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber stor
|
||||
* retunrs blkno of lefmost child
|
||||
*/
|
||||
static BlockNumber
|
||||
dataGetLeftMostPage(GinBtree btree, Page page) {
|
||||
dataGetLeftMostPage(GinBtree btree, Page page)
|
||||
{
|
||||
PostingItem *pitem;
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert( GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(GinPageIsData(page));
|
||||
Assert(GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber);
|
||||
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page, FirstOffsetNumber);
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, FirstOffsetNumber);
|
||||
return PostingItemGetBlockNumber(pitem);
|
||||
}
|
||||
|
||||
@@ -223,18 +256,22 @@ dataGetLeftMostPage(GinBtree btree, Page page) {
|
||||
* correct value! depending on leaf or non-leaf page
|
||||
*/
|
||||
void
|
||||
GinDataPageAddItem( Page page, void *data, OffsetNumber offset ) {
|
||||
GinDataPageAddItem(Page page, void *data, OffsetNumber offset)
|
||||
{
|
||||
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
char *ptr;
|
||||
char *ptr;
|
||||
|
||||
if ( offset == InvalidOffsetNumber ) {
|
||||
ptr = GinDataPageGetItem(page,maxoff+1);
|
||||
} else {
|
||||
ptr = GinDataPageGetItem(page,offset);
|
||||
if ( maxoff+1-offset != 0 )
|
||||
memmove( ptr+GinSizeOfItem(page), ptr, (maxoff-offset+1) * GinSizeOfItem(page) );
|
||||
if (offset == InvalidOffsetNumber)
|
||||
{
|
||||
ptr = GinDataPageGetItem(page, maxoff + 1);
|
||||
}
|
||||
memcpy( ptr, data, GinSizeOfItem(page) );
|
||||
else
|
||||
{
|
||||
ptr = GinDataPageGetItem(page, offset);
|
||||
if (maxoff + 1 - offset != 0)
|
||||
memmove(ptr + GinSizeOfItem(page), ptr, (maxoff - offset + 1) * GinSizeOfItem(page));
|
||||
}
|
||||
memcpy(ptr, data, GinSizeOfItem(page));
|
||||
|
||||
GinPageGetOpaque(page)->maxoff++;
|
||||
}
|
||||
@@ -243,15 +280,16 @@ GinDataPageAddItem( Page page, void *data, OffsetNumber offset ) {
|
||||
* Deletes posting item from non-leaf page
|
||||
*/
|
||||
void
|
||||
PageDeletePostingItem(Page page, OffsetNumber offset) {
|
||||
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
PageDeletePostingItem(Page page, OffsetNumber offset)
|
||||
{
|
||||
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( offset>=FirstOffsetNumber && offset <= maxoff );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(offset >= FirstOffsetNumber && offset <= maxoff);
|
||||
|
||||
if ( offset != maxoff )
|
||||
memmove( GinDataPageGetItem(page,offset), GinDataPageGetItem(page,offset+1),
|
||||
sizeof(PostingItem) * (maxoff-offset) );
|
||||
if (offset != maxoff)
|
||||
memmove(GinDataPageGetItem(page, offset), GinDataPageGetItem(page, offset + 1),
|
||||
sizeof(PostingItem) * (maxoff - offset));
|
||||
|
||||
GinPageGetOpaque(page)->maxoff--;
|
||||
}
|
||||
@@ -261,19 +299,24 @@ PageDeletePostingItem(Page page, OffsetNumber offset) {
|
||||
* item pointer never deletes!
|
||||
*/
|
||||
static bool
|
||||
dataIsEnoughSpace( GinBtree btree, Buffer buf, OffsetNumber off ) {
|
||||
Page page = BufferGetPage(buf);
|
||||
dataIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off)
|
||||
{
|
||||
Page page = BufferGetPage(buf);
|
||||
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert( !btree->isDelete );
|
||||
Assert(GinPageIsData(page));
|
||||
Assert(!btree->isDelete);
|
||||
|
||||
if ( GinPageIsLeaf(page) ) {
|
||||
if ( GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff ) {
|
||||
if ( (btree->nitem - btree->curitem) * sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page) )
|
||||
if (GinPageIsLeaf(page))
|
||||
{
|
||||
if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
|
||||
{
|
||||
if ((btree->nitem - btree->curitem) * sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
|
||||
return true;
|
||||
} else if ( sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page) )
|
||||
}
|
||||
else if (sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
|
||||
return true;
|
||||
} else if ( sizeof(PostingItem) <= GinDataPageGetFreeSpace(page) )
|
||||
}
|
||||
else if (sizeof(PostingItem) <= GinDataPageGetFreeSpace(page))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -285,14 +328,17 @@ dataIsEnoughSpace( GinBtree btree, Buffer buf, OffsetNumber off ) {
|
||||
* item pointer never deletes!
|
||||
*/
|
||||
static BlockNumber
|
||||
dataPrepareData( GinBtree btree, Page page, OffsetNumber off) {
|
||||
dataPrepareData(GinBtree btree, Page page, OffsetNumber off)
|
||||
{
|
||||
BlockNumber ret = InvalidBlockNumber;
|
||||
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( !GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber ) {
|
||||
PostingItem *pitem = (PostingItem*)GinDataPageGetItem(page,off);
|
||||
PostingItemSetBlockNumber( pitem, btree->rightblkno );
|
||||
if (!GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber)
|
||||
{
|
||||
PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, off);
|
||||
|
||||
PostingItemSetBlockNumber(pitem, btree->rightblkno);
|
||||
ret = btree->rightblkno;
|
||||
}
|
||||
|
||||
@@ -301,24 +347,25 @@ dataPrepareData( GinBtree btree, Page page, OffsetNumber off) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Places keys to page and fills WAL record. In case leaf page and
|
||||
* build mode puts all ItemPointers to page.
|
||||
*/
|
||||
static void
|
||||
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata) {
|
||||
Page page = BufferGetPage(buf);
|
||||
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
|
||||
{
|
||||
Page page = BufferGetPage(buf);
|
||||
static XLogRecData rdata[3];
|
||||
int sizeofitem = GinSizeOfItem(page);
|
||||
static ginxlogInsert data;
|
||||
int sizeofitem = GinSizeOfItem(page);
|
||||
static ginxlogInsert data;
|
||||
|
||||
*prdata = rdata;
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
data.updateBlkno = dataPrepareData( btree, page, off );
|
||||
data.updateBlkno = dataPrepareData(btree, page, off);
|
||||
|
||||
data.node = btree->index->rd_node;
|
||||
data.blkno = BufferGetBlockNumber( buf );
|
||||
data.blkno = BufferGetBlockNumber(buf);
|
||||
data.offset = off;
|
||||
data.nitem = 1;
|
||||
data.isDelete = FALSE;
|
||||
@@ -337,109 +384,124 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
|
||||
rdata[1].next = &rdata[2];
|
||||
|
||||
rdata[2].buffer = InvalidBuffer;
|
||||
rdata[2].data = (GinPageIsLeaf(page)) ? ((char*)(btree->items+btree->curitem)) : ((char*)&(btree->pitem));
|
||||
rdata[2].data = (GinPageIsLeaf(page)) ? ((char *) (btree->items + btree->curitem)) : ((char *) &(btree->pitem));
|
||||
rdata[2].len = sizeofitem;
|
||||
rdata[2].next = NULL;
|
||||
|
||||
if ( GinPageIsLeaf(page) ) {
|
||||
if ( GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff ) {
|
||||
if (GinPageIsLeaf(page))
|
||||
{
|
||||
if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
|
||||
{
|
||||
/* usually, create index... */
|
||||
uint32 savedPos = btree->curitem;
|
||||
uint32 savedPos = btree->curitem;
|
||||
|
||||
while( btree->curitem < btree->nitem ) {
|
||||
GinDataPageAddItem(page, btree->items+btree->curitem, off);
|
||||
while (btree->curitem < btree->nitem)
|
||||
{
|
||||
GinDataPageAddItem(page, btree->items + btree->curitem, off);
|
||||
off++;
|
||||
btree->curitem++;
|
||||
}
|
||||
data.nitem = btree->curitem-savedPos;
|
||||
data.nitem = btree->curitem - savedPos;
|
||||
rdata[2].len = sizeofitem * data.nitem;
|
||||
} else {
|
||||
GinDataPageAddItem(page, btree->items+btree->curitem, off);
|
||||
}
|
||||
else
|
||||
{
|
||||
GinDataPageAddItem(page, btree->items + btree->curitem, off);
|
||||
btree->curitem++;
|
||||
}
|
||||
} else
|
||||
GinDataPageAddItem(page, &(btree->pitem), off);
|
||||
}
|
||||
else
|
||||
GinDataPageAddItem(page, &(btree->pitem), off);
|
||||
}
|
||||
|
||||
/*
|
||||
* split page and fills WAL record. original buffer(lbuf) leaves untouched,
|
||||
* returns shadow page of lbuf filled new data. In leaf page and build mode puts all
|
||||
* returns shadow page of lbuf filled new data. In leaf page and build mode puts all
|
||||
* ItemPointers to pages. Also, in build mode splits data by way to full fulled
|
||||
* left page
|
||||
*/
|
||||
static Page
|
||||
dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata) {
|
||||
dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
|
||||
{
|
||||
static ginxlogSplit data;
|
||||
static XLogRecData rdata[4];
|
||||
static char vector[2*BLCKSZ];
|
||||
char *ptr;
|
||||
static char vector[2 * BLCKSZ];
|
||||
char *ptr;
|
||||
OffsetNumber separator;
|
||||
ItemPointer bound;
|
||||
Page lpage = GinPageGetCopyPage( BufferGetPage( lbuf ) );
|
||||
ItemPointerData oldbound = *GinDataPageGetRightBound(lpage);
|
||||
int sizeofitem = GinSizeOfItem(lpage);
|
||||
ItemPointer bound;
|
||||
Page lpage = GinPageGetCopyPage(BufferGetPage(lbuf));
|
||||
ItemPointerData oldbound = *GinDataPageGetRightBound(lpage);
|
||||
int sizeofitem = GinSizeOfItem(lpage);
|
||||
OffsetNumber maxoff = GinPageGetOpaque(lpage)->maxoff;
|
||||
Page rpage = BufferGetPage( rbuf );
|
||||
Size pageSize = PageGetPageSize( lpage );
|
||||
Size freeSpace;
|
||||
uint32 nCopied = 1;
|
||||
Page rpage = BufferGetPage(rbuf);
|
||||
Size pageSize = PageGetPageSize(lpage);
|
||||
Size freeSpace;
|
||||
uint32 nCopied = 1;
|
||||
|
||||
GinInitPage( rpage, GinPageGetOpaque(lpage)->flags, pageSize );
|
||||
GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
|
||||
freeSpace = GinDataPageGetFreeSpace(rpage);
|
||||
|
||||
*prdata = rdata;
|
||||
data.leftChildBlkno = ( GinPageIsLeaf(lpage) ) ?
|
||||
InvalidOffsetNumber : PostingItemGetBlockNumber( &(btree->pitem) );
|
||||
data.updateBlkno = dataPrepareData( btree, lpage, off );
|
||||
data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
|
||||
InvalidOffsetNumber : PostingItemGetBlockNumber(&(btree->pitem));
|
||||
data.updateBlkno = dataPrepareData(btree, lpage, off);
|
||||
|
||||
memcpy(vector, GinDataPageGetItem(lpage, FirstOffsetNumber),
|
||||
maxoff*sizeofitem);
|
||||
memcpy(vector, GinDataPageGetItem(lpage, FirstOffsetNumber),
|
||||
maxoff * sizeofitem);
|
||||
|
||||
if ( GinPageIsLeaf(lpage) && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff ) {
|
||||
if (GinPageIsLeaf(lpage) && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff)
|
||||
{
|
||||
nCopied = 0;
|
||||
while( btree->curitem < btree->nitem && maxoff*sizeof(ItemPointerData) < 2*(freeSpace - sizeof(ItemPointerData)) ) {
|
||||
memcpy( vector + maxoff*sizeof(ItemPointerData), btree->items+btree->curitem,
|
||||
sizeof(ItemPointerData) );
|
||||
while (btree->curitem < btree->nitem && maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData)))
|
||||
{
|
||||
memcpy(vector + maxoff * sizeof(ItemPointerData), btree->items + btree->curitem,
|
||||
sizeof(ItemPointerData));
|
||||
maxoff++;
|
||||
nCopied++;
|
||||
btree->curitem++;
|
||||
}
|
||||
} else {
|
||||
ptr = vector + (off-1)*sizeofitem;
|
||||
if ( maxoff+1-off != 0 )
|
||||
memmove( ptr+sizeofitem, ptr, (maxoff-off+1) * sizeofitem );
|
||||
if ( GinPageIsLeaf(lpage) ) {
|
||||
memcpy(ptr, btree->items+btree->curitem, sizeofitem );
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = vector + (off - 1) * sizeofitem;
|
||||
if (maxoff + 1 - off != 0)
|
||||
memmove(ptr + sizeofitem, ptr, (maxoff - off + 1) * sizeofitem);
|
||||
if (GinPageIsLeaf(lpage))
|
||||
{
|
||||
memcpy(ptr, btree->items + btree->curitem, sizeofitem);
|
||||
btree->curitem++;
|
||||
} else
|
||||
memcpy(ptr, &(btree->pitem), sizeofitem );
|
||||
|
||||
}
|
||||
else
|
||||
memcpy(ptr, &(btree->pitem), sizeofitem);
|
||||
|
||||
maxoff++;
|
||||
}
|
||||
|
||||
/* we suppose that during index creation table scaned from
|
||||
begin to end, so ItemPointers are monotonically increased.. */
|
||||
if ( btree->isBuild && GinPageRightMost(lpage) )
|
||||
separator=freeSpace/sizeofitem;
|
||||
/*
|
||||
* we suppose that during index creation table scaned from begin to end,
|
||||
* so ItemPointers are monotonically increased..
|
||||
*/
|
||||
if (btree->isBuild && GinPageRightMost(lpage))
|
||||
separator = freeSpace / sizeofitem;
|
||||
else
|
||||
separator=maxoff/2;
|
||||
separator = maxoff / 2;
|
||||
|
||||
GinInitPage( rpage, GinPageGetOpaque(lpage)->flags, pageSize );
|
||||
GinInitPage( lpage, GinPageGetOpaque(rpage)->flags, pageSize );
|
||||
GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
|
||||
GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
|
||||
|
||||
memcpy( GinDataPageGetItem(lpage, FirstOffsetNumber), vector, separator * sizeofitem );
|
||||
memcpy(GinDataPageGetItem(lpage, FirstOffsetNumber), vector, separator * sizeofitem);
|
||||
GinPageGetOpaque(lpage)->maxoff = separator;
|
||||
memcpy( GinDataPageGetItem(rpage, FirstOffsetNumber),
|
||||
vector + separator * sizeofitem, (maxoff-separator) * sizeofitem );
|
||||
GinPageGetOpaque(rpage)->maxoff = maxoff-separator;
|
||||
memcpy(GinDataPageGetItem(rpage, FirstOffsetNumber),
|
||||
vector + separator * sizeofitem, (maxoff - separator) * sizeofitem);
|
||||
GinPageGetOpaque(rpage)->maxoff = maxoff - separator;
|
||||
|
||||
PostingItemSetBlockNumber( &(btree->pitem), BufferGetBlockNumber(lbuf) );
|
||||
if ( GinPageIsLeaf(lpage) )
|
||||
btree->pitem.key = *(ItemPointerData*)GinDataPageGetItem(lpage,
|
||||
GinPageGetOpaque(lpage)->maxoff);
|
||||
else
|
||||
btree->pitem.key = ((PostingItem*)GinDataPageGetItem(lpage,
|
||||
GinPageGetOpaque(lpage)->maxoff))->key;
|
||||
PostingItemSetBlockNumber(&(btree->pitem), BufferGetBlockNumber(lbuf));
|
||||
if (GinPageIsLeaf(lpage))
|
||||
btree->pitem.key = *(ItemPointerData *) GinDataPageGetItem(lpage,
|
||||
GinPageGetOpaque(lpage)->maxoff);
|
||||
else
|
||||
btree->pitem.key = ((PostingItem *) GinDataPageGetItem(lpage,
|
||||
GinPageGetOpaque(lpage)->maxoff))->key;
|
||||
btree->rightblkno = BufferGetBlockNumber(rbuf);
|
||||
|
||||
/* set up right bound for left page */
|
||||
@@ -452,8 +514,8 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
|
||||
|
||||
data.node = btree->index->rd_node;
|
||||
data.rootBlkno = InvalidBlockNumber;
|
||||
data.lblkno = BufferGetBlockNumber( lbuf );
|
||||
data.rblkno = BufferGetBlockNumber( rbuf );
|
||||
data.lblkno = BufferGetBlockNumber(lbuf);
|
||||
data.rblkno = BufferGetBlockNumber(rbuf);
|
||||
data.separator = separator;
|
||||
data.nitem = maxoff;
|
||||
data.isData = TRUE;
|
||||
@@ -468,34 +530,37 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
|
||||
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].data = vector;
|
||||
rdata[1].len = MAXALIGN( maxoff * sizeofitem );
|
||||
rdata[1].len = MAXALIGN(maxoff * sizeofitem);
|
||||
rdata[1].next = NULL;
|
||||
|
||||
return lpage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills new root by right bound values from child.
|
||||
* Fills new root by right bound values from child.
|
||||
* Also called from ginxlog, should not use btree
|
||||
*/
|
||||
void
|
||||
dataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf) {
|
||||
Page page = BufferGetPage(root),
|
||||
lpage = BufferGetPage(lbuf),
|
||||
rpage = BufferGetPage(rbuf);
|
||||
PostingItem li, ri;
|
||||
dataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
|
||||
{
|
||||
Page page = BufferGetPage(root),
|
||||
lpage = BufferGetPage(lbuf),
|
||||
rpage = BufferGetPage(rbuf);
|
||||
PostingItem li,
|
||||
ri;
|
||||
|
||||
li.key = *GinDataPageGetRightBound(lpage);
|
||||
PostingItemSetBlockNumber( &li, BufferGetBlockNumber(lbuf) );
|
||||
GinDataPageAddItem(page, &li, InvalidOffsetNumber );
|
||||
PostingItemSetBlockNumber(&li, BufferGetBlockNumber(lbuf));
|
||||
GinDataPageAddItem(page, &li, InvalidOffsetNumber);
|
||||
|
||||
ri.key = *GinDataPageGetRightBound(rpage);
|
||||
PostingItemSetBlockNumber( &ri, BufferGetBlockNumber(rbuf) );
|
||||
GinDataPageAddItem(page, &ri, InvalidOffsetNumber );
|
||||
PostingItemSetBlockNumber(&ri, BufferGetBlockNumber(rbuf));
|
||||
GinDataPageAddItem(page, &ri, InvalidOffsetNumber);
|
||||
}
|
||||
|
||||
void
|
||||
prepareDataScan( GinBtree btree, Relation index) {
|
||||
prepareDataScan(GinBtree btree, Relation index)
|
||||
{
|
||||
memset(btree, 0, sizeof(GinBtreeData));
|
||||
btree->index = index;
|
||||
btree->isMoveRight = dataIsMoveRight;
|
||||
@@ -509,21 +574,22 @@ prepareDataScan( GinBtree btree, Relation index) {
|
||||
btree->fillRoot = dataFillRoot;
|
||||
|
||||
btree->searchMode = FALSE;
|
||||
btree->isDelete = FALSE;
|
||||
btree->isDelete = FALSE;
|
||||
btree->fullScan = FALSE;
|
||||
btree->isBuild= FALSE;
|
||||
btree->isBuild = FALSE;
|
||||
}
|
||||
|
||||
GinPostingTreeScan*
|
||||
prepareScanPostingTree( Relation index, BlockNumber rootBlkno, bool searchMode) {
|
||||
GinPostingTreeScan *gdi = (GinPostingTreeScan*)palloc0( sizeof(GinPostingTreeScan) );
|
||||
GinPostingTreeScan *
|
||||
prepareScanPostingTree(Relation index, BlockNumber rootBlkno, bool searchMode)
|
||||
{
|
||||
GinPostingTreeScan *gdi = (GinPostingTreeScan *) palloc0(sizeof(GinPostingTreeScan));
|
||||
|
||||
prepareDataScan(&gdi->btree, index);
|
||||
|
||||
prepareDataScan( &gdi->btree, index );
|
||||
|
||||
gdi->btree.searchMode = searchMode;
|
||||
gdi->btree.fullScan = searchMode;
|
||||
|
||||
gdi->stack = ginPrepareFindLeafPage( &gdi->btree, rootBlkno );
|
||||
gdi->stack = ginPrepareFindLeafPage(&gdi->btree, rootBlkno);
|
||||
|
||||
return gdi;
|
||||
}
|
||||
@@ -532,33 +598,35 @@ prepareScanPostingTree( Relation index, BlockNumber rootBlkno, bool searchMode)
|
||||
* Inserts array of item pointers, may execute several tree scan (very rare)
|
||||
*/
|
||||
void
|
||||
insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem) {
|
||||
insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem)
|
||||
{
|
||||
BlockNumber rootBlkno = gdi->stack->blkno;
|
||||
|
||||
gdi->btree.items = items;
|
||||
gdi->btree.nitem = nitem;
|
||||
gdi->btree.curitem = 0;
|
||||
|
||||
while( gdi->btree.curitem < gdi->btree.nitem ) {
|
||||
while (gdi->btree.curitem < gdi->btree.nitem)
|
||||
{
|
||||
if (!gdi->stack)
|
||||
gdi->stack = ginPrepareFindLeafPage( &gdi->btree, rootBlkno );
|
||||
gdi->stack = ginPrepareFindLeafPage(&gdi->btree, rootBlkno);
|
||||
|
||||
gdi->stack = ginFindLeafPage( &gdi->btree, gdi->stack );
|
||||
gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
|
||||
|
||||
if ( gdi->btree.findItem( &(gdi->btree), gdi->stack ) )
|
||||
elog(ERROR,"item pointer (%u,%d) already exists",
|
||||
ItemPointerGetBlockNumber(gdi->btree.items + gdi->btree.curitem),
|
||||
ItemPointerGetOffsetNumber(gdi->btree.items + gdi->btree.curitem));
|
||||
if (gdi->btree.findItem(&(gdi->btree), gdi->stack))
|
||||
elog(ERROR, "item pointer (%u,%d) already exists",
|
||||
ItemPointerGetBlockNumber(gdi->btree.items + gdi->btree.curitem),
|
||||
ItemPointerGetOffsetNumber(gdi->btree.items + gdi->btree.curitem));
|
||||
|
||||
ginInsertValue(&(gdi->btree), gdi->stack);
|
||||
|
||||
gdi->stack=NULL;
|
||||
gdi->stack = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer
|
||||
scanBeginPostingTree( GinPostingTreeScan *gdi ) {
|
||||
gdi->stack = ginFindLeafPage( &gdi->btree, gdi->stack );
|
||||
scanBeginPostingTree(GinPostingTreeScan *gdi)
|
||||
{
|
||||
gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
|
||||
return gdi->stack->buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginentrypage.c
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
* page utilities routines for the postgres inverted index access method.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.3 2006/07/14 14:52:16 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.4 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -23,48 +23,52 @@
|
||||
* 1) Posting list
|
||||
* - itup->t_info & INDEX_SIZE_MASK contains size of tuple as usial
|
||||
* - ItemPointerGetBlockNumber(&itup->t_tid) contains original
|
||||
* size of tuple (without posting list).
|
||||
* size of tuple (without posting list).
|
||||
* Macroses: GinGetOrigSizePosting(itup) / GinSetOrigSizePosting(itup,n)
|
||||
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains number
|
||||
* of elements in posting list (number of heap itempointer)
|
||||
* Macroses: GinGetNPosting(itup) / GinSetNPosting(itup,n)
|
||||
* - After usial part of tuple there is a posting list
|
||||
* - After usial part of tuple there is a posting list
|
||||
* Macros: GinGetPosting(itup)
|
||||
* 2) Posting tree
|
||||
* - itup->t_info & INDEX_SIZE_MASK contains size of tuple as usial
|
||||
* - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
|
||||
* - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
|
||||
* root of posting tree
|
||||
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains magick number GIN_TREE_POSTING
|
||||
*/
|
||||
IndexTuple
|
||||
GinFormTuple(GinState *ginstate, Datum key, ItemPointerData *ipd, uint32 nipd) {
|
||||
bool isnull=FALSE;
|
||||
GinFormTuple(GinState *ginstate, Datum key, ItemPointerData *ipd, uint32 nipd)
|
||||
{
|
||||
bool isnull = FALSE;
|
||||
IndexTuple itup;
|
||||
|
||||
itup = index_form_tuple(ginstate->tupdesc, &key, &isnull);
|
||||
itup = index_form_tuple(ginstate->tupdesc, &key, &isnull);
|
||||
|
||||
GinSetOrigSizePosting( itup, IndexTupleSize(itup) );
|
||||
GinSetOrigSizePosting(itup, IndexTupleSize(itup));
|
||||
|
||||
if ( nipd > 0 ) {
|
||||
uint32 newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData)*nipd);
|
||||
if (nipd > 0)
|
||||
{
|
||||
uint32 newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData) * nipd);
|
||||
|
||||
if ( newsize >= INDEX_SIZE_MASK )
|
||||
if (newsize >= INDEX_SIZE_MASK)
|
||||
return NULL;
|
||||
|
||||
if ( newsize > TOAST_INDEX_TARGET && nipd > 1 )
|
||||
if (newsize > TOAST_INDEX_TARGET && nipd > 1)
|
||||
return NULL;
|
||||
|
||||
itup = repalloc( itup, newsize );
|
||||
itup = repalloc(itup, newsize);
|
||||
|
||||
/* set new size */
|
||||
itup->t_info &= ~INDEX_SIZE_MASK;
|
||||
itup->t_info &= ~INDEX_SIZE_MASK;
|
||||
itup->t_info |= newsize;
|
||||
|
||||
if ( ipd )
|
||||
memcpy( GinGetPosting(itup), ipd, sizeof(ItemPointerData)*nipd );
|
||||
GinSetNPosting(itup, nipd);
|
||||
} else {
|
||||
GinSetNPosting(itup, 0);
|
||||
if (ipd)
|
||||
memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);
|
||||
GinSetNPosting(itup, nipd);
|
||||
}
|
||||
else
|
||||
{
|
||||
GinSetNPosting(itup, 0);
|
||||
}
|
||||
return itup;
|
||||
}
|
||||
@@ -74,31 +78,35 @@ GinFormTuple(GinState *ginstate, Datum key, ItemPointerData *ipd, uint32 nipd) {
|
||||
* so we don't use right bound, we use rightest key instead.
|
||||
*/
|
||||
static IndexTuple
|
||||
getRightMostTuple(Page page) {
|
||||
getRightMostTuple(Page page)
|
||||
{
|
||||
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
|
||||
|
||||
return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
|
||||
}
|
||||
|
||||
Datum
|
||||
ginGetHighKey(GinState *ginstate, Page page) {
|
||||
IndexTuple itup;
|
||||
bool isnull;
|
||||
ginGetHighKey(GinState *ginstate, Page page)
|
||||
{
|
||||
IndexTuple itup;
|
||||
bool isnull;
|
||||
|
||||
itup = getRightMostTuple(page);
|
||||
|
||||
return index_getattr(itup, FirstOffsetNumber, ginstate->tupdesc, &isnull);
|
||||
return index_getattr(itup, FirstOffsetNumber, ginstate->tupdesc, &isnull);
|
||||
}
|
||||
|
||||
static bool
|
||||
entryIsMoveRight(GinBtree btree, Page page) {
|
||||
Datum highkey;
|
||||
static bool
|
||||
entryIsMoveRight(GinBtree btree, Page page)
|
||||
{
|
||||
Datum highkey;
|
||||
|
||||
if ( GinPageRightMost(page) )
|
||||
if (GinPageRightMost(page))
|
||||
return FALSE;
|
||||
|
||||
highkey = ginGetHighKey(btree->ginstate, page);
|
||||
|
||||
if ( compareEntries(btree->ginstate, btree->entryValue, highkey) > 0 )
|
||||
if (compareEntries(btree->ginstate, btree->entryValue, highkey) > 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
@@ -109,16 +117,20 @@ entryIsMoveRight(GinBtree btree, Page page) {
|
||||
* page correctly choosen and searching value SHOULD be on page
|
||||
*/
|
||||
static BlockNumber
|
||||
entryLocateEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
OffsetNumber low, high, maxoff;
|
||||
IndexTuple itup = NULL;
|
||||
int result;
|
||||
Page page = BufferGetPage( stack->buffer );
|
||||
entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
OffsetNumber low,
|
||||
high,
|
||||
maxoff;
|
||||
IndexTuple itup = NULL;
|
||||
int result;
|
||||
Page page = BufferGetPage(stack->buffer);
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( btree->fullScan ) {
|
||||
if (btree->fullScan)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
stack->predictNumber *= PageGetMaxOffsetNumber(page);
|
||||
return btree->getLeftMostPage(btree, page);
|
||||
@@ -126,39 +138,43 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
|
||||
low = FirstOffsetNumber;
|
||||
maxoff = high = PageGetMaxOffsetNumber(page);
|
||||
Assert( high >= low );
|
||||
Assert(high >= low);
|
||||
|
||||
high++;
|
||||
|
||||
while (high > low) {
|
||||
while (high > low)
|
||||
{
|
||||
OffsetNumber mid = low + ((high - low) / 2);
|
||||
|
||||
if ( mid == maxoff && GinPageRightMost(page) )
|
||||
if (mid == maxoff && GinPageRightMost(page))
|
||||
/* Right infinity */
|
||||
result = -1;
|
||||
else {
|
||||
bool isnull;
|
||||
else
|
||||
{
|
||||
bool isnull;
|
||||
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
|
||||
result = compareEntries(btree->ginstate, btree->entryValue,
|
||||
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull) );
|
||||
result = compareEntries(btree->ginstate, btree->entryValue,
|
||||
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull));
|
||||
}
|
||||
|
||||
if ( result == 0 ) {
|
||||
if (result == 0)
|
||||
{
|
||||
stack->off = mid;
|
||||
Assert( GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO );
|
||||
Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
|
||||
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
|
||||
} else if ( result > 0 )
|
||||
}
|
||||
else if (result > 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
Assert( high>=FirstOffsetNumber && high <= maxoff );
|
||||
Assert(high >= FirstOffsetNumber && high <= maxoff);
|
||||
|
||||
stack->off = high;
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
|
||||
Assert( GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO );
|
||||
Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
|
||||
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
|
||||
}
|
||||
|
||||
@@ -168,15 +184,18 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
* Returns true if value found on page.
|
||||
*/
|
||||
static bool
|
||||
entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
Page page = BufferGetPage( stack->buffer );
|
||||
OffsetNumber low, high;
|
||||
IndexTuple itup;
|
||||
entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
|
||||
{
|
||||
Page page = BufferGetPage(stack->buffer);
|
||||
OffsetNumber low,
|
||||
high;
|
||||
IndexTuple itup;
|
||||
|
||||
Assert( GinPageIsLeaf(page) );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(GinPageIsLeaf(page));
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( btree->fullScan ) {
|
||||
if (btree->fullScan)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -184,26 +203,30 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
low = FirstOffsetNumber;
|
||||
high = PageGetMaxOffsetNumber(page);
|
||||
|
||||
if ( high < low ) {
|
||||
if (high < low)
|
||||
{
|
||||
stack->off = FirstOffsetNumber;
|
||||
return false;
|
||||
}
|
||||
|
||||
high++;
|
||||
|
||||
while (high > low) {
|
||||
while (high > low)
|
||||
{
|
||||
OffsetNumber mid = low + ((high - low) / 2);
|
||||
bool isnull;
|
||||
int result;
|
||||
bool isnull;
|
||||
int result;
|
||||
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
|
||||
result = compareEntries(btree->ginstate, btree->entryValue,
|
||||
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull) );
|
||||
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull));
|
||||
|
||||
if ( result == 0 ) {
|
||||
if (result == 0)
|
||||
{
|
||||
stack->off = mid;
|
||||
return true;
|
||||
} else if ( result > 0 )
|
||||
}
|
||||
else if (result > 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
@@ -214,33 +237,40 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack) {
|
||||
}
|
||||
|
||||
static OffsetNumber
|
||||
entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff) {
|
||||
OffsetNumber i, maxoff = PageGetMaxOffsetNumber(page);
|
||||
IndexTuple itup;
|
||||
entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
|
||||
{
|
||||
OffsetNumber i,
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
IndexTuple itup;
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
/* if page isn't changed, we returns storedOff */
|
||||
if ( storedOff>= FirstOffsetNumber && storedOff<=maxoff) {
|
||||
if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
|
||||
{
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
|
||||
if ( GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno )
|
||||
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
|
||||
return storedOff;
|
||||
|
||||
/* we hope, that needed pointer goes to right. It's true
|
||||
if there wasn't a deletion */
|
||||
for( i=storedOff+1 ; i <= maxoff ; i++ ) {
|
||||
/*
|
||||
* we hope, that needed pointer goes to right. It's true if there
|
||||
* wasn't a deletion
|
||||
*/
|
||||
for (i = storedOff + 1; i <= maxoff; i++)
|
||||
{
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
|
||||
if ( GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno )
|
||||
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
|
||||
return i;
|
||||
}
|
||||
maxoff = storedOff-1;
|
||||
maxoff = storedOff - 1;
|
||||
}
|
||||
|
||||
/* last chance */
|
||||
for( i=FirstOffsetNumber; i <= maxoff ; i++ ) {
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
||||
{
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
|
||||
if ( GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno )
|
||||
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -248,31 +278,35 @@ entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber sto
|
||||
}
|
||||
|
||||
static BlockNumber
|
||||
entryGetLeftMostPage(GinBtree btree, Page page) {
|
||||
IndexTuple itup;
|
||||
entryGetLeftMostPage(GinBtree btree, Page page)
|
||||
{
|
||||
IndexTuple itup;
|
||||
|
||||
Assert( !GinPageIsLeaf(page) );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert( PageGetMaxOffsetNumber(page) >= FirstOffsetNumber );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(!GinPageIsData(page));
|
||||
Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
|
||||
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
|
||||
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
|
||||
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
|
||||
}
|
||||
|
||||
static bool
|
||||
entryIsEnoughSpace( GinBtree btree, Buffer buf, OffsetNumber off ) {
|
||||
Size itupsz = 0;
|
||||
Page page = BufferGetPage(buf);
|
||||
entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off)
|
||||
{
|
||||
Size itupsz = 0;
|
||||
Page page = BufferGetPage(buf);
|
||||
|
||||
Assert( btree->entry );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(btree->entry);
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( btree->isDelete ) {
|
||||
IndexTuple itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, off));
|
||||
itupsz = MAXALIGN( IndexTupleSize( itup ) ) + sizeof(ItemIdData);
|
||||
if (btree->isDelete)
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
|
||||
|
||||
itupsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
|
||||
}
|
||||
|
||||
if ( PageGetFreeSpace(page) + itupsz >= MAXALIGN(IndexTupleSize(btree->entry)) + sizeof(ItemIdData) )
|
||||
if (PageGetFreeSpace(page) + itupsz >= MAXALIGN(IndexTupleSize(btree->entry)) + sizeof(ItemIdData))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -284,19 +318,23 @@ entryIsEnoughSpace( GinBtree btree, Buffer buf, OffsetNumber off ) {
|
||||
* if child split is occured
|
||||
*/
|
||||
static BlockNumber
|
||||
entryPreparePage( GinBtree btree, Page page, OffsetNumber off) {
|
||||
entryPreparePage(GinBtree btree, Page page, OffsetNumber off)
|
||||
{
|
||||
BlockNumber ret = InvalidBlockNumber;
|
||||
|
||||
Assert( btree->entry );
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(btree->entry);
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( btree->isDelete ) {
|
||||
Assert( GinPageIsLeaf(page) );
|
||||
if (btree->isDelete)
|
||||
{
|
||||
Assert(GinPageIsLeaf(page));
|
||||
PageIndexTupleDelete(page, off);
|
||||
}
|
||||
|
||||
if ( !GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber ) {
|
||||
IndexTuple itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, off));
|
||||
if (!GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber)
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
|
||||
|
||||
ItemPointerSet(&itup->t_tid, btree->rightblkno, InvalidOffsetNumber);
|
||||
ret = btree->rightblkno;
|
||||
}
|
||||
@@ -310,22 +348,23 @@ entryPreparePage( GinBtree btree, Page page, OffsetNumber off) {
|
||||
* Place tuple on page and fills WAL record
|
||||
*/
|
||||
static void
|
||||
entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata) {
|
||||
Page page = BufferGetPage(buf);
|
||||
entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
|
||||
{
|
||||
Page page = BufferGetPage(buf);
|
||||
static XLogRecData rdata[3];
|
||||
OffsetNumber placed;
|
||||
static ginxlogInsert data;
|
||||
OffsetNumber placed;
|
||||
static ginxlogInsert data;
|
||||
|
||||
*prdata = rdata;
|
||||
data.updateBlkno = entryPreparePage( btree, page, off );
|
||||
data.updateBlkno = entryPreparePage(btree, page, off);
|
||||
|
||||
placed = PageAddItem( page, (Item)btree->entry, IndexTupleSize(btree->entry), off, LP_USED);
|
||||
if ( placed != off )
|
||||
placed = PageAddItem(page, (Item) btree->entry, IndexTupleSize(btree->entry), off, LP_USED);
|
||||
if (placed != off)
|
||||
elog(ERROR, "failed to add item to index page in \"%s\"",
|
||||
RelationGetRelationName(btree->index));
|
||||
RelationGetRelationName(btree->index));
|
||||
|
||||
data.node = btree->index->rd_node;
|
||||
data.blkno = BufferGetBlockNumber( buf );
|
||||
data.blkno = BufferGetBlockNumber(buf);
|
||||
data.offset = off;
|
||||
data.nitem = 1;
|
||||
data.isDelete = btree->isDelete;
|
||||
@@ -358,87 +397,99 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd
|
||||
* an equal number!
|
||||
*/
|
||||
static Page
|
||||
entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata) {
|
||||
entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
|
||||
{
|
||||
static XLogRecData rdata[2];
|
||||
OffsetNumber i, maxoff, separator=InvalidOffsetNumber;
|
||||
Size totalsize=0;
|
||||
Size lsize = 0, size;
|
||||
static char tupstore[ 2*BLCKSZ ];
|
||||
char *ptr;
|
||||
IndexTuple itup, leftrightmost=NULL;
|
||||
static ginxlogSplit data;
|
||||
Datum value;
|
||||
bool isnull;
|
||||
Page page;
|
||||
Page lpage = GinPageGetCopyPage( BufferGetPage( lbuf ) );
|
||||
Page rpage = BufferGetPage( rbuf );
|
||||
Size pageSize = PageGetPageSize( lpage );
|
||||
OffsetNumber i,
|
||||
maxoff,
|
||||
separator = InvalidOffsetNumber;
|
||||
Size totalsize = 0;
|
||||
Size lsize = 0,
|
||||
size;
|
||||
static char tupstore[2 * BLCKSZ];
|
||||
char *ptr;
|
||||
IndexTuple itup,
|
||||
leftrightmost = NULL;
|
||||
static ginxlogSplit data;
|
||||
Datum value;
|
||||
bool isnull;
|
||||
Page page;
|
||||
Page lpage = GinPageGetCopyPage(BufferGetPage(lbuf));
|
||||
Page rpage = BufferGetPage(rbuf);
|
||||
Size pageSize = PageGetPageSize(lpage);
|
||||
|
||||
*prdata = rdata;
|
||||
data.leftChildBlkno = ( GinPageIsLeaf(lpage) ) ?
|
||||
InvalidOffsetNumber : GinItemPointerGetBlockNumber( &(btree->entry->t_tid) );
|
||||
data.updateBlkno = entryPreparePage( btree, lpage, off );
|
||||
data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
|
||||
InvalidOffsetNumber : GinItemPointerGetBlockNumber(&(btree->entry->t_tid));
|
||||
data.updateBlkno = entryPreparePage(btree, lpage, off);
|
||||
|
||||
maxoff = PageGetMaxOffsetNumber(lpage);
|
||||
ptr = tupstore;
|
||||
ptr = tupstore;
|
||||
|
||||
for(i=FirstOffsetNumber; i<=maxoff; i++) {
|
||||
if ( i==off ) {
|
||||
size = MAXALIGN( IndexTupleSize(btree->entry) );
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
||||
{
|
||||
if (i == off)
|
||||
{
|
||||
size = MAXALIGN(IndexTupleSize(btree->entry));
|
||||
memcpy(ptr, btree->entry, size);
|
||||
ptr+=size;
|
||||
ptr += size;
|
||||
totalsize += size + sizeof(ItemIdData);
|
||||
}
|
||||
|
||||
itup = (IndexTuple)PageGetItem(lpage, PageGetItemId(lpage, i));
|
||||
size = MAXALIGN( IndexTupleSize(itup) );
|
||||
itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
|
||||
size = MAXALIGN(IndexTupleSize(itup));
|
||||
memcpy(ptr, itup, size);
|
||||
ptr+=size;
|
||||
ptr += size;
|
||||
totalsize += size + sizeof(ItemIdData);
|
||||
}
|
||||
|
||||
if ( off==maxoff+1 ) {
|
||||
size = MAXALIGN( IndexTupleSize(btree->entry) );
|
||||
if (off == maxoff + 1)
|
||||
{
|
||||
size = MAXALIGN(IndexTupleSize(btree->entry));
|
||||
memcpy(ptr, btree->entry, size);
|
||||
ptr+=size;
|
||||
ptr += size;
|
||||
totalsize += size + sizeof(ItemIdData);
|
||||
}
|
||||
|
||||
GinInitPage( rpage, GinPageGetOpaque(lpage)->flags, pageSize );
|
||||
GinInitPage( lpage, GinPageGetOpaque(rpage)->flags, pageSize );
|
||||
GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
|
||||
GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
|
||||
|
||||
ptr = tupstore;
|
||||
maxoff++;
|
||||
maxoff++;
|
||||
lsize = 0;
|
||||
|
||||
page = lpage;
|
||||
for(i=FirstOffsetNumber; i<=maxoff; i++) {
|
||||
itup = (IndexTuple)ptr;
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
||||
{
|
||||
itup = (IndexTuple) ptr;
|
||||
|
||||
if ( lsize > totalsize/2 ) {
|
||||
if ( separator==InvalidOffsetNumber )
|
||||
separator = i-1;
|
||||
if (lsize > totalsize / 2)
|
||||
{
|
||||
if (separator == InvalidOffsetNumber)
|
||||
separator = i - 1;
|
||||
page = rpage;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
leftrightmost = itup;
|
||||
lsize += MAXALIGN( IndexTupleSize(itup) ) + sizeof(ItemIdData);
|
||||
lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
|
||||
}
|
||||
|
||||
if ( PageAddItem( page, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index page in \"%s\"",
|
||||
RelationGetRelationName(btree->index));
|
||||
ptr += MAXALIGN( IndexTupleSize(itup) );
|
||||
RelationGetRelationName(btree->index));
|
||||
ptr += MAXALIGN(IndexTupleSize(itup));
|
||||
}
|
||||
|
||||
|
||||
value = index_getattr(leftrightmost, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull);
|
||||
btree->entry = GinFormTuple( btree->ginstate, value, NULL, 0);
|
||||
ItemPointerSet(&(btree->entry)->t_tid, BufferGetBlockNumber( lbuf ), InvalidOffsetNumber);
|
||||
btree->rightblkno = BufferGetBlockNumber( rbuf );
|
||||
|
||||
btree->entry = GinFormTuple(btree->ginstate, value, NULL, 0);
|
||||
ItemPointerSet(&(btree->entry)->t_tid, BufferGetBlockNumber(lbuf), InvalidOffsetNumber);
|
||||
btree->rightblkno = BufferGetBlockNumber(rbuf);
|
||||
|
||||
data.node = btree->index->rd_node;
|
||||
data.rootBlkno = InvalidBlockNumber;
|
||||
data.lblkno = BufferGetBlockNumber( lbuf );
|
||||
data.rblkno = BufferGetBlockNumber( rbuf );
|
||||
data.lblkno = BufferGetBlockNumber(lbuf);
|
||||
data.rblkno = BufferGetBlockNumber(rbuf);
|
||||
data.separator = separator;
|
||||
data.nitem = maxoff;
|
||||
data.isData = FALSE;
|
||||
@@ -458,23 +509,28 @@ entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogR
|
||||
return lpage;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* return newly allocate rightmost tuple
|
||||
*/
|
||||
IndexTuple
|
||||
ginPageGetLinkItup(Buffer buf) {
|
||||
IndexTuple itup, nitup;
|
||||
Page page = BufferGetPage(buf);
|
||||
ginPageGetLinkItup(Buffer buf)
|
||||
{
|
||||
IndexTuple itup,
|
||||
nitup;
|
||||
Page page = BufferGetPage(buf);
|
||||
|
||||
itup = getRightMostTuple( page );
|
||||
if ( GinPageIsLeaf(page) && !GinIsPostingTree(itup) ) {
|
||||
nitup = (IndexTuple)palloc( MAXALIGN(GinGetOrigSizePosting(itup)) );
|
||||
memcpy( nitup, itup, GinGetOrigSizePosting(itup) );
|
||||
itup = getRightMostTuple(page);
|
||||
if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
|
||||
{
|
||||
nitup = (IndexTuple) palloc(MAXALIGN(GinGetOrigSizePosting(itup)));
|
||||
memcpy(nitup, itup, GinGetOrigSizePosting(itup));
|
||||
nitup->t_info &= ~INDEX_SIZE_MASK;
|
||||
nitup->t_info |= GinGetOrigSizePosting(itup);
|
||||
} else {
|
||||
nitup = (IndexTuple)palloc( MAXALIGN(IndexTupleSize(itup)) );
|
||||
memcpy( nitup, itup, IndexTupleSize(itup) );
|
||||
}
|
||||
else
|
||||
{
|
||||
nitup = (IndexTuple) palloc(MAXALIGN(IndexTupleSize(itup)));
|
||||
memcpy(nitup, itup, IndexTupleSize(itup));
|
||||
}
|
||||
|
||||
ItemPointerSet(&nitup->t_tid, BufferGetBlockNumber(buf), InvalidOffsetNumber);
|
||||
@@ -486,23 +542,25 @@ ginPageGetLinkItup(Buffer buf) {
|
||||
* Also called from ginxlog, should not use btree
|
||||
*/
|
||||
void
|
||||
entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf) {
|
||||
Page page;
|
||||
IndexTuple itup;
|
||||
entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
|
||||
{
|
||||
Page page;
|
||||
IndexTuple itup;
|
||||
|
||||
page = BufferGetPage(root);
|
||||
|
||||
itup = ginPageGetLinkItup( lbuf );
|
||||
if ( PageAddItem( page, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
itup = ginPageGetLinkItup(lbuf);
|
||||
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index root page");
|
||||
|
||||
itup = ginPageGetLinkItup( rbuf );
|
||||
if ( PageAddItem( page, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
itup = ginPageGetLinkItup(rbuf);
|
||||
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index root page");
|
||||
}
|
||||
|
||||
void
|
||||
prepareEntryScan( GinBtree btree, Relation index, Datum value, GinState *ginstate) {
|
||||
prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate)
|
||||
{
|
||||
memset(btree, 0, sizeof(GinBtreeData));
|
||||
|
||||
btree->isMoveRight = entryIsMoveRight;
|
||||
@@ -524,4 +582,3 @@ prepareEntryScan( GinBtree btree, Relation index, Datum value, GinState *ginstat
|
||||
btree->fullScan = FALSE;
|
||||
btree->isBuild = FALSE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginget.c
|
||||
* fetch tuples from a GIN scan.
|
||||
* fetch tuples from a GIN scan.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.2 2006/07/14 14:52:16 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.3 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -18,15 +18,17 @@
|
||||
#include "utils/memutils.h"
|
||||
|
||||
static OffsetNumber
|
||||
findItemInPage( Page page, ItemPointer item, OffsetNumber off ) {
|
||||
findItemInPage(Page page, ItemPointer item, OffsetNumber off)
|
||||
{
|
||||
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
|
||||
int res;
|
||||
int res;
|
||||
|
||||
for(; off<=maxoff; off++) {
|
||||
res = compareItemPointers( item, (ItemPointer)GinDataPageGetItem(page, off) );
|
||||
Assert( res>= 0 );
|
||||
for (; off <= maxoff; off++)
|
||||
{
|
||||
res = compareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, off));
|
||||
Assert(res >= 0);
|
||||
|
||||
if ( res == 0 )
|
||||
if (res == 0)
|
||||
return off;
|
||||
}
|
||||
|
||||
@@ -38,24 +40,29 @@ findItemInPage( Page page, ItemPointer item, OffsetNumber off ) {
|
||||
* Stop* functions unlock buffer (but don't release!)
|
||||
*/
|
||||
static void
|
||||
startScanEntry( Relation index, GinState *ginstate, GinScanEntry entry, bool firstCall ) {
|
||||
if ( entry->master != NULL ) {
|
||||
startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry, bool firstCall)
|
||||
{
|
||||
if (entry->master != NULL)
|
||||
{
|
||||
entry->isFinished = entry->master->isFinished;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( firstCall ) {
|
||||
/* at first call we should find entry, and
|
||||
begin scan of posting tree or just store posting list in memory */
|
||||
if (firstCall)
|
||||
{
|
||||
/*
|
||||
* at first call we should find entry, and begin scan of posting tree
|
||||
* or just store posting list in memory
|
||||
*/
|
||||
GinBtreeData btreeEntry;
|
||||
GinBtreeStack *stackEntry;
|
||||
Page page;
|
||||
bool needUnlock = TRUE;
|
||||
GinBtreeStack *stackEntry;
|
||||
Page page;
|
||||
bool needUnlock = TRUE;
|
||||
|
||||
prepareEntryScan( &btreeEntry, index, entry->entry, ginstate );
|
||||
prepareEntryScan(&btreeEntry, index, entry->entry, ginstate);
|
||||
btreeEntry.searchMode = TRUE;
|
||||
stackEntry = ginFindLeafPage(&btreeEntry, NULL);
|
||||
page = BufferGetPage( stackEntry->buffer );
|
||||
page = BufferGetPage(stackEntry->buffer);
|
||||
|
||||
entry->isFinished = TRUE;
|
||||
entry->buffer = InvalidBuffer;
|
||||
@@ -65,103 +72,115 @@ startScanEntry( Relation index, GinState *ginstate, GinScanEntry entry, bool fir
|
||||
entry->reduceResult = FALSE;
|
||||
entry->predictNumberResult = 0;
|
||||
|
||||
if ( btreeEntry.findItem( &btreeEntry, stackEntry ) ) {
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
|
||||
if (btreeEntry.findItem(&btreeEntry, stackEntry))
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
|
||||
|
||||
if ( GinIsPostingTree(itup) ) {
|
||||
if (GinIsPostingTree(itup))
|
||||
{
|
||||
BlockNumber rootPostingTree = GinGetPostingTree(itup);
|
||||
GinPostingTreeScan *gdi;
|
||||
Page page;
|
||||
Page page;
|
||||
|
||||
LockBuffer(stackEntry->buffer, GIN_UNLOCK);
|
||||
needUnlock = FALSE;
|
||||
gdi = prepareScanPostingTree( index, rootPostingTree, TRUE );
|
||||
needUnlock = FALSE;
|
||||
gdi = prepareScanPostingTree(index, rootPostingTree, TRUE);
|
||||
|
||||
entry->buffer = scanBeginPostingTree( gdi );
|
||||
IncrBufferRefCount( entry->buffer );
|
||||
entry->buffer = scanBeginPostingTree(gdi);
|
||||
IncrBufferRefCount(entry->buffer);
|
||||
|
||||
page = BufferGetPage( entry->buffer );
|
||||
entry->predictNumberResult = gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff;
|
||||
page = BufferGetPage(entry->buffer);
|
||||
entry->predictNumberResult = gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff;
|
||||
|
||||
freeGinBtreeStack( gdi->stack );
|
||||
pfree( gdi );
|
||||
freeGinBtreeStack(gdi->stack);
|
||||
pfree(gdi);
|
||||
entry->isFinished = FALSE;
|
||||
} else if ( GinGetNPosting(itup) > 0 ) {
|
||||
}
|
||||
else if (GinGetNPosting(itup) > 0)
|
||||
{
|
||||
entry->nlist = GinGetNPosting(itup);
|
||||
entry->list = (ItemPointerData*)palloc( sizeof(ItemPointerData) * entry->nlist );
|
||||
memcpy( entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist );
|
||||
entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist);
|
||||
memcpy(entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist);
|
||||
entry->isFinished = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( needUnlock )
|
||||
if (needUnlock)
|
||||
LockBuffer(stackEntry->buffer, GIN_UNLOCK);
|
||||
freeGinBtreeStack( stackEntry );
|
||||
} else if ( entry->buffer != InvalidBuffer ) {
|
||||
freeGinBtreeStack(stackEntry);
|
||||
}
|
||||
else if (entry->buffer != InvalidBuffer)
|
||||
{
|
||||
/* we should find place were we was stopped */
|
||||
BlockNumber blkno;
|
||||
Page page;
|
||||
Page page;
|
||||
|
||||
LockBuffer( entry->buffer, GIN_SHARE );
|
||||
LockBuffer(entry->buffer, GIN_SHARE);
|
||||
|
||||
if ( !ItemPointerIsValid( &entry->curItem ) )
|
||||
if (!ItemPointerIsValid(&entry->curItem))
|
||||
/* start position */
|
||||
return;
|
||||
Assert( entry->offset!=InvalidOffsetNumber );
|
||||
Assert(entry->offset != InvalidOffsetNumber);
|
||||
|
||||
page = BufferGetPage( entry->buffer );
|
||||
page = BufferGetPage(entry->buffer);
|
||||
|
||||
/* try to find curItem in current buffer */
|
||||
if ( (entry->offset=findItemInPage(page , &entry->curItem, entry->offset))!=InvalidOffsetNumber )
|
||||
if ((entry->offset = findItemInPage(page, &entry->curItem, entry->offset)) != InvalidOffsetNumber)
|
||||
return;
|
||||
|
||||
/* walk to right */
|
||||
while( (blkno = GinPageGetOpaque( page )->rightlink)!=InvalidBlockNumber ) {
|
||||
LockBuffer( entry->buffer, GIN_UNLOCK );
|
||||
entry->buffer = ReleaseAndReadBuffer( entry->buffer, index, blkno );
|
||||
LockBuffer( entry->buffer, GIN_SHARE );
|
||||
page = BufferGetPage( entry->buffer );
|
||||
while ((blkno = GinPageGetOpaque(page)->rightlink) != InvalidBlockNumber)
|
||||
{
|
||||
LockBuffer(entry->buffer, GIN_UNLOCK);
|
||||
entry->buffer = ReleaseAndReadBuffer(entry->buffer, index, blkno);
|
||||
LockBuffer(entry->buffer, GIN_SHARE);
|
||||
page = BufferGetPage(entry->buffer);
|
||||
|
||||
if ( (entry->offset=findItemInPage(page , &entry->curItem, FirstOffsetNumber))!=InvalidOffsetNumber )
|
||||
if ((entry->offset = findItemInPage(page, &entry->curItem, FirstOffsetNumber)) != InvalidOffsetNumber)
|
||||
return;
|
||||
}
|
||||
|
||||
elog(ERROR,"Logic error: lost previously founded ItemId");
|
||||
elog(ERROR, "Logic error: lost previously founded ItemId");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stopScanEntry( GinScanEntry entry ) {
|
||||
if ( entry->buffer != InvalidBuffer )
|
||||
LockBuffer( entry->buffer, GIN_UNLOCK );
|
||||
stopScanEntry(GinScanEntry entry)
|
||||
{
|
||||
if (entry->buffer != InvalidBuffer)
|
||||
LockBuffer(entry->buffer, GIN_UNLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
startScanKey( Relation index, GinState *ginstate, GinScanKey key ) {
|
||||
uint32 i;
|
||||
startScanKey(Relation index, GinState *ginstate, GinScanKey key)
|
||||
{
|
||||
uint32 i;
|
||||
|
||||
for(i=0;i<key->nentries;i++)
|
||||
startScanEntry( index, ginstate, key->scanEntry+i, key->firstCall );
|
||||
|
||||
if ( key->firstCall ) {
|
||||
memset( key->entryRes, TRUE, sizeof(bool) * key->nentries );
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
startScanEntry(index, ginstate, key->scanEntry + i, key->firstCall);
|
||||
|
||||
if (key->firstCall)
|
||||
{
|
||||
memset(key->entryRes, TRUE, sizeof(bool) * key->nentries);
|
||||
key->isFinished = FALSE;
|
||||
key->firstCall = FALSE;
|
||||
|
||||
if ( GinFuzzySearchLimit > 0 ) {
|
||||
if (GinFuzzySearchLimit > 0)
|
||||
{
|
||||
/*
|
||||
* If all of keys more than treshold we will try to reduce
|
||||
* result, we hope (and only hope, for intersection operation of array
|
||||
* our supposition isn't true), that total result will not more
|
||||
* than minimal predictNumberResult.
|
||||
* If all of keys more than treshold we will try to reduce result,
|
||||
* we hope (and only hope, for intersection operation of array our
|
||||
* supposition isn't true), that total result will not more than
|
||||
* minimal predictNumberResult.
|
||||
*/
|
||||
|
||||
for(i=0;i<key->nentries;i++)
|
||||
if ( key->scanEntry[i].predictNumberResult <= key->nentries * GinFuzzySearchLimit )
|
||||
return;
|
||||
|
||||
for(i=0;i<key->nentries;i++)
|
||||
if ( key->scanEntry[i].predictNumberResult > key->nentries * GinFuzzySearchLimit ) {
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
if (key->scanEntry[i].predictNumberResult <= key->nentries * GinFuzzySearchLimit)
|
||||
return;
|
||||
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
if (key->scanEntry[i].predictNumberResult > key->nentries * GinFuzzySearchLimit)
|
||||
{
|
||||
key->scanEntry[i].predictNumberResult /= key->nentries;
|
||||
key->scanEntry[i].reduceResult = TRUE;
|
||||
}
|
||||
@@ -170,50 +189,60 @@ startScanKey( Relation index, GinState *ginstate, GinScanKey key ) {
|
||||
}
|
||||
|
||||
static void
|
||||
stopScanKey( GinScanKey key ) {
|
||||
uint32 i;
|
||||
stopScanKey(GinScanKey key)
|
||||
{
|
||||
uint32 i;
|
||||
|
||||
for(i=0;i<key->nentries;i++)
|
||||
stopScanEntry( key->scanEntry+i );
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
stopScanEntry(key->scanEntry + i);
|
||||
}
|
||||
|
||||
static void
|
||||
startScan( IndexScanDesc scan ) {
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
startScan(IndexScanDesc scan)
|
||||
{
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
for(i=0; i<so->nkeys; i++)
|
||||
startScanKey( scan->indexRelation, &so->ginstate, so->keys + i );
|
||||
for (i = 0; i < so->nkeys; i++)
|
||||
startScanKey(scan->indexRelation, &so->ginstate, so->keys + i);
|
||||
}
|
||||
|
||||
static void
|
||||
stopScan( IndexScanDesc scan ) {
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
stopScan(IndexScanDesc scan)
|
||||
{
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
for(i=0; i<so->nkeys; i++)
|
||||
stopScanKey( so->keys + i );
|
||||
for (i = 0; i < so->nkeys; i++)
|
||||
stopScanKey(so->keys + i);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
entryGetNextItem( Relation index, GinScanEntry entry ) {
|
||||
Page page = BufferGetPage( entry->buffer );
|
||||
entryGetNextItem(Relation index, GinScanEntry entry)
|
||||
{
|
||||
Page page = BufferGetPage(entry->buffer);
|
||||
|
||||
entry->offset++;
|
||||
if ( entry->offset <= GinPageGetOpaque( page )->maxoff && GinPageGetOpaque( page )->maxoff >= FirstOffsetNumber ) {
|
||||
entry->curItem = *(ItemPointerData*)GinDataPageGetItem(page, entry->offset);
|
||||
} else {
|
||||
BlockNumber blkno = GinPageGetOpaque( page )->rightlink;
|
||||
|
||||
LockBuffer( entry->buffer, GIN_UNLOCK );
|
||||
if ( blkno == InvalidBlockNumber ) {
|
||||
ReleaseBuffer( entry->buffer );
|
||||
if (entry->offset <= GinPageGetOpaque(page)->maxoff && GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber)
|
||||
{
|
||||
entry->curItem = *(ItemPointerData *) GinDataPageGetItem(page, entry->offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
LockBuffer(entry->buffer, GIN_UNLOCK);
|
||||
if (blkno == InvalidBlockNumber)
|
||||
{
|
||||
ReleaseBuffer(entry->buffer);
|
||||
entry->buffer = InvalidBuffer;
|
||||
entry->isFinished = TRUE;
|
||||
} else {
|
||||
entry->buffer = ReleaseAndReadBuffer( entry->buffer, index, blkno );
|
||||
LockBuffer( entry->buffer, GIN_SHARE );
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->buffer = ReleaseAndReadBuffer(entry->buffer, index, blkno);
|
||||
LockBuffer(entry->buffer, GIN_SHARE);
|
||||
entry->offset = InvalidOffsetNumber;
|
||||
entryGetNextItem(index, entry);
|
||||
}
|
||||
@@ -221,29 +250,37 @@ entryGetNextItem( Relation index, GinScanEntry entry ) {
|
||||
}
|
||||
|
||||
#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
|
||||
#define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
|
||||
#define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
|
||||
|
||||
/*
|
||||
* Sets entry->curItem to new found heap item pointer for one
|
||||
* Sets entry->curItem to new found heap item pointer for one
|
||||
* entry of one scan key
|
||||
*/
|
||||
static bool
|
||||
entryGetItem( Relation index, GinScanEntry entry ) {
|
||||
if ( entry->master ) {
|
||||
entryGetItem(Relation index, GinScanEntry entry)
|
||||
{
|
||||
if (entry->master)
|
||||
{
|
||||
entry->isFinished = entry->master->isFinished;
|
||||
entry->curItem = entry->master->curItem;
|
||||
} else if ( entry->list ) {
|
||||
}
|
||||
else if (entry->list)
|
||||
{
|
||||
entry->offset++;
|
||||
if ( entry->offset <= entry->nlist )
|
||||
entry->curItem = entry->list[ entry->offset - 1 ];
|
||||
else {
|
||||
ItemPointerSet( &entry->curItem, InvalidBlockNumber, InvalidOffsetNumber );
|
||||
if (entry->offset <= entry->nlist)
|
||||
entry->curItem = entry->list[entry->offset - 1];
|
||||
else
|
||||
{
|
||||
ItemPointerSet(&entry->curItem, InvalidBlockNumber, InvalidOffsetNumber);
|
||||
entry->isFinished = TRUE;
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
entryGetNextItem(index, entry);
|
||||
} while ( entry->isFinished == FALSE && entry->reduceResult == TRUE && dropItem(entry) );
|
||||
} while (entry->isFinished == FALSE && entry->reduceResult == TRUE && dropItem(entry));
|
||||
}
|
||||
|
||||
return entry->isFinished;
|
||||
@@ -254,155 +291,180 @@ entryGetItem( Relation index, GinScanEntry entry ) {
|
||||
* returns isFinished!
|
||||
*/
|
||||
static bool
|
||||
keyGetItem( Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey key ) {
|
||||
uint32 i;
|
||||
GinScanEntry entry;
|
||||
bool res;
|
||||
MemoryContext oldCtx;
|
||||
keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey key)
|
||||
{
|
||||
uint32 i;
|
||||
GinScanEntry entry;
|
||||
bool res;
|
||||
MemoryContext oldCtx;
|
||||
|
||||
if ( key->isFinished )
|
||||
if (key->isFinished)
|
||||
return TRUE;
|
||||
|
||||
do {
|
||||
/* move forward from previously value and set new curItem,
|
||||
which is minimal from entries->curItems */
|
||||
ItemPointerSetMax( &key->curItem );
|
||||
for(i=0;i<key->nentries;i++) {
|
||||
entry = key->scanEntry+i;
|
||||
|
||||
if ( key->entryRes[i] ) {
|
||||
if ( entry->isFinished == FALSE && entryGetItem(index, entry) == FALSE ) {
|
||||
if (compareItemPointers( &entry->curItem, &key->curItem ) < 0)
|
||||
do
|
||||
{
|
||||
/*
|
||||
* move forward from previously value and set new curItem, which is
|
||||
* minimal from entries->curItems
|
||||
*/
|
||||
ItemPointerSetMax(&key->curItem);
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
{
|
||||
entry = key->scanEntry + i;
|
||||
|
||||
if (key->entryRes[i])
|
||||
{
|
||||
if (entry->isFinished == FALSE && entryGetItem(index, entry) == FALSE)
|
||||
{
|
||||
if (compareItemPointers(&entry->curItem, &key->curItem) < 0)
|
||||
key->curItem = entry->curItem;
|
||||
} else
|
||||
}
|
||||
else
|
||||
key->entryRes[i] = FALSE;
|
||||
} else if ( entry->isFinished == FALSE ) {
|
||||
if (compareItemPointers( &entry->curItem, &key->curItem ) < 0)
|
||||
}
|
||||
else if (entry->isFinished == FALSE)
|
||||
{
|
||||
if (compareItemPointers(&entry->curItem, &key->curItem) < 0)
|
||||
key->curItem = entry->curItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ItemPointerIsMax( &key->curItem ) ) {
|
||||
if (ItemPointerIsMax(&key->curItem))
|
||||
{
|
||||
/* all entries are finished */
|
||||
key->isFinished = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ( key->nentries == 1 ) {
|
||||
|
||||
if (key->nentries == 1)
|
||||
{
|
||||
/* we can do not call consistentFn !! */
|
||||
key->entryRes[0] = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* setting up array for consistentFn */
|
||||
for(i=0;i<key->nentries;i++) {
|
||||
entry = key->scanEntry+i;
|
||||
|
||||
if ( entry->isFinished == FALSE && compareItemPointers( &entry->curItem, &key->curItem )==0 )
|
||||
for (i = 0; i < key->nentries; i++)
|
||||
{
|
||||
entry = key->scanEntry + i;
|
||||
|
||||
if (entry->isFinished == FALSE && compareItemPointers(&entry->curItem, &key->curItem) == 0)
|
||||
key->entryRes[i] = TRUE;
|
||||
else
|
||||
key->entryRes[i] = FALSE;
|
||||
}
|
||||
|
||||
oldCtx = MemoryContextSwitchTo(tempCtx);
|
||||
res = DatumGetBool( FunctionCall3(
|
||||
&ginstate->consistentFn,
|
||||
PointerGetDatum( key->entryRes ),
|
||||
UInt16GetDatum( key->strategy ),
|
||||
key->query
|
||||
));
|
||||
res = DatumGetBool(FunctionCall3(
|
||||
&ginstate->consistentFn,
|
||||
PointerGetDatum(key->entryRes),
|
||||
UInt16GetDatum(key->strategy),
|
||||
key->query
|
||||
));
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
MemoryContextReset(tempCtx);
|
||||
} while( !res );
|
||||
|
||||
} while (!res);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get heap item pointer from scan
|
||||
* returns true if found
|
||||
* Get heap item pointer from scan
|
||||
* returns true if found
|
||||
*/
|
||||
static bool
|
||||
scanGetItem( IndexScanDesc scan, ItemPointerData *item ) {
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
scanGetItem(IndexScanDesc scan, ItemPointerData *item)
|
||||
{
|
||||
uint32 i;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
ItemPointerSetMin( item );
|
||||
for(i=0;i<so->nkeys;i++) {
|
||||
GinScanKey key = so->keys+i;
|
||||
ItemPointerSetMin(item);
|
||||
for (i = 0; i < so->nkeys; i++)
|
||||
{
|
||||
GinScanKey key = so->keys + i;
|
||||
|
||||
if ( keyGetItem( scan->indexRelation, &so->ginstate, so->tempCtx, key )==FALSE ) {
|
||||
if ( compareItemPointers( item, &key->curItem ) < 0 )
|
||||
if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == FALSE)
|
||||
{
|
||||
if (compareItemPointers(item, &key->curItem) < 0)
|
||||
*item = key->curItem;
|
||||
} else
|
||||
return FALSE; /* finshed one of keys */
|
||||
}
|
||||
else
|
||||
return FALSE; /* finshed one of keys */
|
||||
}
|
||||
|
||||
for(i=1;i<=so->nkeys;i++) {
|
||||
GinScanKey key = so->keys+i-1;
|
||||
|
||||
for(;;) {
|
||||
int cmp = compareItemPointers( item, &key->curItem );
|
||||
for (i = 1; i <= so->nkeys; i++)
|
||||
{
|
||||
GinScanKey key = so->keys + i - 1;
|
||||
|
||||
if ( cmp == 0 )
|
||||
for (;;)
|
||||
{
|
||||
int cmp = compareItemPointers(item, &key->curItem);
|
||||
|
||||
if (cmp == 0)
|
||||
break;
|
||||
else if ( cmp > 0 ) {
|
||||
if ( keyGetItem( scan->indexRelation, &so->ginstate, so->tempCtx, key )==TRUE )
|
||||
return FALSE; /* finshed one of keys */
|
||||
} else { /* returns to begin */
|
||||
else if (cmp > 0)
|
||||
{
|
||||
if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == TRUE)
|
||||
return FALSE; /* finshed one of keys */
|
||||
}
|
||||
else
|
||||
{ /* returns to begin */
|
||||
*item = key->curItem;
|
||||
i=0;
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define GinIsNewKey(s) ( ((GinScanOpaque) scan->opaque)->keys == NULL )
|
||||
|
||||
Datum
|
||||
gingetmulti(PG_FUNCTION_ARGS) {
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
Datum
|
||||
gingetmulti(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
|
||||
int32 max_tids = PG_GETARG_INT32(2);
|
||||
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
|
||||
int32 max_tids = PG_GETARG_INT32(2);
|
||||
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
|
||||
|
||||
if ( GinIsNewKey(scan) )
|
||||
newScanKey( scan );
|
||||
if (GinIsNewKey(scan))
|
||||
newScanKey(scan);
|
||||
|
||||
startScan( scan );
|
||||
startScan(scan);
|
||||
|
||||
*returned_tids = 0;
|
||||
|
||||
do {
|
||||
if ( scanGetItem( scan, tids + *returned_tids ) )
|
||||
do
|
||||
{
|
||||
if (scanGetItem(scan, tids + *returned_tids))
|
||||
(*returned_tids)++;
|
||||
else
|
||||
break;
|
||||
} while ( *returned_tids < max_tids );
|
||||
} while (*returned_tids < max_tids);
|
||||
|
||||
stopScan( scan );
|
||||
stopScan(scan);
|
||||
|
||||
PG_RETURN_BOOL(*returned_tids == max_tids);
|
||||
}
|
||||
|
||||
Datum
|
||||
gingettuple(PG_FUNCTION_ARGS) {
|
||||
gingettuple(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
|
||||
bool res;
|
||||
bool res;
|
||||
|
||||
if ( dir != ForwardScanDirection )
|
||||
if (dir != ForwardScanDirection)
|
||||
elog(ERROR, "Gin doesn't support other scan directions than forward");
|
||||
|
||||
if ( GinIsNewKey(scan) )
|
||||
newScanKey( scan );
|
||||
|
||||
startScan( scan );
|
||||
if (GinIsNewKey(scan))
|
||||
newScanKey(scan);
|
||||
|
||||
startScan(scan);
|
||||
res = scanGetItem(scan, &scan->xs_ctup.t_self);
|
||||
stopScan( scan );
|
||||
stopScan(scan);
|
||||
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* gininsert.c
|
||||
* insert routines for the postgres inverted index access method.
|
||||
* insert routines for the postgres inverted index access method.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.4 2006/07/14 14:52:16 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.5 2006/10/04 00:29:47 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
#include "miscadmin.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
typedef struct {
|
||||
GinState ginstate;
|
||||
double indtuples;
|
||||
MemoryContext tmpCtx;
|
||||
MemoryContext funcCtx;
|
||||
BuildAccumulator accum;
|
||||
typedef struct
|
||||
{
|
||||
GinState ginstate;
|
||||
double indtuples;
|
||||
MemoryContext tmpCtx;
|
||||
MemoryContext funcCtx;
|
||||
BuildAccumulator accum;
|
||||
} GinBuildState;
|
||||
|
||||
/*
|
||||
@@ -32,24 +33,26 @@ typedef struct {
|
||||
* suppose that items[] fits to page
|
||||
*/
|
||||
static BlockNumber
|
||||
createPostingTree( Relation index, ItemPointerData *items, uint32 nitems ) {
|
||||
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
|
||||
{
|
||||
BlockNumber blkno;
|
||||
Buffer buffer = GinNewBuffer(index);
|
||||
Page page;
|
||||
Buffer buffer = GinNewBuffer(index);
|
||||
Page page;
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
GinInitBuffer( buffer, GIN_DATA|GIN_LEAF );
|
||||
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
|
||||
page = BufferGetPage(buffer);
|
||||
blkno = BufferGetBlockNumber(buffer);
|
||||
|
||||
memcpy( GinDataPageGetData(page), items, sizeof(ItemPointerData) * nitems );
|
||||
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * nitems);
|
||||
GinPageGetOpaque(page)->maxoff = nitems;
|
||||
|
||||
if (!index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
if (!index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
XLogRecData rdata[2];
|
||||
ginxlogCreatePostingTree data;
|
||||
ginxlogCreatePostingTree data;
|
||||
|
||||
data.node = index->rd_node;
|
||||
data.blkno = blkno;
|
||||
@@ -71,7 +74,7 @@ createPostingTree( Relation index, ItemPointerData *items, uint32 nitems ) {
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MarkBufferDirty(buffer);
|
||||
UnlockReleaseBuffer(buffer);
|
||||
@@ -89,21 +92,25 @@ createPostingTree( Relation index, ItemPointerData *items, uint32 nitems ) {
|
||||
* GinFormTuple().
|
||||
*/
|
||||
static IndexTuple
|
||||
addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
|
||||
IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild) {
|
||||
bool isnull;
|
||||
Datum key = index_getattr(old, FirstOffsetNumber, ginstate->tupdesc, &isnull);
|
||||
IndexTuple res = GinFormTuple(ginstate, key, NULL, nitem + GinGetNPosting(old));
|
||||
addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
|
||||
IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild)
|
||||
{
|
||||
bool isnull;
|
||||
Datum key = index_getattr(old, FirstOffsetNumber, ginstate->tupdesc, &isnull);
|
||||
IndexTuple res = GinFormTuple(ginstate, key, NULL, nitem + GinGetNPosting(old));
|
||||
|
||||
if ( res ) {
|
||||
if (res)
|
||||
{
|
||||
/* good, small enough */
|
||||
MergeItemPointers( GinGetPosting(res),
|
||||
GinGetPosting(old), GinGetNPosting(old),
|
||||
items, nitem
|
||||
);
|
||||
|
||||
MergeItemPointers(GinGetPosting(res),
|
||||
GinGetPosting(old), GinGetNPosting(old),
|
||||
items, nitem
|
||||
);
|
||||
|
||||
GinSetNPosting(res, nitem + GinGetNPosting(old));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockNumber postingRoot;
|
||||
GinPostingTreeScan *gdi;
|
||||
|
||||
@@ -112,7 +119,7 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
|
||||
postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old));
|
||||
GinSetPostingTree(res, postingRoot);
|
||||
|
||||
gdi = prepareScanPostingTree(index, postingRoot, FALSE);
|
||||
gdi = prepareScanPostingTree(index, postingRoot, FALSE);
|
||||
gdi->btree.isBuild = isBuild;
|
||||
|
||||
insertItemPointer(gdi, items, nitem);
|
||||
@@ -124,36 +131,39 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts only one entry to the index, but it can adds more that 1
|
||||
* ItemPointer.
|
||||
* Inserts only one entry to the index, but it can adds more that 1
|
||||
* ItemPointer.
|
||||
*/
|
||||
static void
|
||||
ginEntryInsert( Relation index, GinState *ginstate, Datum value, ItemPointerData *items, uint32 nitem, bool isBuild) {
|
||||
GinBtreeData btree;
|
||||
ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData *items, uint32 nitem, bool isBuild)
|
||||
{
|
||||
GinBtreeData btree;
|
||||
GinBtreeStack *stack;
|
||||
IndexTuple itup;
|
||||
Page page;
|
||||
IndexTuple itup;
|
||||
Page page;
|
||||
|
||||
prepareEntryScan( &btree, index, value, ginstate );
|
||||
prepareEntryScan(&btree, index, value, ginstate);
|
||||
|
||||
stack = ginFindLeafPage(&btree, NULL);
|
||||
page = BufferGetPage( stack->buffer );
|
||||
page = BufferGetPage(stack->buffer);
|
||||
|
||||
if ( btree.findItem( &btree, stack ) ) {
|
||||
if (btree.findItem(&btree, stack))
|
||||
{
|
||||
/* found entry */
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
|
||||
|
||||
if ( GinIsPostingTree(itup) ) {
|
||||
if (GinIsPostingTree(itup))
|
||||
{
|
||||
/* lock root of posting tree */
|
||||
GinPostingTreeScan *gdi;
|
||||
BlockNumber rootPostingTree = GinGetPostingTree(itup);
|
||||
BlockNumber rootPostingTree = GinGetPostingTree(itup);
|
||||
|
||||
/* release all stack */
|
||||
LockBuffer(stack->buffer, GIN_UNLOCK);
|
||||
freeGinBtreeStack( stack );
|
||||
freeGinBtreeStack(stack);
|
||||
|
||||
/* insert into posting tree */
|
||||
gdi = prepareScanPostingTree( index, rootPostingTree, FALSE );
|
||||
gdi = prepareScanPostingTree(index, rootPostingTree, FALSE);
|
||||
gdi->btree.isBuild = isBuild;
|
||||
insertItemPointer(gdi, items, nitem);
|
||||
|
||||
@@ -163,23 +173,26 @@ ginEntryInsert( Relation index, GinState *ginstate, Datum value, ItemPointerData
|
||||
itup = addItemPointersToTuple(index, ginstate, stack, itup, items, nitem, isBuild);
|
||||
|
||||
btree.isDelete = TRUE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We suppose, that tuple can store at list one itempointer */
|
||||
itup = GinFormTuple( ginstate, value, items, 1);
|
||||
if ( itup==NULL || IndexTupleSize(itup) >= GinMaxItemSize )
|
||||
itup = GinFormTuple(ginstate, value, items, 1);
|
||||
if (itup == NULL || IndexTupleSize(itup) >= GinMaxItemSize)
|
||||
elog(ERROR, "huge tuple");
|
||||
|
||||
if ( nitem>1 ) {
|
||||
if (nitem > 1)
|
||||
{
|
||||
IndexTuple previtup = itup;
|
||||
|
||||
itup = addItemPointersToTuple(index, ginstate, stack, previtup, items+1, nitem-1, isBuild);
|
||||
itup = addItemPointersToTuple(index, ginstate, stack, previtup, items + 1, nitem - 1, isBuild);
|
||||
pfree(previtup);
|
||||
}
|
||||
}
|
||||
|
||||
btree.entry = itup;
|
||||
ginInsertValue(&btree, stack);
|
||||
pfree( itup );
|
||||
pfree(itup);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -187,48 +200,53 @@ ginEntryInsert( Relation index, GinState *ginstate, Datum value, ItemPointerData
|
||||
* Function isnt use during normal insert
|
||||
*/
|
||||
static uint32
|
||||
ginHeapTupleBulkInsert(GinBuildState *buildstate, Datum value, ItemPointer heapptr) {
|
||||
Datum *entries;
|
||||
uint32 nentries;
|
||||
ginHeapTupleBulkInsert(GinBuildState *buildstate, Datum value, ItemPointer heapptr)
|
||||
{
|
||||
Datum *entries;
|
||||
uint32 nentries;
|
||||
MemoryContext oldCtx;
|
||||
|
||||
oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
|
||||
entries = extractEntriesSU( buildstate->accum.ginstate, value, &nentries);
|
||||
entries = extractEntriesSU(buildstate->accum.ginstate, value, &nentries);
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
|
||||
if ( nentries==0 )
|
||||
if (nentries == 0)
|
||||
/* nothing to insert */
|
||||
return 0;
|
||||
|
||||
ginInsertRecordBA( &buildstate->accum, heapptr, entries, nentries);
|
||||
ginInsertRecordBA(&buildstate->accum, heapptr, entries, nentries);
|
||||
|
||||
MemoryContextReset(buildstate->funcCtx);
|
||||
|
||||
return nentries;
|
||||
}
|
||||
|
||||
static void
|
||||
static void
|
||||
ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
|
||||
bool *isnull, bool tupleIsAlive, void *state) {
|
||||
bool *isnull, bool tupleIsAlive, void *state)
|
||||
{
|
||||
|
||||
GinBuildState *buildstate = (GinBuildState*)state;
|
||||
GinBuildState *buildstate = (GinBuildState *) state;
|
||||
MemoryContext oldCtx;
|
||||
|
||||
if ( *isnull )
|
||||
if (*isnull)
|
||||
return;
|
||||
|
||||
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
||||
|
||||
buildstate->indtuples += ginHeapTupleBulkInsert(buildstate, *values, &htup->t_self);
|
||||
|
||||
/* we use only half maintenance_work_mem, because there is some leaks
|
||||
during insertion and extract values */
|
||||
if ( buildstate->accum.allocatedMemory >= maintenance_work_mem*1024L/2L ) {
|
||||
ItemPointerData *list;
|
||||
Datum entry;
|
||||
uint32 nlist;
|
||||
/*
|
||||
* we use only half maintenance_work_mem, because there is some leaks
|
||||
* during insertion and extract values
|
||||
*/
|
||||
if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L / 2L)
|
||||
{
|
||||
ItemPointerData *list;
|
||||
Datum entry;
|
||||
uint32 nlist;
|
||||
|
||||
while( (list=ginGetEntry(&buildstate->accum, &entry, &nlist)) != NULL )
|
||||
while ((list = ginGetEntry(&buildstate->accum, &entry, &nlist)) != NULL)
|
||||
ginEntryInsert(index, &buildstate->ginstate, entry, list, nlist, TRUE);
|
||||
|
||||
MemoryContextReset(buildstate->tmpCtx);
|
||||
@@ -239,22 +257,23 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
|
||||
}
|
||||
|
||||
Datum
|
||||
ginbuild(PG_FUNCTION_ARGS) {
|
||||
Relation heap = (Relation) PG_GETARG_POINTER(0);
|
||||
Relation index = (Relation) PG_GETARG_POINTER(1);
|
||||
ginbuild(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Relation heap = (Relation) PG_GETARG_POINTER(0);
|
||||
Relation index = (Relation) PG_GETARG_POINTER(1);
|
||||
IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
|
||||
IndexBuildResult *result;
|
||||
double reltuples;
|
||||
GinBuildState buildstate;
|
||||
double reltuples;
|
||||
GinBuildState buildstate;
|
||||
Buffer buffer;
|
||||
ItemPointerData *list;
|
||||
Datum entry;
|
||||
uint32 nlist;
|
||||
ItemPointerData *list;
|
||||
Datum entry;
|
||||
uint32 nlist;
|
||||
MemoryContext oldCtx;
|
||||
|
||||
if (RelationGetNumberOfBlocks(index) != 0)
|
||||
elog(ERROR, "index \"%s\" already contains data",
|
||||
RelationGetRelationName(index));
|
||||
RelationGetRelationName(index));
|
||||
|
||||
initGinState(&buildstate.ginstate, index);
|
||||
|
||||
@@ -262,10 +281,11 @@ ginbuild(PG_FUNCTION_ARGS) {
|
||||
buffer = GinNewBuffer(index);
|
||||
START_CRIT_SECTION();
|
||||
GinInitBuffer(buffer, GIN_LEAF);
|
||||
if (!index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
if (!index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
XLogRecData rdata;
|
||||
Page page;
|
||||
Page page;
|
||||
|
||||
rdata.buffer = InvalidBuffer;
|
||||
rdata.data = (char *) &(index->rd_node);
|
||||
@@ -279,7 +299,7 @@ ginbuild(PG_FUNCTION_ARGS) {
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MarkBufferDirty(buffer);
|
||||
UnlockReleaseBuffer(buffer);
|
||||
@@ -293,26 +313,26 @@ ginbuild(PG_FUNCTION_ARGS) {
|
||||
* inserted into the index
|
||||
*/
|
||||
buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Gin build temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
"Gin build temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
buildstate.funcCtx = AllocSetContextCreate(buildstate.tmpCtx,
|
||||
"Gin build temporary context for user-defined function",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
"Gin build temporary context for user-defined function",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
buildstate.accum.ginstate = &buildstate.ginstate;
|
||||
ginInitBA( &buildstate.accum );
|
||||
ginInitBA(&buildstate.accum);
|
||||
|
||||
/* do the heap scan */
|
||||
reltuples = IndexBuildHeapScan(heap, index, indexInfo,
|
||||
ginBuildCallback, (void *) &buildstate);
|
||||
ginBuildCallback, (void *) &buildstate);
|
||||
|
||||
oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
|
||||
while( (list=ginGetEntry(&buildstate.accum, &entry, &nlist)) != NULL )
|
||||
while ((list = ginGetEntry(&buildstate.accum, &entry, &nlist)) != NULL)
|
||||
ginEntryInsert(index, &buildstate.ginstate, entry, list, nlist, TRUE);
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
|
||||
@@ -333,55 +353,58 @@ ginbuild(PG_FUNCTION_ARGS) {
|
||||
* Inserts value during normal insertion
|
||||
*/
|
||||
static uint32
|
||||
ginHeapTupleInsert( Relation index, GinState *ginstate, Datum value, ItemPointer item) {
|
||||
Datum *entries;
|
||||
uint32 i,nentries;
|
||||
ginHeapTupleInsert(Relation index, GinState *ginstate, Datum value, ItemPointer item)
|
||||
{
|
||||
Datum *entries;
|
||||
uint32 i,
|
||||
nentries;
|
||||
|
||||
entries = extractEntriesSU( ginstate, value, &nentries);
|
||||
entries = extractEntriesSU(ginstate, value, &nentries);
|
||||
|
||||
if ( nentries==0 )
|
||||
if (nentries == 0)
|
||||
/* nothing to insert */
|
||||
return 0;
|
||||
|
||||
for(i=0;i<nentries;i++)
|
||||
for (i = 0; i < nentries; i++)
|
||||
ginEntryInsert(index, ginstate, entries[i], item, 1, FALSE);
|
||||
|
||||
return nentries;
|
||||
}
|
||||
|
||||
Datum
|
||||
gininsert(PG_FUNCTION_ARGS) {
|
||||
Relation index = (Relation) PG_GETARG_POINTER(0);
|
||||
Datum *values = (Datum *) PG_GETARG_POINTER(1);
|
||||
bool *isnull = (bool *) PG_GETARG_POINTER(2);
|
||||
gininsert(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Relation index = (Relation) PG_GETARG_POINTER(0);
|
||||
Datum *values = (Datum *) PG_GETARG_POINTER(1);
|
||||
bool *isnull = (bool *) PG_GETARG_POINTER(2);
|
||||
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
|
||||
|
||||
#ifdef NOT_USED
|
||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||
bool checkUnique = PG_GETARG_BOOL(5);
|
||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||
bool checkUnique = PG_GETARG_BOOL(5);
|
||||
#endif
|
||||
GinState ginstate;
|
||||
GinState ginstate;
|
||||
MemoryContext oldCtx;
|
||||
MemoryContext insertCtx;
|
||||
uint32 res;
|
||||
uint32 res;
|
||||
|
||||
if ( *isnull )
|
||||
if (*isnull)
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Gin insert temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
"Gin insert temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
oldCtx = MemoryContextSwitchTo(insertCtx);
|
||||
|
||||
initGinState(&ginstate, index);
|
||||
|
||||
res = ginHeapTupleInsert(index, &ginstate, *values, ht_ctid);
|
||||
res = ginHeapTupleInsert(index, &ginstate, *values, ht_ctid);
|
||||
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
MemoryContextDelete(insertCtx);
|
||||
|
||||
PG_RETURN_BOOL(res>0);
|
||||
PG_RETURN_BOOL(res > 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginscan.c
|
||||
* routines to manage scans inverted index relations
|
||||
* routines to manage scans inverted index relations
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.5 2006/09/14 11:26:49 teodor Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.6 2006/10/04 00:29:48 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
Datum
|
||||
ginbeginscan(PG_FUNCTION_ARGS) {
|
||||
Relation rel = (Relation) PG_GETARG_POINTER(0);
|
||||
int keysz = PG_GETARG_INT32(1);
|
||||
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(2);
|
||||
Datum
|
||||
ginbeginscan(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Relation rel = (Relation) PG_GETARG_POINTER(0);
|
||||
int keysz = PG_GETARG_INT32(1);
|
||||
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(2);
|
||||
IndexScanDesc scan;
|
||||
|
||||
scan = RelationGetIndexScan(rel, keysz, scankey);
|
||||
@@ -32,22 +33,25 @@ ginbeginscan(PG_FUNCTION_ARGS) {
|
||||
}
|
||||
|
||||
static void
|
||||
fillScanKey( GinState *ginstate, GinScanKey key, Datum query,
|
||||
Datum *entryValues, uint32 nEntryValues, StrategyNumber strategy ) {
|
||||
uint32 i,j;
|
||||
fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
|
||||
Datum *entryValues, uint32 nEntryValues, StrategyNumber strategy)
|
||||
{
|
||||
uint32 i,
|
||||
j;
|
||||
|
||||
key->nentries = nEntryValues;
|
||||
key->entryRes = (bool*)palloc0( sizeof(bool) * nEntryValues );
|
||||
key->scanEntry = (GinScanEntry) palloc( sizeof(GinScanEntryData) * nEntryValues );
|
||||
key->entryRes = (bool *) palloc0(sizeof(bool) * nEntryValues);
|
||||
key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nEntryValues);
|
||||
key->strategy = strategy;
|
||||
key->query = query;
|
||||
key->firstCall= TRUE;
|
||||
ItemPointerSet( &(key->curItem), InvalidBlockNumber, InvalidOffsetNumber );
|
||||
key->firstCall = TRUE;
|
||||
ItemPointerSet(&(key->curItem), InvalidBlockNumber, InvalidOffsetNumber);
|
||||
|
||||
for(i=0; i<nEntryValues; i++) {
|
||||
for (i = 0; i < nEntryValues; i++)
|
||||
{
|
||||
key->scanEntry[i].pval = key->entryRes + i;
|
||||
key->scanEntry[i].entry = entryValues[i];
|
||||
ItemPointerSet( &(key->scanEntry[i].curItem), InvalidBlockNumber, InvalidOffsetNumber );
|
||||
ItemPointerSet(&(key->scanEntry[i].curItem), InvalidBlockNumber, InvalidOffsetNumber);
|
||||
key->scanEntry[i].offset = InvalidOffsetNumber;
|
||||
key->scanEntry[i].buffer = InvalidBuffer;
|
||||
key->scanEntry[i].list = NULL;
|
||||
@@ -55,8 +59,9 @@ fillScanKey( GinState *ginstate, GinScanKey key, Datum query,
|
||||
|
||||
/* link to the equals entry in current scan key */
|
||||
key->scanEntry[i].master = NULL;
|
||||
for( j=0; j<i; j++)
|
||||
if ( compareEntries( ginstate, entryValues[i], entryValues[j] ) == 0 ) {
|
||||
for (j = 0; j < i; j++)
|
||||
if (compareEntries(ginstate, entryValues[i], entryValues[j]) == 0)
|
||||
{
|
||||
key->scanEntry[i].master = key->scanEntry + j;
|
||||
break;
|
||||
}
|
||||
@@ -66,23 +71,27 @@ fillScanKey( GinState *ginstate, GinScanKey key, Datum query,
|
||||
#ifdef NOT_USED
|
||||
|
||||
static void
|
||||
resetScanKeys(GinScanKey keys, uint32 nkeys) {
|
||||
uint32 i, j;
|
||||
resetScanKeys(GinScanKey keys, uint32 nkeys)
|
||||
{
|
||||
uint32 i,
|
||||
j;
|
||||
|
||||
if ( keys == NULL )
|
||||
if (keys == NULL)
|
||||
return;
|
||||
|
||||
for(i=0;i<nkeys;i++) {
|
||||
GinScanKey key = keys + i;
|
||||
for (i = 0; i < nkeys; i++)
|
||||
{
|
||||
GinScanKey key = keys + i;
|
||||
|
||||
key->firstCall = TRUE;
|
||||
ItemPointerSet( &(key->curItem), InvalidBlockNumber, InvalidOffsetNumber );
|
||||
ItemPointerSet(&(key->curItem), InvalidBlockNumber, InvalidOffsetNumber);
|
||||
|
||||
for(j=0;j<key->nentries;j++) {
|
||||
if ( key->scanEntry[j].buffer != InvalidBuffer )
|
||||
ReleaseBuffer( key->scanEntry[i].buffer );
|
||||
for (j = 0; j < key->nentries; j++)
|
||||
{
|
||||
if (key->scanEntry[j].buffer != InvalidBuffer)
|
||||
ReleaseBuffer(key->scanEntry[i].buffer);
|
||||
|
||||
ItemPointerSet( &(key->scanEntry[j].curItem), InvalidBlockNumber, InvalidOffsetNumber );
|
||||
ItemPointerSet(&(key->scanEntry[j].curItem), InvalidBlockNumber, InvalidOffsetNumber);
|
||||
key->scanEntry[j].offset = InvalidOffsetNumber;
|
||||
key->scanEntry[j].buffer = InvalidBuffer;
|
||||
key->scanEntry[j].list = NULL;
|
||||
@@ -90,111 +99,121 @@ resetScanKeys(GinScanKey keys, uint32 nkeys) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
freeScanKeys(GinScanKey keys, uint32 nkeys, bool removeRes) {
|
||||
uint32 i, j;
|
||||
freeScanKeys(GinScanKey keys, uint32 nkeys, bool removeRes)
|
||||
{
|
||||
uint32 i,
|
||||
j;
|
||||
|
||||
if ( keys == NULL )
|
||||
if (keys == NULL)
|
||||
return;
|
||||
|
||||
for(i=0;i<nkeys;i++) {
|
||||
GinScanKey key = keys + i;
|
||||
for (i = 0; i < nkeys; i++)
|
||||
{
|
||||
GinScanKey key = keys + i;
|
||||
|
||||
for(j=0;j<key->nentries;j++) {
|
||||
if ( key->scanEntry[j].buffer != InvalidBuffer )
|
||||
ReleaseBuffer( key->scanEntry[j].buffer );
|
||||
if ( removeRes && key->scanEntry[j].list )
|
||||
for (j = 0; j < key->nentries; j++)
|
||||
{
|
||||
if (key->scanEntry[j].buffer != InvalidBuffer)
|
||||
ReleaseBuffer(key->scanEntry[j].buffer);
|
||||
if (removeRes && key->scanEntry[j].list)
|
||||
pfree(key->scanEntry[j].list);
|
||||
}
|
||||
|
||||
if ( removeRes )
|
||||
if (removeRes)
|
||||
pfree(key->entryRes);
|
||||
pfree(key->scanEntry);
|
||||
}
|
||||
|
||||
|
||||
pfree(keys);
|
||||
}
|
||||
|
||||
void
|
||||
newScanKey( IndexScanDesc scan ) {
|
||||
ScanKey scankey = scan->keyData;
|
||||
newScanKey(IndexScanDesc scan)
|
||||
{
|
||||
ScanKey scankey = scan->keyData;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
int i;
|
||||
uint32 nkeys = 0;
|
||||
int i;
|
||||
uint32 nkeys = 0;
|
||||
|
||||
so->keys = (GinScanKey) palloc( scan->numberOfKeys * sizeof(GinScanKeyData) );
|
||||
so->keys = (GinScanKey) palloc(scan->numberOfKeys * sizeof(GinScanKeyData));
|
||||
|
||||
if (scan->numberOfKeys < 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("GIN indexes do not support whole-index scans")));
|
||||
errmsg("GIN indexes do not support whole-index scans")));
|
||||
|
||||
for(i=0; i<scan->numberOfKeys; i++) {
|
||||
Datum* entryValues;
|
||||
uint32 nEntryValues;
|
||||
for (i = 0; i < scan->numberOfKeys; i++)
|
||||
{
|
||||
Datum *entryValues;
|
||||
uint32 nEntryValues;
|
||||
|
||||
if ( scankey[i].sk_flags & SK_ISNULL )
|
||||
if (scankey[i].sk_flags & SK_ISNULL)
|
||||
elog(ERROR, "Gin doesn't support NULL as scan key");
|
||||
Assert( scankey[i].sk_attno == 1 );
|
||||
Assert(scankey[i].sk_attno == 1);
|
||||
|
||||
entryValues = (Datum*)DatumGetPointer(
|
||||
FunctionCall3(
|
||||
&so->ginstate.extractQueryFn,
|
||||
scankey[i].sk_argument,
|
||||
PointerGetDatum( &nEntryValues ),
|
||||
UInt16GetDatum(scankey[i].sk_strategy)
|
||||
)
|
||||
);
|
||||
if ( entryValues==NULL || nEntryValues == 0 )
|
||||
entryValues = (Datum *) DatumGetPointer(
|
||||
FunctionCall3(
|
||||
&so->ginstate.extractQueryFn,
|
||||
scankey[i].sk_argument,
|
||||
PointerGetDatum(&nEntryValues),
|
||||
UInt16GetDatum(scankey[i].sk_strategy)
|
||||
)
|
||||
);
|
||||
if (entryValues == NULL || nEntryValues == 0)
|
||||
/* full scan... */
|
||||
continue;
|
||||
|
||||
fillScanKey( &so->ginstate, &(so->keys[nkeys]), scankey[i].sk_argument,
|
||||
entryValues, nEntryValues, scankey[i].sk_strategy );
|
||||
fillScanKey(&so->ginstate, &(so->keys[nkeys]), scankey[i].sk_argument,
|
||||
entryValues, nEntryValues, scankey[i].sk_strategy);
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
so->nkeys = nkeys;
|
||||
|
||||
if ( so->nkeys == 0 )
|
||||
if (so->nkeys == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("GIN index doesn't support search with void query")));
|
||||
errmsg("GIN index doesn't support search with void query")));
|
||||
|
||||
pgstat_count_index_scan(&scan->xs_pgstat_info);
|
||||
}
|
||||
|
||||
Datum
|
||||
ginrescan(PG_FUNCTION_ARGS) {
|
||||
ginrescan(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
|
||||
GinScanOpaque so;
|
||||
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
|
||||
GinScanOpaque so;
|
||||
|
||||
so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
if ( so == NULL ) {
|
||||
if (so == NULL)
|
||||
{
|
||||
/* if called from ginbeginscan */
|
||||
so = (GinScanOpaque)palloc( sizeof(GinScanOpaqueData) );
|
||||
so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData));
|
||||
so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Gin scan temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
"Gin scan temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
initGinState(&so->ginstate, scan->indexRelation);
|
||||
scan->opaque = so;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
freeScanKeys(so->keys, so->nkeys, TRUE);
|
||||
freeScanKeys(so->markPos, so->nkeys, FALSE);
|
||||
}
|
||||
|
||||
so->markPos=so->keys=NULL;
|
||||
so->markPos = so->keys = NULL;
|
||||
|
||||
if ( scankey && scan->numberOfKeys > 0 ) {
|
||||
if (scankey && scan->numberOfKeys > 0)
|
||||
{
|
||||
memmove(scan->keyData, scankey,
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
PG_RETURN_VOID();
|
||||
@@ -202,13 +221,15 @@ ginrescan(PG_FUNCTION_ARGS) {
|
||||
|
||||
|
||||
Datum
|
||||
ginendscan(PG_FUNCTION_ARGS) {
|
||||
ginendscan(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
if ( so != NULL ) {
|
||||
freeScanKeys(so->keys, so->nkeys, TRUE);
|
||||
freeScanKeys(so->markPos, so->nkeys, FALSE);
|
||||
if (so != NULL)
|
||||
{
|
||||
freeScanKeys(so->keys, so->nkeys, TRUE);
|
||||
freeScanKeys(so->markPos, so->nkeys, FALSE);
|
||||
|
||||
MemoryContextDelete(so->tempCtx);
|
||||
|
||||
@@ -219,22 +240,28 @@ ginendscan(PG_FUNCTION_ARGS) {
|
||||
}
|
||||
|
||||
static GinScanKey
|
||||
copyScanKeys( GinScanKey keys, uint32 nkeys ) {
|
||||
copyScanKeys(GinScanKey keys, uint32 nkeys)
|
||||
{
|
||||
GinScanKey newkeys;
|
||||
uint32 i, j;
|
||||
uint32 i,
|
||||
j;
|
||||
|
||||
newkeys = (GinScanKey)palloc( sizeof(GinScanKeyData) * nkeys );
|
||||
memcpy( newkeys, keys, sizeof(GinScanKeyData) * nkeys );
|
||||
newkeys = (GinScanKey) palloc(sizeof(GinScanKeyData) * nkeys);
|
||||
memcpy(newkeys, keys, sizeof(GinScanKeyData) * nkeys);
|
||||
|
||||
for(i=0;i<nkeys;i++) {
|
||||
newkeys[i].scanEntry = (GinScanEntry)palloc(sizeof(GinScanEntryData) * keys[i].nentries );
|
||||
memcpy( newkeys[i].scanEntry, keys[i].scanEntry, sizeof(GinScanEntryData) * keys[i].nentries );
|
||||
for (i = 0; i < nkeys; i++)
|
||||
{
|
||||
newkeys[i].scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * keys[i].nentries);
|
||||
memcpy(newkeys[i].scanEntry, keys[i].scanEntry, sizeof(GinScanEntryData) * keys[i].nentries);
|
||||
|
||||
for (j = 0; j < keys[i].nentries; j++)
|
||||
{
|
||||
if (keys[i].scanEntry[j].buffer != InvalidBuffer)
|
||||
IncrBufferRefCount(keys[i].scanEntry[j].buffer);
|
||||
if (keys[i].scanEntry[j].master)
|
||||
{
|
||||
int masterN = keys[i].scanEntry[j].master - keys[i].scanEntry;
|
||||
|
||||
for(j=0;j<keys[i].nentries; j++) {
|
||||
if ( keys[i].scanEntry[j].buffer != InvalidBuffer )
|
||||
IncrBufferRefCount( keys[i].scanEntry[j].buffer );
|
||||
if ( keys[i].scanEntry[j].master ) {
|
||||
int masterN = keys[i].scanEntry[j].master - keys[i].scanEntry;
|
||||
newkeys[i].scanEntry[j].master = newkeys[i].scanEntry + masterN;
|
||||
}
|
||||
}
|
||||
@@ -243,24 +270,26 @@ copyScanKeys( GinScanKey keys, uint32 nkeys ) {
|
||||
return newkeys;
|
||||
}
|
||||
|
||||
Datum
|
||||
ginmarkpos(PG_FUNCTION_ARGS) {
|
||||
Datum
|
||||
ginmarkpos(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
freeScanKeys(so->markPos, so->nkeys, FALSE);
|
||||
so->markPos = copyScanKeys( so->keys, so->nkeys );
|
||||
so->markPos = copyScanKeys(so->keys, so->nkeys);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
Datum
|
||||
ginrestrpos(PG_FUNCTION_ARGS) {
|
||||
Datum
|
||||
ginrestrpos(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
GinScanOpaque so = (GinScanOpaque) scan->opaque;
|
||||
|
||||
freeScanKeys(so->keys, so->nkeys, FALSE);
|
||||
so->keys = copyScanKeys( so->markPos, so->nkeys );
|
||||
so->keys = copyScanKeys(so->markPos, so->nkeys);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginutil.c
|
||||
* utilities routines for the postgres inverted index access method.
|
||||
* utilities routines for the postgres inverted index access method.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.6 2006/09/05 18:25:10 teodor Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.7 2006/10/04 00:29:48 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -19,26 +19,27 @@
|
||||
#include "access/reloptions.h"
|
||||
#include "storage/freespace.h"
|
||||
|
||||
void
|
||||
initGinState( GinState *state, Relation index ) {
|
||||
if ( index->rd_att->natts != 1 )
|
||||
elog(ERROR, "numberOfAttributes %d != 1",
|
||||
index->rd_att->natts);
|
||||
|
||||
void
|
||||
initGinState(GinState *state, Relation index)
|
||||
{
|
||||
if (index->rd_att->natts != 1)
|
||||
elog(ERROR, "numberOfAttributes %d != 1",
|
||||
index->rd_att->natts);
|
||||
|
||||
state->tupdesc = index->rd_att;
|
||||
|
||||
fmgr_info_copy(&(state->compareFn),
|
||||
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
|
||||
CurrentMemoryContext);
|
||||
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
|
||||
CurrentMemoryContext);
|
||||
fmgr_info_copy(&(state->extractValueFn),
|
||||
index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
|
||||
CurrentMemoryContext);
|
||||
index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
|
||||
CurrentMemoryContext);
|
||||
fmgr_info_copy(&(state->extractQueryFn),
|
||||
index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
|
||||
CurrentMemoryContext);
|
||||
index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
|
||||
CurrentMemoryContext);
|
||||
fmgr_info_copy(&(state->consistentFn),
|
||||
index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
|
||||
CurrentMemoryContext);
|
||||
index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
|
||||
CurrentMemoryContext);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -48,13 +49,16 @@ initGinState( GinState *state, Relation index ) {
|
||||
*/
|
||||
|
||||
Buffer
|
||||
GinNewBuffer(Relation index) {
|
||||
Buffer buffer;
|
||||
bool needLock;
|
||||
GinNewBuffer(Relation index)
|
||||
{
|
||||
Buffer buffer;
|
||||
bool needLock;
|
||||
|
||||
/* First, try to get a page from FSM */
|
||||
for(;;) {
|
||||
for (;;)
|
||||
{
|
||||
BlockNumber blkno = GetFreeIndexPage(&index->rd_node);
|
||||
|
||||
if (blkno == InvalidBlockNumber)
|
||||
break;
|
||||
|
||||
@@ -64,14 +68,15 @@ GinNewBuffer(Relation index) {
|
||||
* We have to guard against the possibility that someone else already
|
||||
* recycled this page; the buffer may be locked if so.
|
||||
*/
|
||||
if (ConditionalLockBuffer(buffer)) {
|
||||
Page page = BufferGetPage(buffer);
|
||||
if (ConditionalLockBuffer(buffer))
|
||||
{
|
||||
Page page = BufferGetPage(buffer);
|
||||
|
||||
if (PageIsNew(page))
|
||||
return buffer; /* OK to use, if never initialized */
|
||||
return buffer; /* OK to use, if never initialized */
|
||||
|
||||
if (GinPageIsDeleted(page))
|
||||
return buffer; /* OK to use */
|
||||
return buffer; /* OK to use */
|
||||
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
}
|
||||
@@ -95,36 +100,39 @@ GinNewBuffer(Relation index) {
|
||||
}
|
||||
|
||||
void
|
||||
GinInitPage(Page page, uint32 f, Size pageSize) {
|
||||
GinInitPage(Page page, uint32 f, Size pageSize)
|
||||
{
|
||||
GinPageOpaque opaque;
|
||||
|
||||
PageInit(page, pageSize, sizeof(GinPageOpaqueData));
|
||||
|
||||
opaque = GinPageGetOpaque(page);
|
||||
memset( opaque, 0, sizeof(GinPageOpaqueData) );
|
||||
opaque->flags = f;
|
||||
memset(opaque, 0, sizeof(GinPageOpaqueData));
|
||||
opaque->flags = f;
|
||||
opaque->rightlink = InvalidBlockNumber;
|
||||
}
|
||||
|
||||
void
|
||||
GinInitBuffer(Buffer b, uint32 f) {
|
||||
GinInitPage( BufferGetPage(b), f, BufferGetPageSize(b) );
|
||||
GinInitBuffer(Buffer b, uint32 f)
|
||||
{
|
||||
GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
|
||||
}
|
||||
|
||||
int
|
||||
compareEntries(GinState *ginstate, Datum a, Datum b) {
|
||||
compareEntries(GinState *ginstate, Datum a, Datum b)
|
||||
{
|
||||
return DatumGetInt32(
|
||||
FunctionCall2(
|
||||
&ginstate->compareFn,
|
||||
a, b
|
||||
)
|
||||
FunctionCall2(
|
||||
&ginstate->compareFn,
|
||||
a, b
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static FmgrInfo* cmpDatumPtr=NULL;
|
||||
static FmgrInfo *cmpDatumPtr = NULL;
|
||||
|
||||
#if defined(__INTEL_COMPILER) && (defined(__ia64__) || defined(__ia64))
|
||||
/*
|
||||
#if defined(__INTEL_COMPILER) && (defined(__ia64__) || defined(__ia64))
|
||||
/*
|
||||
* Intel Compiler on Intel Itanium with -O2 has a bug around
|
||||
* change static variable by user function called from
|
||||
* libc func: it doesn't change. So mark it as volatile.
|
||||
@@ -132,7 +140,7 @@ static FmgrInfo* cmpDatumPtr=NULL;
|
||||
* It's a pity, but it's impossible to define optimization
|
||||
* level here.
|
||||
*/
|
||||
#define VOLATILE volatile
|
||||
#define VOLATILE volatile
|
||||
#else
|
||||
#define VOLATILE
|
||||
#endif
|
||||
@@ -140,57 +148,64 @@ static FmgrInfo* cmpDatumPtr=NULL;
|
||||
static bool VOLATILE needUnique = FALSE;
|
||||
|
||||
static int
|
||||
cmpEntries(const void * a, const void * b) {
|
||||
int res = DatumGetInt32(
|
||||
FunctionCall2(
|
||||
cmpDatumPtr,
|
||||
*(Datum*)a,
|
||||
*(Datum*)b
|
||||
)
|
||||
cmpEntries(const void *a, const void *b)
|
||||
{
|
||||
int res = DatumGetInt32(
|
||||
FunctionCall2(
|
||||
cmpDatumPtr,
|
||||
*(Datum *) a,
|
||||
*(Datum *) b
|
||||
)
|
||||
);
|
||||
|
||||
if ( res == 0 )
|
||||
if (res == 0)
|
||||
needUnique = TRUE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Datum*
|
||||
extractEntriesS(GinState *ginstate, Datum value, uint32 *nentries) {
|
||||
Datum *entries;
|
||||
Datum *
|
||||
extractEntriesS(GinState *ginstate, Datum value, uint32 *nentries)
|
||||
{
|
||||
Datum *entries;
|
||||
|
||||
entries = (Datum*)DatumGetPointer(
|
||||
FunctionCall2(
|
||||
&ginstate->extractValueFn,
|
||||
value,
|
||||
PointerGetDatum( nentries )
|
||||
)
|
||||
);
|
||||
entries = (Datum *) DatumGetPointer(
|
||||
FunctionCall2(
|
||||
&ginstate->extractValueFn,
|
||||
value,
|
||||
PointerGetDatum(nentries)
|
||||
)
|
||||
);
|
||||
|
||||
if ( entries == NULL )
|
||||
if (entries == NULL)
|
||||
*nentries = 0;
|
||||
|
||||
if ( *nentries > 1 ) {
|
||||
if (*nentries > 1)
|
||||
{
|
||||
cmpDatumPtr = &ginstate->compareFn;
|
||||
needUnique = FALSE;
|
||||
qsort(entries, *nentries, sizeof(Datum), cmpEntries);
|
||||
qsort(entries, *nentries, sizeof(Datum), cmpEntries);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
Datum*
|
||||
extractEntriesSU(GinState *ginstate, Datum value, uint32 *nentries) {
|
||||
Datum *entries = extractEntriesS(ginstate, value, nentries);
|
||||
Datum *
|
||||
extractEntriesSU(GinState *ginstate, Datum value, uint32 *nentries)
|
||||
{
|
||||
Datum *entries = extractEntriesS(ginstate, value, nentries);
|
||||
|
||||
if ( *nentries>1 && needUnique ) {
|
||||
Datum *ptr, *res;
|
||||
if (*nentries > 1 && needUnique)
|
||||
{
|
||||
Datum *ptr,
|
||||
*res;
|
||||
|
||||
ptr = res = entries;
|
||||
|
||||
while( ptr - entries < *nentries ) {
|
||||
if ( compareEntries(ginstate, *ptr, *res ) != 0 )
|
||||
while (ptr - entries < *nentries)
|
||||
{
|
||||
if (compareEntries(ginstate, *ptr, *res) != 0)
|
||||
*(++res) = *ptr++;
|
||||
else
|
||||
ptr++;
|
||||
@@ -206,13 +221,14 @@ extractEntriesSU(GinState *ginstate, Datum value, uint32 *nentries) {
|
||||
* It's analog of PageGetTempPage(), but copies whole page
|
||||
*/
|
||||
Page
|
||||
GinPageGetCopyPage( Page page ) {
|
||||
Size pageSize = PageGetPageSize( page );
|
||||
Page tmppage;
|
||||
GinPageGetCopyPage(Page page)
|
||||
{
|
||||
Size pageSize = PageGetPageSize(page);
|
||||
Page tmppage;
|
||||
|
||||
tmppage = (Page) palloc(pageSize);
|
||||
memcpy(tmppage, page, pageSize);
|
||||
|
||||
tmppage=(Page)palloc( pageSize );
|
||||
memcpy( tmppage, page, pageSize );
|
||||
|
||||
return tmppage;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ginvacuum.c
|
||||
* delete & vacuum routines for the postgres GIN
|
||||
* delete & vacuum routines for the postgres GIN
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.6 2006/09/21 20:31:21 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.7 2006/10/04 00:29:48 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -21,42 +21,50 @@
|
||||
#include "storage/freespace.h"
|
||||
#include "commands/vacuum.h"
|
||||
|
||||
typedef struct {
|
||||
Relation index;
|
||||
IndexBulkDeleteResult *result;
|
||||
IndexBulkDeleteCallback callback;
|
||||
void *callback_state;
|
||||
GinState ginstate;
|
||||
typedef struct
|
||||
{
|
||||
Relation index;
|
||||
IndexBulkDeleteResult *result;
|
||||
IndexBulkDeleteCallback callback;
|
||||
void *callback_state;
|
||||
GinState ginstate;
|
||||
} GinVacuumState;
|
||||
|
||||
|
||||
/*
|
||||
* Cleans array of ItemPointer (removes dead pointers)
|
||||
* Results are always stored in *cleaned, which will be allocated
|
||||
* if its needed. In case of *cleaned!=NULL caller is resposible to
|
||||
* if its needed. In case of *cleaned!=NULL caller is resposible to
|
||||
* enough space. *cleaned and items may point to the same
|
||||
* memory addres.
|
||||
*/
|
||||
|
||||
static uint32
|
||||
ginVacuumPostingList( GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned ) {
|
||||
uint32 i,j=0;
|
||||
ginVacuumPostingList(GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned)
|
||||
{
|
||||
uint32 i,
|
||||
j = 0;
|
||||
|
||||
/*
|
||||
* just scan over ItemPointer array
|
||||
*/
|
||||
|
||||
for(i=0;i<nitem;i++) {
|
||||
if ( gvs->callback(items+i, gvs->callback_state) ) {
|
||||
for (i = 0; i < nitem; i++)
|
||||
{
|
||||
if (gvs->callback(items + i, gvs->callback_state))
|
||||
{
|
||||
gvs->result->tuples_removed += 1;
|
||||
if ( !*cleaned ) {
|
||||
*cleaned = (ItemPointerData*)palloc(sizeof(ItemPointerData)*nitem);
|
||||
if ( i!=0 )
|
||||
memcpy( *cleaned, items, sizeof(ItemPointerData)*i);
|
||||
if (!*cleaned)
|
||||
{
|
||||
*cleaned = (ItemPointerData *) palloc(sizeof(ItemPointerData) * nitem);
|
||||
if (i != 0)
|
||||
memcpy(*cleaned, items, sizeof(ItemPointerData) * i);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
gvs->result->num_index_tuples += 1;
|
||||
if (i!=j)
|
||||
if (i != j)
|
||||
(*cleaned)[j] = items[i];
|
||||
j++;
|
||||
}
|
||||
@@ -69,56 +77,65 @@ ginVacuumPostingList( GinVacuumState *gvs, ItemPointerData *items, uint32 nitem,
|
||||
* fills WAL record for vacuum leaf page
|
||||
*/
|
||||
static void
|
||||
xlogVacuumPage(Relation index, Buffer buffer) {
|
||||
Page page = BufferGetPage( buffer );
|
||||
XLogRecPtr recptr;
|
||||
xlogVacuumPage(Relation index, Buffer buffer)
|
||||
{
|
||||
Page page = BufferGetPage(buffer);
|
||||
XLogRecPtr recptr;
|
||||
XLogRecData rdata[3];
|
||||
ginxlogVacuumPage data;
|
||||
char *backup;
|
||||
char itups[BLCKSZ];
|
||||
uint32 len=0;
|
||||
ginxlogVacuumPage data;
|
||||
char *backup;
|
||||
char itups[BLCKSZ];
|
||||
uint32 len = 0;
|
||||
|
||||
Assert( GinPageIsLeaf( page ) );
|
||||
Assert(GinPageIsLeaf(page));
|
||||
|
||||
if (index->rd_istemp)
|
||||
return;
|
||||
return;
|
||||
|
||||
data.node = index->rd_node;
|
||||
data.blkno = BufferGetBlockNumber(buffer);
|
||||
|
||||
if ( GinPageIsData( page ) ) {
|
||||
backup = GinDataPageGetData( page );
|
||||
data.nitem = GinPageGetOpaque( page )->maxoff;
|
||||
if ( data.nitem )
|
||||
len = MAXALIGN( sizeof(ItemPointerData)*data.nitem );
|
||||
} else {
|
||||
char *ptr;
|
||||
if (GinPageIsData(page))
|
||||
{
|
||||
backup = GinDataPageGetData(page);
|
||||
data.nitem = GinPageGetOpaque(page)->maxoff;
|
||||
if (data.nitem)
|
||||
len = MAXALIGN(sizeof(ItemPointerData) * data.nitem);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *ptr;
|
||||
OffsetNumber i;
|
||||
|
||||
ptr = backup = itups;
|
||||
for(i=FirstOffsetNumber;i<=PageGetMaxOffsetNumber(page);i++) {
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
|
||||
memcpy( ptr, itup, IndexTupleSize( itup ) );
|
||||
ptr += MAXALIGN( IndexTupleSize( itup ) );
|
||||
for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
|
||||
|
||||
memcpy(ptr, itup, IndexTupleSize(itup));
|
||||
ptr += MAXALIGN(IndexTupleSize(itup));
|
||||
}
|
||||
|
||||
data.nitem = PageGetMaxOffsetNumber(page);
|
||||
len = ptr-backup;
|
||||
len = ptr - backup;
|
||||
}
|
||||
|
||||
rdata[0].buffer = buffer;
|
||||
rdata[0].buffer_std = ( GinPageIsData( page ) ) ? FALSE : TRUE;
|
||||
rdata[0].buffer_std = (GinPageIsData(page)) ? FALSE : TRUE;
|
||||
rdata[0].len = 0;
|
||||
rdata[0].data = NULL;
|
||||
rdata[0].next = rdata + 1;
|
||||
|
||||
rdata[1].buffer = InvalidBuffer;
|
||||
rdata[1].len = sizeof(ginxlogVacuumPage);
|
||||
rdata[1].data = (char*)&data;
|
||||
rdata[1].data = (char *) &data;
|
||||
|
||||
if ( len == 0 ) {
|
||||
if (len == 0)
|
||||
{
|
||||
rdata[1].next = NULL;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
rdata[1].next = rdata + 2;
|
||||
|
||||
rdata[2].buffer = InvalidBuffer;
|
||||
@@ -133,71 +150,84 @@ xlogVacuumPage(Relation index, Buffer buffer) {
|
||||
}
|
||||
|
||||
static bool
|
||||
ginVacuumPostingTreeLeaves( GinVacuumState *gvs, BlockNumber blkno, bool isRoot, Buffer *rootBuffer ) {
|
||||
Buffer buffer = ReadBuffer( gvs->index, blkno );
|
||||
Page page = BufferGetPage( buffer );
|
||||
bool hasVoidPage = FALSE;
|
||||
ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, Buffer *rootBuffer)
|
||||
{
|
||||
Buffer buffer = ReadBuffer(gvs->index, blkno);
|
||||
Page page = BufferGetPage(buffer);
|
||||
bool hasVoidPage = FALSE;
|
||||
|
||||
/*
|
||||
/*
|
||||
* We should be sure that we don't concurrent with inserts, insert process
|
||||
* never release root page until end (but it can unlock it and lock again).
|
||||
* If we lock root with with LockBufferForCleanup, new scan process can't begin,
|
||||
* but previous may run.
|
||||
* ginmarkpos/start* keeps buffer pinned, so we will wait for it.
|
||||
* We lock only one posting tree in whole index, so, it's concurrent enough..
|
||||
* Side effect: after this is full complete, tree is unused by any other process
|
||||
* never release root page until end (but it can unlock it and lock
|
||||
* again). If we lock root with with LockBufferForCleanup, new scan
|
||||
* process can't begin, but previous may run. ginmarkpos/start* keeps
|
||||
* buffer pinned, so we will wait for it. We lock only one posting tree in
|
||||
* whole index, so, it's concurrent enough.. Side effect: after this is
|
||||
* full complete, tree is unused by any other process
|
||||
*/
|
||||
|
||||
LockBufferForCleanup( buffer );
|
||||
LockBufferForCleanup(buffer);
|
||||
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( GinPageIsLeaf(page) ) {
|
||||
OffsetNumber newMaxOff, oldMaxOff = GinPageGetOpaque(page)->maxoff;
|
||||
if (GinPageIsLeaf(page))
|
||||
{
|
||||
OffsetNumber newMaxOff,
|
||||
oldMaxOff = GinPageGetOpaque(page)->maxoff;
|
||||
ItemPointerData *cleaned = NULL;
|
||||
|
||||
newMaxOff = ginVacuumPostingList( gvs,
|
||||
(ItemPointer)GinDataPageGetData(page), oldMaxOff, &cleaned );
|
||||
newMaxOff = ginVacuumPostingList(gvs,
|
||||
(ItemPointer) GinDataPageGetData(page), oldMaxOff, &cleaned);
|
||||
|
||||
/* saves changes about deleted tuple ... */
|
||||
if ( oldMaxOff != newMaxOff ) {
|
||||
if (oldMaxOff != newMaxOff)
|
||||
{
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
if ( newMaxOff > 0 )
|
||||
memcpy( GinDataPageGetData(page), cleaned, sizeof(ItemPointerData) * newMaxOff );
|
||||
pfree( cleaned );
|
||||
if (newMaxOff > 0)
|
||||
memcpy(GinDataPageGetData(page), cleaned, sizeof(ItemPointerData) * newMaxOff);
|
||||
pfree(cleaned);
|
||||
GinPageGetOpaque(page)->maxoff = newMaxOff;
|
||||
|
||||
xlogVacuumPage(gvs->index, buffer);
|
||||
xlogVacuumPage(gvs->index, buffer);
|
||||
|
||||
MarkBufferDirty( buffer );
|
||||
MarkBufferDirty(buffer);
|
||||
END_CRIT_SECTION();
|
||||
|
||||
/* if root is a leaf page, we don't desire futher processing */
|
||||
if ( !isRoot && GinPageGetOpaque(page)->maxoff < FirstOffsetNumber )
|
||||
|
||||
/* if root is a leaf page, we don't desire futher processing */
|
||||
if (!isRoot && GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
|
||||
hasVoidPage = TRUE;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
OffsetNumber i;
|
||||
bool isChildHasVoid = FALSE;
|
||||
bool isChildHasVoid = FALSE;
|
||||
|
||||
for( i=FirstOffsetNumber ; i <= GinPageGetOpaque(page)->maxoff ; i++ ) {
|
||||
PostingItem *pitem = (PostingItem*)GinDataPageGetItem(page, i);
|
||||
if ( ginVacuumPostingTreeLeaves( gvs, PostingItemGetBlockNumber(pitem), FALSE, NULL ) )
|
||||
for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
|
||||
{
|
||||
PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);
|
||||
|
||||
if (ginVacuumPostingTreeLeaves(gvs, PostingItemGetBlockNumber(pitem), FALSE, NULL))
|
||||
isChildHasVoid = TRUE;
|
||||
}
|
||||
|
||||
if ( isChildHasVoid )
|
||||
if (isChildHasVoid)
|
||||
hasVoidPage = TRUE;
|
||||
}
|
||||
|
||||
/* if we have root and theres void pages in tree, then we don't release lock
|
||||
to go further processing and guarantee that tree is unused */
|
||||
if ( !(isRoot && hasVoidPage) ) {
|
||||
UnlockReleaseBuffer( buffer );
|
||||
} else {
|
||||
Assert( rootBuffer );
|
||||
/*
|
||||
* if we have root and theres void pages in tree, then we don't release
|
||||
* lock to go further processing and guarantee that tree is unused
|
||||
*/
|
||||
if (!(isRoot && hasVoidPage))
|
||||
{
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(rootBuffer);
|
||||
*rootBuffer = buffer;
|
||||
}
|
||||
|
||||
@@ -205,49 +235,54 @@ ginVacuumPostingTreeLeaves( GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
|
||||
}
|
||||
|
||||
static void
|
||||
ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno,
|
||||
BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot ) {
|
||||
Buffer dBuffer = ReadBuffer( gvs->index, deleteBlkno );
|
||||
Buffer lBuffer = (leftBlkno==InvalidBlockNumber) ? InvalidBuffer : ReadBuffer( gvs->index, leftBlkno );
|
||||
Buffer pBuffer = ReadBuffer( gvs->index, parentBlkno );
|
||||
Page page, parentPage;
|
||||
ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno,
|
||||
BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
|
||||
{
|
||||
Buffer dBuffer = ReadBuffer(gvs->index, deleteBlkno);
|
||||
Buffer lBuffer = (leftBlkno == InvalidBlockNumber) ? InvalidBuffer : ReadBuffer(gvs->index, leftBlkno);
|
||||
Buffer pBuffer = ReadBuffer(gvs->index, parentBlkno);
|
||||
Page page,
|
||||
parentPage;
|
||||
|
||||
LockBuffer( dBuffer, GIN_EXCLUSIVE );
|
||||
if ( !isParentRoot ) /* parent is already locked by LockBufferForCleanup() */
|
||||
LockBuffer( pBuffer, GIN_EXCLUSIVE );
|
||||
LockBuffer(dBuffer, GIN_EXCLUSIVE);
|
||||
if (!isParentRoot) /* parent is already locked by
|
||||
* LockBufferForCleanup() */
|
||||
LockBuffer(pBuffer, GIN_EXCLUSIVE);
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
if ( leftBlkno!= InvalidBlockNumber ) {
|
||||
if (leftBlkno != InvalidBlockNumber)
|
||||
{
|
||||
BlockNumber rightlink;
|
||||
|
||||
LockBuffer( lBuffer, GIN_EXCLUSIVE );
|
||||
LockBuffer(lBuffer, GIN_EXCLUSIVE);
|
||||
|
||||
page = BufferGetPage( dBuffer );
|
||||
page = BufferGetPage(dBuffer);
|
||||
rightlink = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
page = BufferGetPage( lBuffer );
|
||||
page = BufferGetPage(lBuffer);
|
||||
GinPageGetOpaque(page)->rightlink = rightlink;
|
||||
}
|
||||
|
||||
parentPage = BufferGetPage( pBuffer );
|
||||
parentPage = BufferGetPage(pBuffer);
|
||||
PageDeletePostingItem(parentPage, myoff);
|
||||
|
||||
page = BufferGetPage( dBuffer );
|
||||
page = BufferGetPage(dBuffer);
|
||||
GinPageGetOpaque(page)->flags = GIN_DELETED;
|
||||
|
||||
if (!gvs->index->rd_istemp) {
|
||||
XLogRecPtr recptr;
|
||||
if (!gvs->index->rd_istemp)
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
XLogRecData rdata[4];
|
||||
ginxlogDeletePage data;
|
||||
int n;
|
||||
ginxlogDeletePage data;
|
||||
int n;
|
||||
|
||||
data.node = gvs->index->rd_node;
|
||||
data.blkno = deleteBlkno;
|
||||
data.parentBlkno = parentBlkno;
|
||||
data.parentOffset = myoff;
|
||||
data.leftBlkno = leftBlkno;
|
||||
data.rightLink = GinPageGetOpaque(page)->rightlink;
|
||||
data.leftBlkno = leftBlkno;
|
||||
data.rightLink = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
rdata[0].buffer = dBuffer;
|
||||
rdata[0].buffer_std = FALSE;
|
||||
@@ -261,20 +296,22 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
|
||||
rdata[1].len = 0;
|
||||
rdata[1].next = rdata + 2;
|
||||
|
||||
if ( leftBlkno!= InvalidBlockNumber ) {
|
||||
if (leftBlkno != InvalidBlockNumber)
|
||||
{
|
||||
rdata[2].buffer = lBuffer;
|
||||
rdata[2].buffer_std = FALSE;
|
||||
rdata[2].data = NULL;
|
||||
rdata[2].len = 0;
|
||||
rdata[2].next = rdata + 3;
|
||||
n = 3;
|
||||
} else
|
||||
}
|
||||
else
|
||||
n = 2;
|
||||
|
||||
rdata[n].buffer = InvalidBuffer;
|
||||
rdata[n].buffer_std = FALSE;
|
||||
rdata[n].len = sizeof(ginxlogDeletePage);
|
||||
rdata[n].data = (char*)&data;
|
||||
rdata[n].data = (char *) &data;
|
||||
rdata[n].next = NULL;
|
||||
|
||||
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE, rdata);
|
||||
@@ -282,122 +319,141 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
PageSetLSN(parentPage, recptr);
|
||||
PageSetTLI(parentPage, ThisTimeLineID);
|
||||
if ( leftBlkno!= InvalidBlockNumber ) {
|
||||
page = BufferGetPage( lBuffer );
|
||||
if (leftBlkno != InvalidBlockNumber)
|
||||
{
|
||||
page = BufferGetPage(lBuffer);
|
||||
PageSetLSN(page, recptr);
|
||||
PageSetTLI(page, ThisTimeLineID);
|
||||
}
|
||||
}
|
||||
|
||||
MarkBufferDirty( pBuffer );
|
||||
if ( !isParentRoot )
|
||||
LockBuffer( pBuffer, GIN_UNLOCK );
|
||||
ReleaseBuffer( pBuffer );
|
||||
MarkBufferDirty(pBuffer);
|
||||
if (!isParentRoot)
|
||||
LockBuffer(pBuffer, GIN_UNLOCK);
|
||||
ReleaseBuffer(pBuffer);
|
||||
|
||||
if ( leftBlkno!= InvalidBlockNumber ) {
|
||||
MarkBufferDirty( lBuffer );
|
||||
UnlockReleaseBuffer( lBuffer );
|
||||
if (leftBlkno != InvalidBlockNumber)
|
||||
{
|
||||
MarkBufferDirty(lBuffer);
|
||||
UnlockReleaseBuffer(lBuffer);
|
||||
}
|
||||
|
||||
MarkBufferDirty( dBuffer );
|
||||
UnlockReleaseBuffer( dBuffer );
|
||||
MarkBufferDirty(dBuffer);
|
||||
UnlockReleaseBuffer(dBuffer);
|
||||
|
||||
END_CRIT_SECTION();
|
||||
|
||||
gvs->result->pages_deleted++;
|
||||
}
|
||||
|
||||
typedef struct DataPageDeleteStack {
|
||||
struct DataPageDeleteStack *child;
|
||||
struct DataPageDeleteStack *parent;
|
||||
typedef struct DataPageDeleteStack
|
||||
{
|
||||
struct DataPageDeleteStack *child;
|
||||
struct DataPageDeleteStack *parent;
|
||||
|
||||
BlockNumber blkno;
|
||||
bool isRoot;
|
||||
BlockNumber blkno;
|
||||
bool isRoot;
|
||||
} DataPageDeleteStack;
|
||||
|
||||
/*
|
||||
* scans posting tree and deletes empty pages
|
||||
*/
|
||||
static bool
|
||||
ginScanToDelete( GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff ) {
|
||||
DataPageDeleteStack *me;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
bool meDelete = FALSE;
|
||||
ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff)
|
||||
{
|
||||
DataPageDeleteStack *me;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
bool meDelete = FALSE;
|
||||
|
||||
if ( isRoot ) {
|
||||
if (isRoot)
|
||||
{
|
||||
me = parent;
|
||||
} else {
|
||||
if ( ! parent->child ) {
|
||||
me = (DataPageDeleteStack*)palloc0(sizeof(DataPageDeleteStack));
|
||||
me->parent=parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parent->child)
|
||||
{
|
||||
me = (DataPageDeleteStack *) palloc0(sizeof(DataPageDeleteStack));
|
||||
me->parent = parent;
|
||||
parent->child = me;
|
||||
me->blkno = InvalidBlockNumber;
|
||||
} else
|
||||
}
|
||||
else
|
||||
me = parent->child;
|
||||
}
|
||||
|
||||
buffer = ReadBuffer( gvs->index, blkno );
|
||||
page = BufferGetPage( buffer );
|
||||
buffer = ReadBuffer(gvs->index, blkno);
|
||||
page = BufferGetPage(buffer);
|
||||
|
||||
Assert( GinPageIsData(page) );
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( !GinPageIsLeaf(page) ) {
|
||||
if (!GinPageIsLeaf(page))
|
||||
{
|
||||
OffsetNumber i;
|
||||
|
||||
for(i=FirstOffsetNumber;i<=GinPageGetOpaque(page)->maxoff;i++) {
|
||||
PostingItem *pitem = (PostingItem*)GinDataPageGetItem(page, i);
|
||||
for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
|
||||
{
|
||||
PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);
|
||||
|
||||
if ( ginScanToDelete( gvs, PostingItemGetBlockNumber(pitem), FALSE, me, i ) )
|
||||
if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), FALSE, me, i))
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if ( GinPageGetOpaque(page)->maxoff < FirstOffsetNumber ) {
|
||||
if ( !( me->blkno == InvalidBlockNumber && GinPageRightMost(page) ) ) {
|
||||
if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
|
||||
{
|
||||
if (!(me->blkno == InvalidBlockNumber && GinPageRightMost(page)))
|
||||
{
|
||||
/* we never delete right most branch */
|
||||
Assert( !isRoot );
|
||||
if ( GinPageGetOpaque(page)->maxoff < FirstOffsetNumber ) {
|
||||
ginDeletePage( gvs, blkno, me->blkno, me->parent->blkno, myoff, me->parent->isRoot );
|
||||
Assert(!isRoot);
|
||||
if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
|
||||
{
|
||||
ginDeletePage(gvs, blkno, me->blkno, me->parent->blkno, myoff, me->parent->isRoot);
|
||||
meDelete = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseBuffer( buffer );
|
||||
ReleaseBuffer(buffer);
|
||||
|
||||
if ( !meDelete )
|
||||
if (!meDelete)
|
||||
me->blkno = blkno;
|
||||
|
||||
return meDelete;
|
||||
}
|
||||
|
||||
static void
|
||||
ginVacuumPostingTree( GinVacuumState *gvs, BlockNumber rootBlkno ) {
|
||||
Buffer rootBuffer = InvalidBuffer;
|
||||
DataPageDeleteStack root, *ptr, *tmp;
|
||||
ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
|
||||
{
|
||||
Buffer rootBuffer = InvalidBuffer;
|
||||
DataPageDeleteStack root,
|
||||
*ptr,
|
||||
*tmp;
|
||||
|
||||
if ( ginVacuumPostingTreeLeaves(gvs, rootBlkno, TRUE, &rootBuffer)==FALSE ) {
|
||||
Assert( rootBuffer == InvalidBuffer );
|
||||
if (ginVacuumPostingTreeLeaves(gvs, rootBlkno, TRUE, &rootBuffer) == FALSE)
|
||||
{
|
||||
Assert(rootBuffer == InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&root,0,sizeof(DataPageDeleteStack));
|
||||
memset(&root, 0, sizeof(DataPageDeleteStack));
|
||||
root.blkno = rootBlkno;
|
||||
root.isRoot = TRUE;
|
||||
|
||||
vacuum_delay_point();
|
||||
|
||||
ginScanToDelete( gvs, rootBlkno, TRUE, &root, InvalidOffsetNumber );
|
||||
ginScanToDelete(gvs, rootBlkno, TRUE, &root, InvalidOffsetNumber);
|
||||
|
||||
ptr = root.child;
|
||||
while( ptr ) {
|
||||
while (ptr)
|
||||
{
|
||||
tmp = ptr->child;
|
||||
pfree( ptr );
|
||||
pfree(ptr);
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
UnlockReleaseBuffer( rootBuffer );
|
||||
UnlockReleaseBuffer(rootBuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -406,48 +462,65 @@ ginVacuumPostingTree( GinVacuumState *gvs, BlockNumber rootBlkno ) {
|
||||
* then page is copied into temprorary one.
|
||||
*/
|
||||
static Page
|
||||
ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot) {
|
||||
Page origpage = BufferGetPage( buffer ), tmppage;
|
||||
OffsetNumber i, maxoff = PageGetMaxOffsetNumber( origpage );
|
||||
ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
|
||||
{
|
||||
Page origpage = BufferGetPage(buffer),
|
||||
tmppage;
|
||||
OffsetNumber i,
|
||||
maxoff = PageGetMaxOffsetNumber(origpage);
|
||||
|
||||
tmppage = origpage;
|
||||
|
||||
*nroot=0;
|
||||
*nroot = 0;
|
||||
|
||||
for(i=FirstOffsetNumber; i<= maxoff; i++) {
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i++)
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
|
||||
|
||||
if ( GinIsPostingTree(itup) ) {
|
||||
/* store posting tree's roots for further processing,
|
||||
we can't vacuum it just now due to risk of deadlocks with scans/inserts */
|
||||
roots[ *nroot ] = GinItemPointerGetBlockNumber(&itup->t_tid);
|
||||
if (GinIsPostingTree(itup))
|
||||
{
|
||||
/*
|
||||
* store posting tree's roots for further processing, we can't
|
||||
* vacuum it just now due to risk of deadlocks with scans/inserts
|
||||
*/
|
||||
roots[*nroot] = GinItemPointerGetBlockNumber(&itup->t_tid);
|
||||
(*nroot)++;
|
||||
} else if ( GinGetNPosting(itup) > 0 ) {
|
||||
/* if we already create temrorary page, we will make changes in place */
|
||||
ItemPointerData *cleaned = (tmppage==origpage) ? NULL : GinGetPosting(itup );
|
||||
uint32 newN = ginVacuumPostingList( gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned );
|
||||
|
||||
if ( GinGetNPosting(itup) != newN ) {
|
||||
bool isnull;
|
||||
Datum value;
|
||||
}
|
||||
else if (GinGetNPosting(itup) > 0)
|
||||
{
|
||||
/*
|
||||
* if we already create temrorary page, we will make changes in
|
||||
* place
|
||||
*/
|
||||
ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup);
|
||||
uint32 newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned);
|
||||
|
||||
if (GinGetNPosting(itup) != newN)
|
||||
{
|
||||
bool isnull;
|
||||
Datum value;
|
||||
|
||||
/*
|
||||
* Some ItemPointers was deleted, so we should remake our tuple
|
||||
* Some ItemPointers was deleted, so we should remake our
|
||||
* tuple
|
||||
*/
|
||||
|
||||
if ( tmppage==origpage ) {
|
||||
if (tmppage == origpage)
|
||||
{
|
||||
/*
|
||||
* On first difference we create temprorary page in memory
|
||||
* and copies content in to it.
|
||||
*/
|
||||
tmppage=GinPageGetCopyPage ( origpage );
|
||||
tmppage = GinPageGetCopyPage(origpage);
|
||||
|
||||
if ( newN > 0 ) {
|
||||
Size pos = ((char*)GinGetPosting(itup)) - ((char*)origpage);
|
||||
memcpy( tmppage+pos, cleaned, sizeof(ItemPointerData)*newN );
|
||||
if (newN > 0)
|
||||
{
|
||||
Size pos = ((char *) GinGetPosting(itup)) - ((char *) origpage);
|
||||
|
||||
memcpy(tmppage + pos, cleaned, sizeof(ItemPointerData) * newN);
|
||||
}
|
||||
|
||||
pfree( cleaned );
|
||||
pfree(cleaned);
|
||||
|
||||
/* set itup pointer to new page */
|
||||
itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
|
||||
@@ -457,30 +530,31 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
|
||||
itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN);
|
||||
PageIndexTupleDelete(tmppage, i);
|
||||
|
||||
if ( PageAddItem( tmppage, (Item)itup, IndexTupleSize(itup), i, LP_USED ) != i )
|
||||
elog(ERROR, "failed to add item to index page in \"%s\"",
|
||||
RelationGetRelationName(gvs->index));
|
||||
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, LP_USED) != i)
|
||||
elog(ERROR, "failed to add item to index page in \"%s\"",
|
||||
RelationGetRelationName(gvs->index));
|
||||
|
||||
pfree( itup );
|
||||
pfree(itup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ( tmppage==origpage ) ? NULL : tmppage;
|
||||
return (tmppage == origpage) ? NULL : tmppage;
|
||||
}
|
||||
|
||||
Datum
|
||||
ginbulkdelete(PG_FUNCTION_ARGS) {
|
||||
ginbulkdelete(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
|
||||
IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
|
||||
IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
|
||||
void *callback_state = (void *) PG_GETARG_POINTER(3);
|
||||
Relation index = info->index;
|
||||
BlockNumber blkno = GIN_ROOT_BLKNO;
|
||||
GinVacuumState gvs;
|
||||
Buffer buffer;
|
||||
BlockNumber rootOfPostingTree[ BLCKSZ/ (sizeof(IndexTupleData)+sizeof(ItemId)) ];
|
||||
uint32 nRoot;
|
||||
BlockNumber blkno = GIN_ROOT_BLKNO;
|
||||
GinVacuumState gvs;
|
||||
Buffer buffer;
|
||||
BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
|
||||
uint32 nRoot;
|
||||
|
||||
/* first time through? */
|
||||
if (stats == NULL)
|
||||
@@ -494,107 +568,117 @@ ginbulkdelete(PG_FUNCTION_ARGS) {
|
||||
gvs.callback_state = callback_state;
|
||||
initGinState(&gvs.ginstate, index);
|
||||
|
||||
buffer = ReadBuffer( index, blkno );
|
||||
buffer = ReadBuffer(index, blkno);
|
||||
|
||||
/* find leaf page */
|
||||
for(;;) {
|
||||
Page page = BufferGetPage( buffer );
|
||||
IndexTuple itup;
|
||||
for (;;)
|
||||
{
|
||||
Page page = BufferGetPage(buffer);
|
||||
IndexTuple itup;
|
||||
|
||||
LockBuffer(buffer,GIN_SHARE);
|
||||
LockBuffer(buffer, GIN_SHARE);
|
||||
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( GinPageIsLeaf(page) ) {
|
||||
LockBuffer(buffer,GIN_UNLOCK);
|
||||
LockBuffer(buffer,GIN_EXCLUSIVE);
|
||||
if (GinPageIsLeaf(page))
|
||||
{
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
LockBuffer(buffer, GIN_EXCLUSIVE);
|
||||
|
||||
if ( blkno==GIN_ROOT_BLKNO && !GinPageIsLeaf(page) ) {
|
||||
LockBuffer(buffer,GIN_UNLOCK);
|
||||
continue; /* check it one more */
|
||||
if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
|
||||
{
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
continue; /* check it one more */
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
Assert( PageGetMaxOffsetNumber(page) >= FirstOffsetNumber );
|
||||
Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
|
||||
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
|
||||
blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
|
||||
Assert( blkno!= InvalidBlockNumber );
|
||||
Assert(blkno != InvalidBlockNumber);
|
||||
|
||||
LockBuffer(buffer,GIN_UNLOCK);
|
||||
buffer = ReleaseAndReadBuffer( buffer, index, blkno );
|
||||
LockBuffer(buffer, GIN_UNLOCK);
|
||||
buffer = ReleaseAndReadBuffer(buffer, index, blkno);
|
||||
}
|
||||
|
||||
/* right now we found leftmost page in entry's BTree */
|
||||
|
||||
for(;;) {
|
||||
Page page = BufferGetPage( buffer );
|
||||
Page resPage;
|
||||
uint32 i;
|
||||
for (;;)
|
||||
{
|
||||
Page page = BufferGetPage(buffer);
|
||||
Page resPage;
|
||||
uint32 i;
|
||||
|
||||
Assert( !GinPageIsData(page) );
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
|
||||
|
||||
blkno = GinPageGetOpaque( page )->rightlink;
|
||||
blkno = GinPageGetOpaque(page)->rightlink;
|
||||
|
||||
if ( resPage ) {
|
||||
if (resPage)
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
PageRestoreTempPage( resPage, page );
|
||||
xlogVacuumPage(gvs.index, buffer);
|
||||
MarkBufferDirty( buffer );
|
||||
PageRestoreTempPage(resPage, page);
|
||||
xlogVacuumPage(gvs.index, buffer);
|
||||
MarkBufferDirty(buffer);
|
||||
UnlockReleaseBuffer(buffer);
|
||||
END_CRIT_SECTION();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
vacuum_delay_point();
|
||||
|
||||
for(i=0; i<nRoot; i++) {
|
||||
ginVacuumPostingTree( &gvs, rootOfPostingTree[i] );
|
||||
for (i = 0; i < nRoot; i++)
|
||||
{
|
||||
ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
|
||||
vacuum_delay_point();
|
||||
}
|
||||
|
||||
if ( blkno==InvalidBlockNumber ) /*rightmost page*/
|
||||
if (blkno == InvalidBlockNumber) /* rightmost page */
|
||||
break;
|
||||
|
||||
buffer = ReadBuffer( index, blkno );
|
||||
LockBuffer(buffer,GIN_EXCLUSIVE);
|
||||
buffer = ReadBuffer(index, blkno);
|
||||
LockBuffer(buffer, GIN_EXCLUSIVE);
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(gvs.result);
|
||||
}
|
||||
|
||||
Datum
|
||||
ginvacuumcleanup(PG_FUNCTION_ARGS) {
|
||||
Datum
|
||||
ginvacuumcleanup(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
|
||||
IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
|
||||
Relation index = info->index;
|
||||
bool needLock;
|
||||
BlockNumber npages,
|
||||
Relation index = info->index;
|
||||
bool needLock;
|
||||
BlockNumber npages,
|
||||
blkno;
|
||||
BlockNumber totFreePages,
|
||||
nFreePages,
|
||||
*freePages,
|
||||
maxFreePages;
|
||||
maxFreePages;
|
||||
BlockNumber lastBlock = GIN_ROOT_BLKNO,
|
||||
lastFilledBlock = GIN_ROOT_BLKNO;
|
||||
lastFilledBlock = GIN_ROOT_BLKNO;
|
||||
|
||||
/* Set up all-zero stats if ginbulkdelete wasn't called */
|
||||
if (stats == NULL)
|
||||
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
|
||||
|
||||
/*
|
||||
* XXX we always report the heap tuple count as the number of index
|
||||
* entries. This is bogus if the index is partial, but it's real hard
|
||||
* to tell how many distinct heap entries are referenced by a GIN index.
|
||||
* entries. This is bogus if the index is partial, but it's real hard to
|
||||
* tell how many distinct heap entries are referenced by a GIN index.
|
||||
*/
|
||||
stats->num_index_tuples = info->num_heap_tuples;
|
||||
|
||||
/*
|
||||
* If vacuum full, we already have exclusive lock on the index.
|
||||
* Otherwise, need lock unless it's local to this backend.
|
||||
* If vacuum full, we already have exclusive lock on the index. Otherwise,
|
||||
* need lock unless it's local to this backend.
|
||||
*/
|
||||
if (info->vacuum_full)
|
||||
needLock = false;
|
||||
@@ -614,32 +698,38 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
|
||||
totFreePages = nFreePages = 0;
|
||||
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
|
||||
|
||||
for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++) {
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++)
|
||||
{
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
|
||||
vacuum_delay_point();
|
||||
|
||||
|
||||
buffer = ReadBuffer(index, blkno);
|
||||
LockBuffer(buffer, GIN_SHARE);
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
if ( GinPageIsDeleted(page) ) {
|
||||
if (GinPageIsDeleted(page))
|
||||
{
|
||||
if (nFreePages < maxFreePages)
|
||||
freePages[nFreePages++] = blkno;
|
||||
totFreePages++;
|
||||
} else
|
||||
}
|
||||
else
|
||||
lastFilledBlock = blkno;
|
||||
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
lastBlock = npages - 1;
|
||||
|
||||
if (info->vacuum_full && nFreePages > 0) {
|
||||
if (info->vacuum_full && nFreePages > 0)
|
||||
{
|
||||
/* try to truncate index */
|
||||
int i;
|
||||
for (i = 0; i < nFreePages; i++)
|
||||
if (freePages[i] >= lastFilledBlock) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nFreePages; i++)
|
||||
if (freePages[i] >= lastFilledBlock)
|
||||
{
|
||||
totFreePages = nFreePages = i;
|
||||
break;
|
||||
}
|
||||
@@ -661,4 +751,3 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
|
||||
|
||||
PG_RETURN_POINTER(stats);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.4 2006/08/07 16:57:56 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.5 2006/10/04 00:29:48 momjian Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
@@ -17,12 +17,13 @@
|
||||
#include "access/heapam.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
static MemoryContext opCtx; /* working memory for operations */
|
||||
static MemoryContext opCtx; /* working memory for operations */
|
||||
static MemoryContext topCtx;
|
||||
|
||||
typedef struct ginIncompleteSplit {
|
||||
RelFileNode node;
|
||||
BlockNumber leftBlkno;
|
||||
typedef struct ginIncompleteSplit
|
||||
{
|
||||
RelFileNode node;
|
||||
BlockNumber leftBlkno;
|
||||
BlockNumber rightBlkno;
|
||||
BlockNumber rootBlkno;
|
||||
} ginIncompleteSplit;
|
||||
@@ -30,10 +31,11 @@ typedef struct ginIncompleteSplit {
|
||||
static List *incomplete_splits;
|
||||
|
||||
static void
|
||||
pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno) {
|
||||
ginIncompleteSplit *split;
|
||||
pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno)
|
||||
{
|
||||
ginIncompleteSplit *split;
|
||||
|
||||
MemoryContextSwitchTo( topCtx );
|
||||
MemoryContextSwitchTo(topCtx);
|
||||
|
||||
split = palloc(sizeof(ginIncompleteSplit));
|
||||
|
||||
@@ -44,17 +46,20 @@ pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBl
|
||||
|
||||
incomplete_splits = lappend(incomplete_splits, split);
|
||||
|
||||
MemoryContextSwitchTo( opCtx );
|
||||
MemoryContextSwitchTo(opCtx);
|
||||
}
|
||||
|
||||
static void
|
||||
forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno) {
|
||||
forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno)
|
||||
{
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, incomplete_splits) {
|
||||
ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
|
||||
foreach(l, incomplete_splits)
|
||||
{
|
||||
ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
|
||||
|
||||
if ( RelFileNodeEquals(node, split->node) && leftBlkno == split->leftBlkno && updateBlkno == split->rightBlkno ) {
|
||||
if (RelFileNodeEquals(node, split->node) && leftBlkno == split->leftBlkno && updateBlkno == split->rightBlkno)
|
||||
{
|
||||
incomplete_splits = list_delete_ptr(incomplete_splits, split);
|
||||
break;
|
||||
}
|
||||
@@ -62,7 +67,8 @@ forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updat
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
@@ -83,9 +89,10 @@ ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree*)XLogRecGetData(record);
|
||||
ItemPointerData *items = (ItemPointerData*)(XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
|
||||
ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
|
||||
ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
@@ -95,8 +102,8 @@ ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record) {
|
||||
Assert(BufferIsValid(buffer));
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
GinInitBuffer(buffer, GIN_DATA|GIN_LEAF);
|
||||
memcpy( GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem );
|
||||
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
|
||||
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
|
||||
GinPageGetOpaque(page)->maxoff = data->nitem;
|
||||
|
||||
PageSetLSN(page, lsn);
|
||||
@@ -107,8 +114,9 @@ ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoInsert(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginxlogInsert *data = (ginxlogInsert*)XLogRecGetData(record);
|
||||
ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
@@ -122,64 +130,73 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record) {
|
||||
Assert(BufferIsValid(buffer));
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
if ( data->isData ) {
|
||||
Assert( data->isDelete == FALSE );
|
||||
Assert( GinPageIsData( page ) );
|
||||
if (data->isData)
|
||||
{
|
||||
Assert(data->isDelete == FALSE);
|
||||
Assert(GinPageIsData(page));
|
||||
|
||||
if ( data->isLeaf ) {
|
||||
if (data->isLeaf)
|
||||
{
|
||||
OffsetNumber i;
|
||||
ItemPointerData *items = (ItemPointerData*)( XLogRecGetData(record) + sizeof(ginxlogInsert) );
|
||||
ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
||||
|
||||
Assert( GinPageIsLeaf( page ) );
|
||||
Assert( data->updateBlkno == InvalidBlockNumber );
|
||||
Assert(GinPageIsLeaf(page));
|
||||
Assert(data->updateBlkno == InvalidBlockNumber);
|
||||
|
||||
for(i=0;i<data->nitem;i++)
|
||||
GinDataPageAddItem( page, items+i, data->offset + i );
|
||||
} else {
|
||||
for (i = 0; i < data->nitem; i++)
|
||||
GinDataPageAddItem(page, items + i, data->offset + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
PostingItem *pitem;
|
||||
|
||||
Assert( !GinPageIsLeaf( page ) );
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
|
||||
if ( data->updateBlkno != InvalidBlockNumber ) {
|
||||
/* update link to right page after split */
|
||||
pitem = (PostingItem*)GinDataPageGetItem(page, data->offset);
|
||||
PostingItemSetBlockNumber( pitem, data->updateBlkno );
|
||||
if (data->updateBlkno != InvalidBlockNumber)
|
||||
{
|
||||
/* update link to right page after split */
|
||||
pitem = (PostingItem *) GinDataPageGetItem(page, data->offset);
|
||||
PostingItemSetBlockNumber(pitem, data->updateBlkno);
|
||||
}
|
||||
|
||||
pitem = (PostingItem*)( XLogRecGetData(record) + sizeof(ginxlogInsert) );
|
||||
pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
||||
|
||||
GinDataPageAddItem( page, pitem, data->offset );
|
||||
GinDataPageAddItem(page, pitem, data->offset);
|
||||
|
||||
if ( data->updateBlkno != InvalidBlockNumber )
|
||||
forgetIncompleteSplit(data->node, PostingItemGetBlockNumber( pitem ), data->updateBlkno);
|
||||
if (data->updateBlkno != InvalidBlockNumber)
|
||||
forgetIncompleteSplit(data->node, PostingItemGetBlockNumber(pitem), data->updateBlkno);
|
||||
}
|
||||
} else {
|
||||
IndexTuple itup;
|
||||
}
|
||||
else
|
||||
{
|
||||
IndexTuple itup;
|
||||
|
||||
Assert( !GinPageIsData( page ) );
|
||||
Assert(!GinPageIsData(page));
|
||||
|
||||
if ( data->updateBlkno != InvalidBlockNumber ) {
|
||||
/* update link to right page after split */
|
||||
Assert( !GinPageIsLeaf( page ) );
|
||||
Assert( data->offset>=FirstOffsetNumber && data->offset<=PageGetMaxOffsetNumber(page) );
|
||||
if (data->updateBlkno != InvalidBlockNumber)
|
||||
{
|
||||
/* update link to right page after split */
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
|
||||
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
|
||||
ItemPointerSet(&itup->t_tid, data->updateBlkno, InvalidOffsetNumber);
|
||||
}
|
||||
|
||||
if ( data->isDelete ) {
|
||||
Assert( GinPageIsLeaf( page ) );
|
||||
Assert( data->offset>=FirstOffsetNumber && data->offset<=PageGetMaxOffsetNumber(page) );
|
||||
if (data->isDelete)
|
||||
{
|
||||
Assert(GinPageIsLeaf(page));
|
||||
Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
|
||||
PageIndexTupleDelete(page, data->offset);
|
||||
}
|
||||
|
||||
itup = (IndexTuple)( XLogRecGetData(record) + sizeof(ginxlogInsert) );
|
||||
itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
|
||||
|
||||
if ( PageAddItem( page, (Item)itup, IndexTupleSize(itup), data->offset, LP_USED) == InvalidOffsetNumber )
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode );
|
||||
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
||||
|
||||
if ( !data->isLeaf && data->updateBlkno != InvalidBlockNumber )
|
||||
forgetIncompleteSplit(data->node, GinItemPointerGetBlockNumber( &itup->t_tid ), data->updateBlkno);
|
||||
if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
|
||||
forgetIncompleteSplit(data->node, GinItemPointerGetBlockNumber(&itup->t_tid), data->updateBlkno);
|
||||
}
|
||||
|
||||
PageSetLSN(page, lsn);
|
||||
@@ -190,18 +207,21 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginxlogSplit *data = (ginxlogSplit*)XLogRecGetData(record);
|
||||
ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
|
||||
Relation reln;
|
||||
Buffer lbuffer, rbuffer;
|
||||
Page lpage, rpage;
|
||||
Buffer lbuffer,
|
||||
rbuffer;
|
||||
Page lpage,
|
||||
rpage;
|
||||
uint32 flags = 0;
|
||||
|
||||
reln = XLogOpenRelation(data->node);
|
||||
|
||||
if ( data->isLeaf )
|
||||
if (data->isLeaf)
|
||||
flags |= GIN_LEAF;
|
||||
if ( data->isData )
|
||||
if (data->isData)
|
||||
flags |= GIN_DATA;
|
||||
|
||||
lbuffer = XLogReadBuffer(reln, data->lblkno, data->isRootSplit);
|
||||
@@ -214,50 +234,57 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) {
|
||||
rpage = (Page) BufferGetPage(rbuffer);
|
||||
GinInitBuffer(rbuffer, flags);
|
||||
|
||||
GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber( rbuffer );
|
||||
GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
||||
GinPageGetOpaque(rpage)->rightlink = data->rrlink;
|
||||
|
||||
if ( data->isData ) {
|
||||
char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
|
||||
Size sizeofitem = GinSizeOfItem(lpage);
|
||||
if (data->isData)
|
||||
{
|
||||
char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
|
||||
Size sizeofitem = GinSizeOfItem(lpage);
|
||||
OffsetNumber i;
|
||||
ItemPointer bound;
|
||||
ItemPointer bound;
|
||||
|
||||
for(i=0;i<data->separator;i++) {
|
||||
GinDataPageAddItem( lpage, ptr, InvalidOffsetNumber );
|
||||
for (i = 0; i < data->separator; i++)
|
||||
{
|
||||
GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
|
||||
ptr += sizeofitem;
|
||||
}
|
||||
|
||||
for(i=data->separator;i<data->nitem;i++) {
|
||||
GinDataPageAddItem( rpage, ptr, InvalidOffsetNumber );
|
||||
for (i = data->separator; i < data->nitem; i++)
|
||||
{
|
||||
GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
|
||||
ptr += sizeofitem;
|
||||
}
|
||||
|
||||
/* set up right key */
|
||||
bound = GinDataPageGetRightBound(lpage);
|
||||
if ( data->isLeaf )
|
||||
*bound = *(ItemPointerData*)GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
|
||||
if (data->isLeaf)
|
||||
*bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
|
||||
else
|
||||
*bound = ((PostingItem*)GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
|
||||
*bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
|
||||
|
||||
bound = GinDataPageGetRightBound(rpage);
|
||||
*bound = data->rightbound;
|
||||
} else {
|
||||
IndexTuple itup = (IndexTuple)( XLogRecGetData(record) + sizeof(ginxlogSplit) );
|
||||
}
|
||||
else
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogSplit));
|
||||
OffsetNumber i;
|
||||
|
||||
for(i=0;i<data->separator;i++) {
|
||||
if ( PageAddItem( lpage, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode );
|
||||
itup = (IndexTuple)( ((char*)itup) + MAXALIGN( IndexTupleSize(itup) ) );
|
||||
for (i = 0; i < data->separator; i++)
|
||||
{
|
||||
if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
||||
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
||||
}
|
||||
|
||||
for(i=data->separator;i<data->nitem;i++) {
|
||||
if ( PageAddItem( rpage, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode );
|
||||
itup = (IndexTuple)( ((char*)itup) + MAXALIGN( IndexTupleSize(itup) ) );
|
||||
for (i = data->separator; i < data->nitem; i++)
|
||||
{
|
||||
if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
||||
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,20 +296,24 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) {
|
||||
PageSetTLI(lpage, ThisTimeLineID);
|
||||
MarkBufferDirty(lbuffer);
|
||||
|
||||
if ( !data->isLeaf && data->updateBlkno != InvalidBlockNumber )
|
||||
if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
|
||||
forgetIncompleteSplit(data->node, data->leftChildBlkno, data->updateBlkno);
|
||||
|
||||
if ( data->isRootSplit ) {
|
||||
Buffer rootBuf = XLogReadBuffer(reln, data->rootBlkno, false);
|
||||
Page rootPage = BufferGetPage( rootBuf );
|
||||
if (data->isRootSplit)
|
||||
{
|
||||
Buffer rootBuf = XLogReadBuffer(reln, data->rootBlkno, false);
|
||||
Page rootPage = BufferGetPage(rootBuf);
|
||||
|
||||
GinInitBuffer( rootBuf, flags & ~GIN_LEAF );
|
||||
GinInitBuffer(rootBuf, flags & ~GIN_LEAF);
|
||||
|
||||
if ( data->isData ) {
|
||||
Assert( data->rootBlkno != GIN_ROOT_BLKNO );
|
||||
if (data->isData)
|
||||
{
|
||||
Assert(data->rootBlkno != GIN_ROOT_BLKNO);
|
||||
dataFillRoot(NULL, rootBuf, lbuffer, rbuffer);
|
||||
} else {
|
||||
Assert( data->rootBlkno == GIN_ROOT_BLKNO );
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(data->rootBlkno == GIN_ROOT_BLKNO);
|
||||
entryFillRoot(NULL, rootBuf, lbuffer, rbuffer);
|
||||
}
|
||||
|
||||
@@ -291,7 +322,8 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) {
|
||||
|
||||
MarkBufferDirty(rootBuf);
|
||||
UnlockReleaseBuffer(rootBuf);
|
||||
} else
|
||||
}
|
||||
else
|
||||
pushIncompleteSplit(data->node, data->lblkno, data->rblkno, data->rootBlkno);
|
||||
|
||||
UnlockReleaseBuffer(rbuffer);
|
||||
@@ -299,8 +331,9 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginxlogVacuumPage *data = (ginxlogVacuumPage*)XLogRecGetData(record);
|
||||
ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
ginxlogVacuumPage *data = (ginxlogVacuumPage *) XLogRecGetData(record);
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
@@ -314,25 +347,30 @@ ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
Assert(BufferIsValid(buffer));
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
if ( GinPageIsData( page ) ) {
|
||||
memcpy( GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
|
||||
GinSizeOfItem(page) * data->nitem );
|
||||
if (GinPageIsData(page))
|
||||
{
|
||||
memcpy(GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
|
||||
GinSizeOfItem(page) *data->nitem);
|
||||
GinPageGetOpaque(page)->maxoff = data->nitem;
|
||||
} else {
|
||||
OffsetNumber i, *tod;
|
||||
IndexTuple itup = (IndexTuple)( XLogRecGetData(record) + sizeof(ginxlogVacuumPage) );
|
||||
}
|
||||
else
|
||||
{
|
||||
OffsetNumber i,
|
||||
*tod;
|
||||
IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogVacuumPage));
|
||||
|
||||
tod = (OffsetNumber*)palloc( sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page) );
|
||||
for(i=FirstOffsetNumber;i<=PageGetMaxOffsetNumber(page);i++)
|
||||
tod[i-1] = i;
|
||||
tod = (OffsetNumber *) palloc(sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page));
|
||||
for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
|
||||
tod[i - 1] = i;
|
||||
|
||||
PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
|
||||
|
||||
for(i=0;i<data->nitem;i++) {
|
||||
if ( PageAddItem( page, (Item)itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber )
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode );
|
||||
itup = (IndexTuple)( ((char*)itup) + MAXALIGN( IndexTupleSize(itup) ) );
|
||||
PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
|
||||
|
||||
for (i = 0; i < data->nitem; i++)
|
||||
{
|
||||
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, LP_USED) == InvalidOffsetNumber)
|
||||
elog(ERROR, "failed to add item to index page in %u/%u/%u",
|
||||
data->node.spcNode, data->node.dbNode, data->node.relNode);
|
||||
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,17 +382,19 @@ ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
ginxlogDeletePage *data = (ginxlogDeletePage*)XLogRecGetData(record);
|
||||
ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
Page page;
|
||||
|
||||
reln = XLogOpenRelation(data->node);
|
||||
|
||||
if ( !( record->xl_info & XLR_BKP_BLOCK_1) ) {
|
||||
if (!(record->xl_info & XLR_BKP_BLOCK_1))
|
||||
{
|
||||
buffer = XLogReadBuffer(reln, data->blkno, false);
|
||||
page = BufferGetPage( buffer );
|
||||
page = BufferGetPage(buffer);
|
||||
Assert(GinPageIsData(page));
|
||||
GinPageGetOpaque(page)->flags = GIN_DELETED;
|
||||
PageSetLSN(page, lsn);
|
||||
@@ -363,9 +403,10 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
if ( !( record->xl_info & XLR_BKP_BLOCK_2) ) {
|
||||
if (!(record->xl_info & XLR_BKP_BLOCK_2))
|
||||
{
|
||||
buffer = XLogReadBuffer(reln, data->parentBlkno, false);
|
||||
page = BufferGetPage( buffer );
|
||||
page = BufferGetPage(buffer);
|
||||
Assert(GinPageIsData(page));
|
||||
Assert(!GinPageIsLeaf(page));
|
||||
PageDeletePostingItem(page, data->parentOffset);
|
||||
@@ -375,9 +416,10 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
if ( !( record->xl_info & XLR_BKP_BLOCK_2) && data->leftBlkno != InvalidBlockNumber ) {
|
||||
if (!(record->xl_info & XLR_BKP_BLOCK_2) && data->leftBlkno != InvalidBlockNumber)
|
||||
{
|
||||
buffer = XLogReadBuffer(reln, data->leftBlkno, false);
|
||||
page = BufferGetPage( buffer );
|
||||
page = BufferGetPage(buffer);
|
||||
Assert(GinPageIsData(page));
|
||||
GinPageGetOpaque(page)->rightlink = data->rightLink;
|
||||
PageSetLSN(page, lsn);
|
||||
@@ -387,28 +429,30 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gin_redo(XLogRecPtr lsn, XLogRecord *record) {
|
||||
uint8 info = record->xl_info & ~XLR_INFO_MASK;
|
||||
void
|
||||
gin_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
{
|
||||
uint8 info = record->xl_info & ~XLR_INFO_MASK;
|
||||
|
||||
topCtx = MemoryContextSwitchTo(opCtx);
|
||||
switch (info) {
|
||||
case XLOG_GIN_CREATE_INDEX:
|
||||
switch (info)
|
||||
{
|
||||
case XLOG_GIN_CREATE_INDEX:
|
||||
ginRedoCreateIndex(lsn, record);
|
||||
break;
|
||||
case XLOG_GIN_CREATE_PTREE:
|
||||
case XLOG_GIN_CREATE_PTREE:
|
||||
ginRedoCreatePTree(lsn, record);
|
||||
break;
|
||||
case XLOG_GIN_INSERT:
|
||||
case XLOG_GIN_INSERT:
|
||||
ginRedoInsert(lsn, record);
|
||||
break;
|
||||
case XLOG_GIN_SPLIT:
|
||||
case XLOG_GIN_SPLIT:
|
||||
ginRedoSplit(lsn, record);
|
||||
break;
|
||||
case XLOG_GIN_VACUUM_PAGE:
|
||||
case XLOG_GIN_VACUUM_PAGE:
|
||||
ginRedoVacuumPage(lsn, record);
|
||||
break;
|
||||
case XLOG_GIN_DELETE_PAGE:
|
||||
case XLOG_GIN_DELETE_PAGE:
|
||||
ginRedoDeletePage(lsn, record);
|
||||
break;
|
||||
default:
|
||||
@@ -419,110 +463,122 @@ gin_redo(XLogRecPtr lsn, XLogRecord *record) {
|
||||
}
|
||||
|
||||
static void
|
||||
desc_node( StringInfo buf, RelFileNode node, BlockNumber blkno ) {
|
||||
appendStringInfo(buf,"node: %u/%u/%u blkno: %u",
|
||||
node.spcNode, node.dbNode, node.relNode, blkno);
|
||||
desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
|
||||
{
|
||||
appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
|
||||
node.spcNode, node.dbNode, node.relNode, blkno);
|
||||
}
|
||||
|
||||
void
|
||||
gin_desc(StringInfo buf, uint8 xl_info, char *rec) {
|
||||
uint8 info = xl_info & ~XLR_INFO_MASK;
|
||||
void
|
||||
gin_desc(StringInfo buf, uint8 xl_info, char *rec)
|
||||
{
|
||||
uint8 info = xl_info & ~XLR_INFO_MASK;
|
||||
|
||||
switch (info) {
|
||||
case XLOG_GIN_CREATE_INDEX:
|
||||
appendStringInfo(buf,"Create index, ");
|
||||
desc_node(buf, *(RelFileNode*)rec, GIN_ROOT_BLKNO );
|
||||
switch (info)
|
||||
{
|
||||
case XLOG_GIN_CREATE_INDEX:
|
||||
appendStringInfo(buf, "Create index, ");
|
||||
desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
|
||||
break;
|
||||
case XLOG_GIN_CREATE_PTREE:
|
||||
appendStringInfo(buf,"Create posting tree, ");
|
||||
desc_node(buf, ((ginxlogCreatePostingTree*)rec)->node, ((ginxlogCreatePostingTree*)rec)->blkno );
|
||||
case XLOG_GIN_CREATE_PTREE:
|
||||
appendStringInfo(buf, "Create posting tree, ");
|
||||
desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
|
||||
break;
|
||||
case XLOG_GIN_INSERT:
|
||||
appendStringInfo(buf,"Insert item, ");
|
||||
desc_node(buf, ((ginxlogInsert*)rec)->node, ((ginxlogInsert*)rec)->blkno );
|
||||
appendStringInfo(buf," offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
|
||||
((ginxlogInsert*)rec)->offset,
|
||||
((ginxlogInsert*)rec)->nitem,
|
||||
( ((ginxlogInsert*)rec)->isData ) ? 'T' : 'F',
|
||||
( ((ginxlogInsert*)rec)->isLeaf ) ? 'T' : 'F',
|
||||
( ((ginxlogInsert*)rec)->isDelete ) ? 'T' : 'F',
|
||||
((ginxlogInsert*)rec)->updateBlkno
|
||||
);
|
||||
case XLOG_GIN_INSERT:
|
||||
appendStringInfo(buf, "Insert item, ");
|
||||
desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
|
||||
appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
|
||||
((ginxlogInsert *) rec)->offset,
|
||||
((ginxlogInsert *) rec)->nitem,
|
||||
(((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
|
||||
(((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
|
||||
(((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
|
||||
((ginxlogInsert *) rec)->updateBlkno
|
||||
);
|
||||
|
||||
break;
|
||||
case XLOG_GIN_SPLIT:
|
||||
appendStringInfo(buf,"Page split, ");
|
||||
desc_node(buf, ((ginxlogSplit*)rec)->node, ((ginxlogSplit*)rec)->lblkno );
|
||||
appendStringInfo(buf," isrootsplit: %c", ( ((ginxlogSplit*)rec)->isRootSplit ) ? 'T' : 'F');
|
||||
case XLOG_GIN_SPLIT:
|
||||
appendStringInfo(buf, "Page split, ");
|
||||
desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
|
||||
appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
|
||||
break;
|
||||
case XLOG_GIN_VACUUM_PAGE:
|
||||
appendStringInfo(buf,"Vacuum page, ");
|
||||
desc_node(buf, ((ginxlogVacuumPage*)rec)->node, ((ginxlogVacuumPage*)rec)->blkno );
|
||||
case XLOG_GIN_VACUUM_PAGE:
|
||||
appendStringInfo(buf, "Vacuum page, ");
|
||||
desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
|
||||
break;
|
||||
case XLOG_GIN_DELETE_PAGE:
|
||||
appendStringInfo(buf,"Delete page, ");
|
||||
desc_node(buf, ((ginxlogDeletePage*)rec)->node, ((ginxlogDeletePage*)rec)->blkno );
|
||||
case XLOG_GIN_DELETE_PAGE:
|
||||
appendStringInfo(buf, "Delete page, ");
|
||||
desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
|
||||
break;
|
||||
default:
|
||||
elog(PANIC, "gin_desc: unknown op code %u", info);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gin_xlog_startup(void) {
|
||||
void
|
||||
gin_xlog_startup(void)
|
||||
{
|
||||
incomplete_splits = NIL;
|
||||
|
||||
opCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"GIN recovery temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
"GIN recovery temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
}
|
||||
|
||||
static void
|
||||
ginContinueSplit( ginIncompleteSplit *split ) {
|
||||
ginContinueSplit(ginIncompleteSplit *split)
|
||||
{
|
||||
GinBtreeData btree;
|
||||
Relation reln;
|
||||
Buffer buffer;
|
||||
GinBtreeStack stack;
|
||||
GinBtreeStack stack;
|
||||
|
||||
/* elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno, split->leftBlkno, split->rightBlkno); */
|
||||
/*
|
||||
* elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno,
|
||||
* split->leftBlkno, split->rightBlkno);
|
||||
*/
|
||||
reln = XLogOpenRelation(split->node);
|
||||
|
||||
buffer = XLogReadBuffer(reln, split->leftBlkno, false);
|
||||
buffer = XLogReadBuffer(reln, split->leftBlkno, false);
|
||||
|
||||
if ( split->rootBlkno == GIN_ROOT_BLKNO ) {
|
||||
prepareEntryScan( &btree, reln, (Datum)0, NULL );
|
||||
btree.entry = ginPageGetLinkItup( buffer );
|
||||
} else {
|
||||
Page page = BufferGetPage( buffer );
|
||||
if (split->rootBlkno == GIN_ROOT_BLKNO)
|
||||
{
|
||||
prepareEntryScan(&btree, reln, (Datum) 0, NULL);
|
||||
btree.entry = ginPageGetLinkItup(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Page page = BufferGetPage(buffer);
|
||||
|
||||
prepareDataScan( &btree, reln );
|
||||
prepareDataScan(&btree, reln);
|
||||
|
||||
PostingItemSetBlockNumber( &(btree.pitem), split->leftBlkno );
|
||||
if ( GinPageIsLeaf(page) )
|
||||
btree.pitem.key = *(ItemPointerData*)GinDataPageGetItem(page,
|
||||
GinPageGetOpaque(page)->maxoff);
|
||||
PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
|
||||
if (GinPageIsLeaf(page))
|
||||
btree.pitem.key = *(ItemPointerData *) GinDataPageGetItem(page,
|
||||
GinPageGetOpaque(page)->maxoff);
|
||||
else
|
||||
btree.pitem.key = ((PostingItem*)GinDataPageGetItem(page,
|
||||
GinPageGetOpaque(page)->maxoff))->key;
|
||||
btree.pitem.key = ((PostingItem *) GinDataPageGetItem(page,
|
||||
GinPageGetOpaque(page)->maxoff))->key;
|
||||
}
|
||||
|
||||
btree.rightblkno = split->rightBlkno;
|
||||
btree.rightblkno = split->rightBlkno;
|
||||
|
||||
stack.blkno = split->leftBlkno;
|
||||
stack.buffer = buffer;
|
||||
stack.off = InvalidOffsetNumber;
|
||||
stack.parent = NULL;
|
||||
|
||||
findParents( &btree, &stack, split->rootBlkno);
|
||||
ginInsertValue( &btree, stack.parent );
|
||||
findParents(&btree, &stack, split->rootBlkno);
|
||||
ginInsertValue(&btree, stack.parent);
|
||||
|
||||
UnlockReleaseBuffer( buffer );
|
||||
UnlockReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
void
|
||||
gin_xlog_cleanup(void) {
|
||||
void
|
||||
gin_xlog_cleanup(void)
|
||||
{
|
||||
ListCell *l;
|
||||
MemoryContext topCtx;
|
||||
|
||||
@@ -531,8 +587,9 @@ gin_xlog_cleanup(void) {
|
||||
foreach(l, incomplete_splits)
|
||||
{
|
||||
ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
|
||||
ginContinueSplit( split );
|
||||
MemoryContextReset( opCtx );
|
||||
|
||||
ginContinueSplit(split);
|
||||
MemoryContextReset(opCtx);
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(topCtx);
|
||||
|
||||
Reference in New Issue
Block a user