1996-08-26 22:02:12 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* gistget.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* fetch tuples from a GiST scan.
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*
|
2008-01-01 20:46:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
2001-05-30 21:53:40 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2008-06-19 02:46:06 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.74 2008/06/19 00:46:03 alvherre Exp $
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-10-31 09:09:47 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
#include "access/gist_private.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "access/relscan.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "executor/execdebug.h"
|
2008-04-11 00:25:26 +02:00
|
|
|
#include "miscadmin.h"
|
2005-10-06 04:29:23 +02:00
|
|
|
#include "pgstat.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/bufmgr.h"
|
2005-05-17 02:59:30 +02:00
|
|
|
#include "utils/memutils.h"
|
1996-10-21 07:11:00 +02:00
|
|
|
|
2005-10-06 04:29:23 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
|
2005-09-22 22:44:36 +02:00
|
|
|
ScanDirection dir);
|
2008-04-13 21:18:14 +02:00
|
|
|
static int64 gistnext(IndexScanDesc scan, ScanDirection dir, TIDBitmap *tbm);
|
2005-05-17 02:59:30 +02:00
|
|
|
static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
|
2005-09-22 22:44:36 +02:00
|
|
|
OffsetNumber offset);
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
static void
|
|
|
|
killtuple(Relation r, GISTScanOpaque so, ItemPointer iptr)
|
|
|
|
{
|
|
|
|
Buffer buffer = so->curbuf;
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
Page p;
|
2005-06-27 14:45:23 +02:00
|
|
|
BlockNumber blkno;
|
2005-09-22 22:44:36 +02:00
|
|
|
OffsetNumber offset,
|
|
|
|
maxoff;
|
|
|
|
|
|
|
|
LockBuffer(buffer, GIST_SHARE);
|
2005-11-06 23:39:21 +01:00
|
|
|
gistcheckpage(r, buffer);
|
2005-09-22 22:44:36 +02:00
|
|
|
p = (Page) BufferGetPage(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (buffer == so->curbuf && XLByteEQ(so->stack->lsn, PageGetLSN(p)))
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
/* page unchanged, so all is simple */
|
|
|
|
offset = ItemPointerGetOffsetNumber(iptr);
|
2007-09-13 00:10:26 +02:00
|
|
|
ItemIdMarkDead(PageGetItemId(p, offset));
|
2005-06-27 14:45:23 +02:00
|
|
|
SetBufferCommitInfoNeedsSave(buffer);
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(buffer, GIST_UNLOCK);
|
2005-06-27 14:45:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
maxoff = PageGetMaxOffsetNumber(p);
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
|
|
|
|
{
|
|
|
|
IndexTuple ituple = (IndexTuple) PageGetItem(p, PageGetItemId(p, offset));
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (ItemPointerEquals(&(ituple->t_tid), iptr))
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
/* found */
|
2007-09-13 00:10:26 +02:00
|
|
|
ItemIdMarkDead(PageGetItemId(p, offset));
|
2005-06-27 14:45:23 +02:00
|
|
|
SetBufferCommitInfoNeedsSave(buffer);
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(buffer, GIST_UNLOCK);
|
|
|
|
if (buffer != so->curbuf)
|
|
|
|
ReleaseBuffer(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
return;
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* follow right link */
|
2005-09-22 22:44:36 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* ??? is it good? if tuple dropped by concurrent vacuum, we will read
|
|
|
|
* all leaf pages...
|
2005-06-27 14:45:23 +02:00
|
|
|
*/
|
|
|
|
blkno = GistPageGetOpaque(p)->rightlink;
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(buffer, GIST_UNLOCK);
|
|
|
|
if (buffer != so->curbuf)
|
|
|
|
ReleaseBuffer(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (blkno == InvalidBlockNumber)
|
2005-06-27 14:45:23 +02:00
|
|
|
/* can't found, dropped by somebody else */
|
|
|
|
return;
|
2005-09-22 22:44:36 +02:00
|
|
|
buffer = ReadBuffer(r, blkno);
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
|
|
|
* gistgettuple() -- Get the next tuple in the scan
|
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistgettuple(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2005-09-22 22:44:36 +02:00
|
|
|
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
|
|
|
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
|
|
|
|
GISTScanOpaque so;
|
|
|
|
bool res;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
so = (GISTScanOpaque) scan->opaque;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* If we have produced an index tuple in the past and the executor has
|
|
|
|
* informed us we need to mark it as "killed", do so now.
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
2007-01-20 19:43:35 +01:00
|
|
|
if (scan->kill_prior_tuple && ItemPointerIsValid(&(so->curpos)))
|
|
|
|
killtuple(scan->indexRelation, so, &(so->curpos));
|
2005-05-17 02:59:30 +02:00
|
|
|
|
|
|
|
/*
|
2008-04-13 21:18:14 +02:00
|
|
|
* Get the next tuple that matches the search key.
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
2008-04-13 21:18:14 +02:00
|
|
|
res = (gistnext(scan, dir, NULL) > 0);
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
PG_RETURN_BOOL(res);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2005-03-28 01:53:05 +02:00
|
|
|
Datum
|
2008-04-11 00:25:26 +02:00
|
|
|
gistgetbitmap(PG_FUNCTION_ARGS)
|
2005-03-28 01:53:05 +02:00
|
|
|
{
|
2005-05-17 02:59:30 +02:00
|
|
|
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
2008-04-11 00:25:26 +02:00
|
|
|
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
|
|
|
|
int64 ntids;
|
2005-03-28 01:53:05 +02:00
|
|
|
|
2008-04-13 21:18:14 +02:00
|
|
|
ntids = gistnext(scan, ForwardScanDirection, tbm);
|
2005-09-22 22:44:36 +02:00
|
|
|
|
2008-04-11 00:25:26 +02:00
|
|
|
PG_RETURN_INT64(ntids);
|
2005-03-28 01:53:05 +02:00
|
|
|
}
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
2008-04-11 00:25:26 +02:00
|
|
|
* Fetch tuple(s) that match the search key; this can be invoked
|
|
|
|
* either to fetch the first such tuple or subsequent matching tuples.
|
|
|
|
*
|
|
|
|
* This function is used by both gistgettuple and gistgetbitmap. When
|
|
|
|
* invoked from gistgettuple, tbm is null and the next matching tuple
|
2008-04-13 21:18:14 +02:00
|
|
|
* is returned in scan->xs_ctup.t_self. When invoked from getbitmap,
|
|
|
|
* tbm is non-null and all matching tuples are added to tbm before
|
|
|
|
* returning. In both cases, the function result is the number of
|
|
|
|
* returned tuples.
|
|
|
|
*
|
|
|
|
* If scan specifies to skip killed tuples, continue looping until we find a
|
|
|
|
* non-killed tuple that matches the search key.
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
2008-04-11 00:25:26 +02:00
|
|
|
static int64
|
2008-04-13 21:18:14 +02:00
|
|
|
gistnext(IndexScanDesc scan, ScanDirection dir, TIDBitmap *tbm)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Page p;
|
|
|
|
OffsetNumber n;
|
|
|
|
GISTScanOpaque so;
|
2005-09-22 22:44:36 +02:00
|
|
|
GISTSearchStack *stk;
|
1997-09-08 04:41:22 +02:00
|
|
|
IndexTuple it;
|
2005-09-22 22:44:36 +02:00
|
|
|
GISTPageOpaque opaque;
|
|
|
|
bool resetoffset = false;
|
2008-04-11 00:25:26 +02:00
|
|
|
int64 ntids = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
so = (GISTScanOpaque) scan->opaque;
|
|
|
|
|
2007-01-20 19:43:35 +01:00
|
|
|
if (ItemPointerIsValid(&so->curpos) == false)
|
2005-05-17 02:59:30 +02:00
|
|
|
{
|
|
|
|
/* Being asked to fetch the first entry, so start at the root */
|
|
|
|
Assert(so->curbuf == InvalidBuffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
Assert(so->stack == NULL);
|
2005-02-05 20:38:58 +01:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
so->curbuf = ReadBuffer(scan->indexRelation, GIST_ROOT_BLKNO);
|
2005-09-22 22:44:36 +02:00
|
|
|
|
|
|
|
stk = so->stack = (GISTSearchStack *) palloc0(sizeof(GISTSearchStack));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
stk->next = NULL;
|
|
|
|
stk->block = GIST_ROOT_BLKNO;
|
2005-10-06 04:29:23 +02:00
|
|
|
|
2007-05-27 05:50:39 +02:00
|
|
|
pgstat_count_index_scan(scan->indexRelation);
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
|
|
|
else if (so->curbuf == InvalidBuffer)
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
return 0;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2008-04-11 00:25:26 +02:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* First of all, we need lock buffer */
|
2005-09-22 22:44:36 +02:00
|
|
|
Assert(so->curbuf != InvalidBuffer);
|
|
|
|
LockBuffer(so->curbuf, GIST_SHARE);
|
2005-11-06 23:39:21 +01:00
|
|
|
gistcheckpage(scan->indexRelation, so->curbuf);
|
2005-06-27 14:45:23 +02:00
|
|
|
p = BufferGetPage(so->curbuf);
|
2005-09-22 22:44:36 +02:00
|
|
|
opaque = GistPageGetOpaque(p);
|
2005-06-27 14:45:23 +02:00
|
|
|
resetoffset = false;
|
2005-09-22 22:44:36 +02:00
|
|
|
|
|
|
|
if (XLogRecPtrIsInvalid(so->stack->lsn) || !XLByteEQ(so->stack->lsn, PageGetLSN(p)))
|
|
|
|
{
|
2008-04-13 21:18:14 +02:00
|
|
|
/* first visit or page changed from last visit, reset offset */
|
2005-06-27 14:45:23 +02:00
|
|
|
so->stack->lsn = PageGetLSN(p);
|
|
|
|
resetoffset = true;
|
|
|
|
|
|
|
|
/* check page split, occured from last visit or visit to parent */
|
2005-09-22 22:44:36 +02:00
|
|
|
if (!XLogRecPtrIsInvalid(so->stack->parentlsn) &&
|
|
|
|
XLByteLT(so->stack->parentlsn, opaque->nsn) &&
|
|
|
|
opaque->rightlink != InvalidBlockNumber /* sanity check */ &&
|
|
|
|
(so->stack->next == NULL || so->stack->next->block != opaque->rightlink) /* check if already
|
|
|
|
added */ )
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
/* detect page split, follow right link to add pages */
|
2005-09-22 22:44:36 +02:00
|
|
|
|
|
|
|
stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
|
2005-06-27 14:45:23 +02:00
|
|
|
stk->next = so->stack->next;
|
|
|
|
stk->block = opaque->rightlink;
|
|
|
|
stk->parentlsn = so->stack->parentlsn;
|
2005-09-22 22:44:36 +02:00
|
|
|
memset(&(stk->lsn), 0, sizeof(GistNSN));
|
2005-06-27 14:45:23 +02:00
|
|
|
so->stack->next = stk;
|
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* if page is empty, then just skip it */
|
2005-09-22 22:44:36 +02:00
|
|
|
if (PageIsEmpty(p))
|
|
|
|
{
|
|
|
|
LockBuffer(so->curbuf, GIST_UNLOCK);
|
2005-06-27 14:45:23 +02:00
|
|
|
stk = so->stack->next;
|
2005-09-22 22:44:36 +02:00
|
|
|
pfree(so->stack);
|
2005-06-27 14:45:23 +02:00
|
|
|
so->stack = stk;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (so->stack == NULL)
|
|
|
|
{
|
2005-05-17 02:59:30 +02:00
|
|
|
ReleaseBuffer(so->curbuf);
|
|
|
|
so->curbuf = InvalidBuffer;
|
2005-06-27 14:45:23 +02:00
|
|
|
return ntids;
|
2005-02-05 20:38:58 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
|
2005-09-22 22:44:36 +02:00
|
|
|
stk->block);
|
2005-06-27 14:45:23 +02:00
|
|
|
continue;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-11-06 23:39:21 +01:00
|
|
|
if (!GistPageIsLeaf(p) || resetoffset ||
|
2007-01-20 19:43:35 +01:00
|
|
|
!ItemPointerIsValid(&so->curpos))
|
2005-06-27 14:45:23 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (ScanDirectionIsBackward(dir))
|
2005-06-27 14:45:23 +02:00
|
|
|
n = PageGetMaxOffsetNumber(p);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2005-06-27 14:45:23 +02:00
|
|
|
n = FirstOffsetNumber;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-01-20 19:43:35 +01:00
|
|
|
n = ItemPointerGetOffsetNumber(&(so->curpos));
|
2005-09-22 22:44:36 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if (ScanDirectionIsBackward(dir))
|
|
|
|
n = OffsetNumberPrev(n);
|
|
|
|
else
|
|
|
|
n = OffsetNumberNext(n);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2006-11-12 07:55:54 +01:00
|
|
|
/* wonderful, we can look at page */
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
n = gistfindnext(scan, n, dir);
|
|
|
|
|
|
|
|
if (!OffsetNumberIsValid(n))
|
|
|
|
{
|
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* We ran out of matching index entries on the current page,
|
|
|
|
* so pop the top stack entry and use it to continue the
|
|
|
|
* search.
|
2005-06-27 14:45:23 +02:00
|
|
|
*/
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(so->curbuf, GIST_UNLOCK);
|
2005-06-27 14:45:23 +02:00
|
|
|
stk = so->stack->next;
|
2005-09-22 22:44:36 +02:00
|
|
|
pfree(so->stack);
|
2005-06-27 14:45:23 +02:00
|
|
|
so->stack = stk;
|
|
|
|
|
|
|
|
/* If we're out of stack entries, we're done */
|
2005-09-22 22:44:36 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if (so->stack == NULL)
|
|
|
|
{
|
|
|
|
ReleaseBuffer(so->curbuf);
|
|
|
|
so->curbuf = InvalidBuffer;
|
|
|
|
return ntids;
|
|
|
|
}
|
|
|
|
|
2005-11-06 23:39:21 +01:00
|
|
|
so->curbuf = ReleaseAndReadBuffer(so->curbuf,
|
|
|
|
scan->indexRelation,
|
2005-09-22 22:44:36 +02:00
|
|
|
stk->block);
|
|
|
|
/* XXX go up */
|
2005-06-27 14:45:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GistPageIsLeaf(p))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We've found a matching index entry in a leaf page, so
|
2005-09-22 22:44:36 +02:00
|
|
|
* return success. Note that we keep "curbuf" pinned so that
|
|
|
|
* we can efficiently resume the index scan later.
|
2005-06-27 14:45:23 +02:00
|
|
|
*/
|
2007-01-20 19:43:35 +01:00
|
|
|
ItemPointerSet(&(so->curpos),
|
2005-09-22 22:44:36 +02:00
|
|
|
BufferGetBlockNumber(so->curbuf), n);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2008-04-13 21:18:14 +02:00
|
|
|
if (!(scan->ignore_killed_tuples &&
|
|
|
|
ItemIdIsDead(PageGetItemId(p, n))))
|
2005-09-22 22:44:36 +02:00
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
|
|
|
ntids++;
|
2008-04-11 00:25:26 +02:00
|
|
|
if (tbm != NULL)
|
2008-04-14 19:05:34 +02:00
|
|
|
tbm_add_tuples(tbm, &it->t_tid, 1, scan->xs_recheck);
|
2008-04-11 00:25:26 +02:00
|
|
|
else
|
2005-09-22 22:44:36 +02:00
|
|
|
{
|
2008-04-13 21:18:14 +02:00
|
|
|
scan->xs_ctup.t_self = it->t_tid;
|
2008-04-14 19:05:34 +02:00
|
|
|
/* scan->xs_recheck is already set */
|
2008-04-11 00:25:26 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(so->curbuf, GIST_UNLOCK);
|
2008-04-11 00:25:26 +02:00
|
|
|
return ntids; /* always 1 */
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We've found an entry in an internal node whose key is
|
2005-09-22 22:44:36 +02:00
|
|
|
* consistent with the search key, so push it to stack
|
2005-06-27 14:45:23 +02:00
|
|
|
*/
|
|
|
|
stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
|
|
|
|
|
|
|
|
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
|
|
|
stk->block = ItemPointerGetBlockNumber(&(it->t_tid));
|
2005-09-22 22:44:36 +02:00
|
|
|
memset(&(stk->lsn), 0, sizeof(GistNSN));
|
2005-06-27 14:45:23 +02:00
|
|
|
stk->parentlsn = so->stack->lsn;
|
|
|
|
|
|
|
|
stk->next = so->stack->next;
|
|
|
|
so->stack->next = stk;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (ScanDirectionIsBackward(dir))
|
2005-06-27 14:45:23 +02:00
|
|
|
n = OffsetNumberPrev(n);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2005-06-27 14:45:23 +02:00
|
|
|
n = OffsetNumberNext(n);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
|
|
|
|
return ntids;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
2006-01-14 23:03:35 +01:00
|
|
|
* gistindex_keytest() -- does this index tuple satisfy the scan key(s)?
|
|
|
|
*
|
2008-04-14 19:05:34 +02:00
|
|
|
* On success return for a leaf tuple, scan->xs_recheck is set to indicate
|
|
|
|
* whether recheck is needed. We recheck if any of the consistent() functions
|
|
|
|
* request it.
|
|
|
|
*
|
2006-01-14 23:03:35 +01:00
|
|
|
* We must decompress the key in the IndexTuple before passing it to the
|
|
|
|
* sk_func (and we have previously overwritten the sk_func to use the
|
|
|
|
* user-defined Consistent method, so we actually are invoking that).
|
|
|
|
*
|
|
|
|
* Note that this function is always invoked in a short-lived memory context,
|
|
|
|
* so we don't need to worry about cleaning up allocated memory, either here
|
|
|
|
* or in the implementation of any Consistent methods.
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static bool
|
1996-08-26 22:02:12 +02:00
|
|
|
gistindex_keytest(IndexTuple tuple,
|
2005-05-17 02:59:30 +02:00
|
|
|
IndexScanDesc scan,
|
1997-09-07 07:04:48 +02:00
|
|
|
OffsetNumber offset)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2005-09-22 22:44:36 +02:00
|
|
|
int keySize = scan->numberOfKeys;
|
|
|
|
ScanKey key = scan->keyData;
|
|
|
|
Relation r = scan->indexRelation;
|
2005-05-17 02:59:30 +02:00
|
|
|
GISTScanOpaque so;
|
2005-09-22 22:44:36 +02:00
|
|
|
Page p;
|
|
|
|
GISTSTATE *giststate;
|
2005-05-17 02:59:30 +02:00
|
|
|
|
|
|
|
so = (GISTScanOpaque) scan->opaque;
|
|
|
|
giststate = so->giststate;
|
|
|
|
p = BufferGetPage(so->curbuf);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
IncrIndexProcessed();
|
|
|
|
|
2008-04-14 19:05:34 +02:00
|
|
|
scan->xs_recheck = false;
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Tuple doesn't restore after crash recovery because of incomplete insert
|
2005-09-22 22:44:36 +02:00
|
|
|
*/
|
|
|
|
if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple))
|
2005-06-20 12:29:37 +02:00
|
|
|
return true;
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
while (keySize > 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
Datum datum;
|
|
|
|
bool isNull;
|
|
|
|
Datum test;
|
2008-04-14 19:05:34 +02:00
|
|
|
bool recheck;
|
2003-11-12 22:15:59 +01:00
|
|
|
GISTENTRY de;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
datum = index_getattr(tuple,
|
2003-11-12 22:15:59 +01:00
|
|
|
key->sk_attno,
|
2001-08-22 20:24:26 +02:00
|
|
|
giststate->tupdesc,
|
1997-09-07 07:04:48 +02:00
|
|
|
&isNull);
|
2006-05-24 13:01:39 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (key->sk_flags & SK_ISNULL)
|
|
|
|
{
|
|
|
|
/*
|
2007-04-07 00:33:43 +02:00
|
|
|
* On non-leaf page we can't conclude that child hasn't NULL
|
|
|
|
* values because of assumption in GiST: uinon (VAL, NULL) is VAL
|
2007-11-15 22:14:46 +01:00
|
|
|
* But if on non-leaf page key IS NULL then all childs has NULL.
|
2006-10-04 02:30:14 +02:00
|
|
|
*/
|
2006-05-24 13:01:39 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
Assert(key->sk_flags & SK_SEARCHNULL);
|
2007-04-07 00:33:43 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
if (GistPageIsLeaf(p) && !isNull)
|
2007-04-07 00:33:43 +02:00
|
|
|
return false;
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
|
|
|
else if (isNull)
|
2007-04-07 00:33:43 +02:00
|
|
|
{
|
2001-05-31 20:16:55 +02:00
|
|
|
return false;
|
2007-04-07 00:33:43 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gistdentryinit(giststate, key->sk_attno - 1, &de,
|
|
|
|
datum, r, p, offset,
|
|
|
|
FALSE, isNull);
|
2001-05-15 16:14:49 +02:00
|
|
|
|
2007-04-07 00:33:43 +02:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Call the Consistent function to evaluate the test. The
|
|
|
|
* arguments are the index datum (as a GISTENTRY*), the comparison
|
2008-04-14 19:05:34 +02:00
|
|
|
* datum, the comparison operator's strategy number and
|
|
|
|
* subtype from pg_amop, and the recheck flag.
|
2007-04-07 00:33:43 +02:00
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* (Presently there's no need to pass the subtype since it'll
|
|
|
|
* always be zero, but might as well pass it for possible future
|
|
|
|
* use.)
|
2008-04-14 19:05:34 +02:00
|
|
|
*
|
|
|
|
* We initialize the recheck flag to true (the safest assumption)
|
|
|
|
* in case the Consistent function forgets to set it.
|
2007-04-07 00:33:43 +02:00
|
|
|
*/
|
2008-04-14 19:05:34 +02:00
|
|
|
recheck = true;
|
|
|
|
|
|
|
|
test = FunctionCall5(&key->sk_func,
|
2007-04-07 00:33:43 +02:00
|
|
|
PointerGetDatum(&de),
|
|
|
|
key->sk_argument,
|
|
|
|
Int32GetDatum(key->sk_strategy),
|
2008-04-14 19:05:34 +02:00
|
|
|
ObjectIdGetDatum(key->sk_subtype),
|
|
|
|
PointerGetDatum(&recheck));
|
2007-04-07 00:33:43 +02:00
|
|
|
|
|
|
|
if (!DatumGetBool(test))
|
|
|
|
return false;
|
2008-04-14 19:05:34 +02:00
|
|
|
scan->xs_recheck |= recheck;
|
2007-04-07 00:33:43 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
keySize--;
|
1997-09-07 07:04:48 +02:00
|
|
|
key++;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
2003-11-12 22:15:59 +01:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
|
|
|
* Return the offset of the first index entry that is consistent with
|
|
|
|
* the search key after offset 'n' in the current page. If there are
|
|
|
|
* no more consistent entries, return InvalidOffsetNumber.
|
2008-04-14 19:05:34 +02:00
|
|
|
* On success, scan->xs_recheck is set correctly, too.
|
2005-06-27 14:45:23 +02:00
|
|
|
* Page should be locked....
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static OffsetNumber
|
2005-05-17 02:59:30 +02:00
|
|
|
gistfindnext(IndexScanDesc scan, OffsetNumber n, ScanDirection dir)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2005-09-22 22:44:36 +02:00
|
|
|
OffsetNumber maxoff;
|
|
|
|
IndexTuple it;
|
|
|
|
GISTScanOpaque so;
|
|
|
|
MemoryContext oldcxt;
|
|
|
|
Page p;
|
2005-05-17 02:59:30 +02:00
|
|
|
|
|
|
|
so = (GISTScanOpaque) scan->opaque;
|
|
|
|
p = BufferGetPage(so->curbuf);
|
1997-09-07 07:04:48 +02:00
|
|
|
maxoff = PageGetMaxOffsetNumber(p);
|
2005-05-17 02:59:30 +02:00
|
|
|
|
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* Make sure we're in a short-lived memory context when we invoke a
|
|
|
|
* user-supplied GiST method in gistindex_keytest(), so we don't leak
|
|
|
|
* memory
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
|
|
|
oldcxt = MemoryContextSwitchTo(so->tempCxt);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* If we modified the index during the scan, we may have a pointer to a
|
|
|
|
* ghost tuple, before the scan. If this is the case, back up one.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2005-05-17 02:59:30 +02:00
|
|
|
if (so->flags & GS_CURBEFORE)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-05-17 02:59:30 +02:00
|
|
|
so->flags &= ~GS_CURBEFORE;
|
1997-09-07 07:04:48 +02:00
|
|
|
n = OffsetNumberPrev(n);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
while (n >= FirstOffsetNumber && n <= maxoff)
|
|
|
|
{
|
2001-05-31 20:16:55 +02:00
|
|
|
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
2005-05-17 02:59:30 +02:00
|
|
|
if (gistindex_keytest(it, scan, n))
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (ScanDirectionIsBackward(dir))
|
|
|
|
n = OffsetNumberPrev(n);
|
|
|
|
else
|
|
|
|
n = OffsetNumberNext(n);
|
|
|
|
}
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
MemoryContextReset(so->tempCxt);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
2005-09-22 22:44:36 +02:00
|
|
|
* If we found a matching entry, return its offset; otherwise return
|
|
|
|
* InvalidOffsetNumber to inform the caller to go to the next page.
|
2005-05-17 02:59:30 +02:00
|
|
|
*/
|
|
|
|
if (n >= FirstOffsetNumber && n <= maxoff)
|
|
|
|
return n;
|
|
|
|
else
|
|
|
|
return InvalidOffsetNumber;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|