mirror of
https://github.com/postgres/postgres.git
synced 2025-05-29 16:21:20 +03:00
Fix possible duplicate tuples while GiST scan. Now page is processed
at once and ItemPointers are collected in memory. Remove tuple's killing by killtuple() if tuple was moved to another page - it could produce unaceptable overhead. Backpatch up to 8.1 because the bug was introduced by GiST's concurrency support.
This commit is contained in:
parent
7e9308544f
commit
4040629fe1
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.52.2.1 2005/11/22 18:23:03 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.52.2.2 2008/08/23 10:43:58 teodor Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -30,29 +30,22 @@ static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
|
||||
static void
|
||||
killtuple(Relation r, GISTScanOpaque so, ItemPointer iptr)
|
||||
{
|
||||
Buffer buffer = so->curbuf;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Page p;
|
||||
BlockNumber blkno;
|
||||
OffsetNumber offset,
|
||||
maxoff;
|
||||
OffsetNumber offset;
|
||||
|
||||
LockBuffer(buffer, GIST_SHARE);
|
||||
p = (Page) BufferGetPage(buffer);
|
||||
LockBuffer(so->curbuf, GIST_SHARE);
|
||||
p = (Page) BufferGetPage(so->curbuf);
|
||||
|
||||
if (buffer == so->curbuf && XLByteEQ(so->stack->lsn, PageGetLSN(p)))
|
||||
if (XLByteEQ(so->stack->lsn, PageGetLSN(p)))
|
||||
{
|
||||
/* page unchanged, so all is simple */
|
||||
offset = ItemPointerGetOffsetNumber(iptr);
|
||||
PageGetItemId(p, offset)->lp_flags |= LP_DELETE;
|
||||
SetBufferCommitInfoNeedsSave(buffer);
|
||||
LockBuffer(buffer, GIST_UNLOCK);
|
||||
break;
|
||||
SetBufferCommitInfoNeedsSave(so->curbuf);
|
||||
}
|
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
else
|
||||
{
|
||||
OffsetNumber maxoff = PageGetMaxOffsetNumber(p);
|
||||
|
||||
for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
|
||||
{
|
||||
@ -62,30 +55,13 @@ killtuple(Relation r, GISTScanOpaque so, ItemPointer iptr)
|
||||
{
|
||||
/* found */
|
||||
PageGetItemId(p, offset)->lp_flags |= LP_DELETE;
|
||||
SetBufferCommitInfoNeedsSave(buffer);
|
||||
LockBuffer(buffer, GIST_UNLOCK);
|
||||
if (buffer != so->curbuf)
|
||||
ReleaseBuffer(buffer);
|
||||
return;
|
||||
SetBufferCommitInfoNeedsSave(so->curbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* follow right link */
|
||||
|
||||
/*
|
||||
* ??? is it good? if tuple dropped by concurrent vacuum, we will read
|
||||
* all leaf pages...
|
||||
*/
|
||||
blkno = GistPageGetOpaque(p)->rightlink;
|
||||
LockBuffer(buffer, GIST_UNLOCK);
|
||||
if (buffer != so->curbuf)
|
||||
ReleaseBuffer(buffer);
|
||||
|
||||
if (blkno == InvalidBlockNumber)
|
||||
/* can't found, dropped by somebody else */
|
||||
return;
|
||||
buffer = ReadBuffer(r, blkno);
|
||||
}
|
||||
LockBuffer(so->curbuf, GIST_UNLOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -146,7 +122,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
GISTSearchStack *stk;
|
||||
IndexTuple it;
|
||||
GISTPageOpaque opaque;
|
||||
bool resetoffset = false;
|
||||
int ntids = 0;
|
||||
|
||||
so = (GISTScanOpaque) scan->opaque;
|
||||
@ -171,6 +146,42 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check stored pointers from last visit
|
||||
*/
|
||||
if ( so->nPageData > 0 )
|
||||
{
|
||||
while( ntids < maxtids && so->curPageData < so->nPageData )
|
||||
{
|
||||
tids[ ntids ] = scan->xs_ctup.t_self = so->pageData[ so->curPageData ];
|
||||
|
||||
so->curPageData ++;
|
||||
ntids++;
|
||||
}
|
||||
|
||||
if ( ntids == maxtids )
|
||||
return ntids;
|
||||
|
||||
/*
|
||||
* Go to the next page
|
||||
*/
|
||||
stk = so->stack->next;
|
||||
pfree(so->stack);
|
||||
so->stack = stk;
|
||||
|
||||
/* If we're out of stack entries, we're done */
|
||||
if (so->stack == NULL)
|
||||
{
|
||||
ReleaseBuffer(so->curbuf);
|
||||
so->curbuf = InvalidBuffer;
|
||||
return ntids;
|
||||
}
|
||||
|
||||
so->curbuf = ReleaseAndReadBuffer(so->curbuf,
|
||||
scan->indexRelation,
|
||||
stk->block);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* First of all, we need lock buffer */
|
||||
@ -178,13 +189,9 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
LockBuffer(so->curbuf, GIST_SHARE);
|
||||
p = BufferGetPage(so->curbuf);
|
||||
opaque = GistPageGetOpaque(p);
|
||||
resetoffset = false;
|
||||
|
||||
if (XLogRecPtrIsInvalid(so->stack->lsn) || !XLByteEQ(so->stack->lsn, PageGetLSN(p)))
|
||||
{
|
||||
/* page changed from last visit or visit first time , reset offset */
|
||||
/* remember lsn to identify page changed for tuple's killing */
|
||||
so->stack->lsn = PageGetLSN(p);
|
||||
resetoffset = true;
|
||||
|
||||
/* check page split, occured from last visit or visit to parent */
|
||||
if (!XLogRecPtrIsInvalid(so->stack->parentlsn) &&
|
||||
@ -202,7 +209,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
memset(&(stk->lsn), 0, sizeof(GistNSN));
|
||||
so->stack->next = stk;
|
||||
}
|
||||
}
|
||||
|
||||
/* if page is empty, then just skip it */
|
||||
if (PageIsEmpty(p))
|
||||
@ -224,24 +230,13 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GistPageIsLeaf(p) || resetoffset || ItemPointerIsValid(&scan->currentItemData) == false)
|
||||
{
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = PageGetMaxOffsetNumber(p);
|
||||
else
|
||||
n = FirstOffsetNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = ItemPointerGetOffsetNumber(&(scan->currentItemData));
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = OffsetNumberPrev(n);
|
||||
else
|
||||
n = OffsetNumberNext(n);
|
||||
}
|
||||
|
||||
/* wonderfull, we can look at page */
|
||||
so->nPageData = so->curPageData = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@ -249,6 +244,20 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
|
||||
if (!OffsetNumberIsValid(n))
|
||||
{
|
||||
while( ntids < maxtids && so->curPageData < so->nPageData )
|
||||
{
|
||||
tids[ ntids ] = scan->xs_ctup.t_self = so->pageData[ so->curPageData ];
|
||||
|
||||
so->curPageData ++;
|
||||
ntids++;
|
||||
}
|
||||
|
||||
if ( ntids == maxtids )
|
||||
{
|
||||
LockBuffer(so->curbuf, GIST_UNLOCK);
|
||||
return ntids;
|
||||
}
|
||||
|
||||
/*
|
||||
* We ran out of matching index entries on the current page,
|
||||
* so pop the top stack entry and use it to continue the
|
||||
@ -288,14 +297,8 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, b
|
||||
if (!(ignore_killed_tuples && ItemIdDeleted(PageGetItemId(p, n))))
|
||||
{
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
tids[ntids] = scan->xs_ctup.t_self = it->t_tid;
|
||||
ntids++;
|
||||
|
||||
if (ntids == maxtids)
|
||||
{
|
||||
LockBuffer(so->curbuf, GIST_UNLOCK);
|
||||
return ntids;
|
||||
}
|
||||
so->pageData[ so->nPageData ] = it->t_tid;
|
||||
so->nPageData ++;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.61 2005/09/22 20:44:36 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.61.2.1 2008/08/23 10:43:58 teodor Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -98,6 +98,7 @@ gistrescan(PG_FUNCTION_ARGS)
|
||||
ReleaseBuffer(so->markbuf);
|
||||
so->markbuf = InvalidBuffer;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -113,6 +114,8 @@ gistrescan(PG_FUNCTION_ARGS)
|
||||
scan->opaque = so;
|
||||
}
|
||||
|
||||
so->nPageData = so->curPageData = 0;
|
||||
|
||||
/* Update scan key, if a new one is given */
|
||||
if (key && scan->numberOfKeys > 0)
|
||||
{
|
||||
@ -179,6 +182,11 @@ gistmarkpos(PG_FUNCTION_ARGS)
|
||||
so->markbuf = so->curbuf;
|
||||
}
|
||||
|
||||
so->markNPageData = so->nPageData;
|
||||
so->markCurPageData = so->curPageData;
|
||||
if ( so->markNPageData > 0 )
|
||||
memcpy( so->markPageData, so->pageData, sizeof(ItemPointerData) * so->markNPageData );
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
@ -228,6 +236,11 @@ gistrestrpos(PG_FUNCTION_ARGS)
|
||||
so->curbuf = so->markbuf;
|
||||
}
|
||||
|
||||
so->nPageData = so->markNPageData;
|
||||
so->curPageData = so->markNPageData;
|
||||
if ( so->markNPageData > 0 )
|
||||
memcpy( so->pageData, so->markPageData, sizeof(ItemPointerData) * so->markNPageData );
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.8 2005/10/15 02:49:42 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.8.2.1 2008/08/23 10:43:58 teodor Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -73,6 +73,13 @@ typedef struct GISTScanOpaqueData
|
||||
MemoryContext tempCxt;
|
||||
Buffer curbuf;
|
||||
Buffer markbuf;
|
||||
|
||||
ItemPointerData pageData[BLCKSZ/sizeof(IndexTupleData)];
|
||||
OffsetNumber nPageData;
|
||||
OffsetNumber curPageData;
|
||||
ItemPointerData markPageData[BLCKSZ/sizeof(IndexTupleData)];
|
||||
OffsetNumber markNPageData;
|
||||
OffsetNumber markCurPageData;
|
||||
} GISTScanOpaqueData;
|
||||
|
||||
typedef GISTScanOpaqueData *GISTScanOpaque;
|
||||
|
Loading…
x
Reference in New Issue
Block a user