summaryrefslogtreecommitdiff
path: root/src/backend/access/gist/gistget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/gist/gistget.c')
-rw-r--r--src/backend/access/gist/gistget.c377
1 files changed, 184 insertions, 193 deletions
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 8f7a6c7ed4..d2b0f75fc1 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -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.45 2005/03/27 23:52:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.46 2005/05/17 00:59:30 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,41 +16,71 @@
#include "access/gist.h"
#include "executor/execdebug.h"
+#include "utils/memutils.h"
-
-static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
- ScanDirection dir);
-static bool gistscancache(IndexScanDesc s, ScanDirection dir);
-static bool gistfirst(IndexScanDesc s, ScanDirection dir);
-static bool gistnext(IndexScanDesc s, ScanDirection dir);
-static bool gistindex_keytest(IndexTuple tuple,
- int scanKeySize, ScanKey key, GISTSTATE *giststate,
- Relation r, Page p, OffsetNumber offset);
+static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
+ ScanDirection dir);
+static bool gistnext(IndexScanDesc scan, ScanDirection dir);
+static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
+ OffsetNumber offset);
+/*
+ * gistgettuple() -- Get the next tuple in the scan
+ */
Datum
gistgettuple(PG_FUNCTION_ARGS)
{
- IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
- bool res;
+ IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
+ ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
+ Page page;
+ OffsetNumber offnum;
+ GISTScanOpaque so;
- /* if we have it cached in the scan desc, just return the value */
- if (gistscancache(s, dir))
- PG_RETURN_BOOL(true);
+ so = (GISTScanOpaque) scan->opaque;
- /* not cached, so we'll have to do some work */
- if (ItemPointerIsValid(&(s->currentItemData)))
- res = gistnext(s, dir);
- else
- res = gistfirst(s, dir);
- PG_RETURN_BOOL(res);
+ /*
+ * 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.
+ *
+ * XXX: right now there is no concurrent access. In the
+ * future, we should (a) get a read lock on the page (b) check
+ * that the location of the previously-fetched tuple hasn't
+ * changed due to concurrent insertions.
+ */
+ if (scan->kill_prior_tuple && ItemPointerIsValid(&(scan->currentItemData)))
+ {
+ offnum = ItemPointerGetOffsetNumber(&(scan->currentItemData));
+ page = BufferGetPage(so->curbuf);
+ PageGetItemId(page, offnum)->lp_flags |= LP_DELETE;
+ SetBufferCommitInfoNeedsSave(so->curbuf);
+ }
+
+ /*
+ * Get the next tuple that matches the search key. If asked to
+ * skip killed tuples, continue looping until we find a non-killed
+ * tuple that matches the search key.
+ */
+ for (;;)
+ {
+ bool res = gistnext(scan, dir);
+
+ if (res == true && scan->ignore_killed_tuples)
+ {
+ offnum = ItemPointerGetOffsetNumber(&(scan->currentItemData));
+ page = BufferGetPage(so->curbuf);
+ if (ItemIdDeleted(PageGetItemId(page, offnum)))
+ continue;
+ }
+
+ PG_RETURN_BOOL(res);
+ }
}
Datum
gistgetmulti(PG_FUNCTION_ARGS)
{
- IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
+ 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);
@@ -60,13 +90,10 @@ gistgetmulti(PG_FUNCTION_ARGS)
/* XXX generic implementation: loop around guts of gistgettuple */
while (ntids < max_tids)
{
- if (ItemPointerIsValid(&(s->currentItemData)))
- res = gistnext(s, ForwardScanDirection);
- else
- res = gistfirst(s, ForwardScanDirection);
+ res = gistnext(scan, ForwardScanDirection);
if (!res)
break;
- tids[ntids] = s->xs_ctup.t_self;
+ tids[ntids] = scan->xs_ctup.t_self;
ntids++;
}
@@ -74,166 +101,123 @@ gistgetmulti(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+/*
+ * Fetch a tuple that matchs the search key; this can be invoked
+ * either to fetch the first such tuple or subsequent matching
+ * tuples. Returns true iff a matching tuple was found.
+ */
static bool
-gistfirst(IndexScanDesc s, ScanDirection dir)
+gistnext(IndexScanDesc scan, ScanDirection dir)
{
- Buffer b;
Page p;
OffsetNumber n;
- OffsetNumber maxoff;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTACK *stk;
- BlockNumber blk;
IndexTuple it;
- so = (GISTScanOpaque) s->opaque;
+ so = (GISTScanOpaque) scan->opaque;
+
+ if (ItemPointerIsValid(&scan->currentItemData) == false)
+ {
+ /* Being asked to fetch the first entry, so start at the root */
+ Assert(so->curbuf == InvalidBuffer);
+ so->curbuf = ReadBuffer(scan->indexRelation, GIST_ROOT_BLKNO);
+ }
- b = ReadBuffer(s->indexRelation, GISTP_ROOT);
- p = BufferGetPage(b);
+ p = BufferGetPage(so->curbuf);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
- for (;;)
+ if (ItemPointerIsValid(&scan->currentItemData) == false)
{
- maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
- n = gistfindnext(s, p, maxoff, dir);
+ n = PageGetMaxOffsetNumber(p);
else
- n = gistfindnext(s, p, FirstOffsetNumber, dir);
-
- while (n < FirstOffsetNumber || n > maxoff)
- {
- stk = so->s_stack;
- if (stk == NULL)
- {
- ReleaseBuffer(b);
- return false;
- }
-
- b = ReleaseAndReadBuffer(b, s->indexRelation, stk->gs_blk);
- p = BufferGetPage(b);
- po = (GISTPageOpaque) PageGetSpecialPointer(p);
- maxoff = PageGetMaxOffsetNumber(p);
-
- if (ScanDirectionIsBackward(dir))
- n = OffsetNumberPrev(stk->gs_child);
- else
- n = OffsetNumberNext(stk->gs_child);
-
- so->s_stack = stk->gs_parent;
- pfree(stk);
-
- n = gistfindnext(s, p, n, dir);
- }
- if (po->flags & F_LEAF)
- {
- ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
-
- it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
-
- s->xs_ctup.t_self = it->t_tid;
-
- ReleaseBuffer(b);
- return true;
- }
- else
- {
- stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
- stk->gs_child = n;
- stk->gs_blk = BufferGetBlockNumber(b);
- stk->gs_parent = so->s_stack;
- so->s_stack = stk;
-
- it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
- blk = ItemPointerGetBlockNumber(&(it->t_tid));
-
- b = ReleaseAndReadBuffer(b, s->indexRelation, blk);
- p = BufferGetPage(b);
- po = (GISTPageOpaque) PageGetSpecialPointer(p);
- }
+ n = FirstOffsetNumber;
}
-}
-
-static bool
-gistnext(IndexScanDesc s, ScanDirection dir)
-{
- Buffer b;
- Page p;
- OffsetNumber n;
- OffsetNumber maxoff;
- GISTPageOpaque po;
- GISTScanOpaque so;
- GISTSTACK *stk;
- BlockNumber blk;
- IndexTuple it;
-
- so = (GISTScanOpaque) s->opaque;
-
- blk = ItemPointerGetBlockNumber(&(s->currentItemData));
- n = ItemPointerGetOffsetNumber(&(s->currentItemData));
-
- if (ScanDirectionIsForward(dir))
- n = OffsetNumberNext(n);
else
- n = OffsetNumberPrev(n);
+ {
+ n = ItemPointerGetOffsetNumber(&(scan->currentItemData));
- b = ReadBuffer(s->indexRelation, blk);
- p = BufferGetPage(b);
- po = (GISTPageOpaque) PageGetSpecialPointer(p);
+ if (ScanDirectionIsBackward(dir))
+ n = OffsetNumberPrev(n);
+ else
+ n = OffsetNumberNext(n);
+ }
for (;;)
{
- maxoff = PageGetMaxOffsetNumber(p);
- n = gistfindnext(s, p, n, dir);
+ n = gistfindnext(scan, n, dir);
- while (n < FirstOffsetNumber || n > maxoff)
+ if (!OffsetNumberIsValid(n))
{
- stk = so->s_stack;
- if (stk == NULL)
+ /*
+ * We ran out of matching index entries on the current
+ * page, so pop the top stack entry and use it to continue
+ * the search.
+ */
+ /* If we're out of stack entries, we're done */
+ if (so->stack == NULL)
{
- ReleaseBuffer(b);
+ ReleaseBuffer(so->curbuf);
+ so->curbuf = InvalidBuffer;
return false;
}
- b = ReleaseAndReadBuffer(b, s->indexRelation, stk->gs_blk);
- p = BufferGetPage(b);
+ stk = so->stack;
+ so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
+ stk->block);
+ p = BufferGetPage(so->curbuf);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
- maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
- n = OffsetNumberPrev(stk->gs_child);
+ n = OffsetNumberPrev(stk->offset);
else
- n = OffsetNumberNext(stk->gs_child);
+ n = OffsetNumberNext(stk->offset);
- so->s_stack = stk->gs_parent;
+ so->stack = stk->parent;
pfree(stk);
- n = gistfindnext(s, p, n, dir);
+ continue;
}
+
if (po->flags & F_LEAF)
{
- ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
+ /*
+ * We've found a matching index entry in a leaf page, so
+ * return success. Note that we keep "curbuf" pinned so
+ * that we can efficiently resume the index scan later.
+ */
+ ItemPointerSet(&(scan->currentItemData),
+ BufferGetBlockNumber(so->curbuf), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
-
- s->xs_ctup.t_self = it->t_tid;
-
- ReleaseBuffer(b);
+ scan->xs_ctup.t_self = it->t_tid;
return true;
}
else
{
+ /*
+ * We've found an entry in an internal node whose key is
+ * consistent with the search key, so continue the search
+ * in the pointed-to child node (i.e. we search depth
+ * first). Push the current node onto the stack so we
+ * resume searching from this node later.
+ */
+ BlockNumber child_block;
+
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
- stk->gs_child = n;
- stk->gs_blk = BufferGetBlockNumber(b);
- stk->gs_parent = so->s_stack;
- so->s_stack = stk;
+ stk->offset = n;
+ stk->block = BufferGetBlockNumber(so->curbuf);
+ stk->parent = so->stack;
+ so->stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
- blk = ItemPointerGetBlockNumber(&(it->t_tid));
+ child_block = ItemPointerGetBlockNumber(&(it->t_tid));
- b = ReleaseAndReadBuffer(b, s->indexRelation, blk);
- p = BufferGetPage(b);
+ so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
+ child_block);
+ p = BufferGetPage(so->curbuf);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir))
@@ -244,19 +228,34 @@ gistnext(IndexScanDesc s, ScanDirection dir)
}
}
-/* Similar to index_keytest, but decompresses the key in the IndexTuple */
+/*
+ * Similar to index_keytest, but first 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 invoke 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).
+ */
static bool
gistindex_keytest(IndexTuple tuple,
- int scanKeySize,
- ScanKey key,
- GISTSTATE *giststate,
- Relation r,
- Page p,
+ IndexScanDesc scan,
OffsetNumber offset)
{
+ int keySize = scan->numberOfKeys;
+ ScanKey key = scan->keyData;
+ Relation r = scan->indexRelation;
+ GISTScanOpaque so;
+ Page p;
+ GISTSTATE *giststate;
+
+ so = (GISTScanOpaque) scan->opaque;
+ giststate = so->giststate;
+ p = BufferGetPage(so->curbuf);
+
IncrIndexProcessed();
- while (scanKeySize > 0)
+ while (keySize > 0)
{
Datum datum;
bool isNull;
@@ -297,53 +296,57 @@ gistindex_keytest(IndexTuple tuple,
Int32GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype));
- /* if index datum had to be decompressed, free it */
- if (de.key != datum && !isAttByVal(giststate, key->sk_attno - 1))
- if (DatumGetPointer(de.key) != NULL)
- pfree(DatumGetPointer(de.key));
-
if (!DatumGetBool(test))
return false;
- scanKeySize--;
+ keySize--;
key++;
}
return true;
}
-
+/*
+ * 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.
+ */
static OffsetNumber
-gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
+gistfindnext(IndexScanDesc scan, OffsetNumber n, ScanDirection dir)
{
- OffsetNumber maxoff;
- IndexTuple it;
- GISTPageOpaque po;
- GISTScanOpaque so;
- GISTSTATE *giststate;
-
+ OffsetNumber maxoff;
+ IndexTuple it;
+ GISTPageOpaque po;
+ GISTScanOpaque so;
+ MemoryContext oldcxt;
+ Page p;
+
+ so = (GISTScanOpaque) scan->opaque;
+ p = BufferGetPage(so->curbuf);
maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
- so = (GISTScanOpaque) s->opaque;
- giststate = so->giststate;
+
+ /*
+ * 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
+ */
+ oldcxt = MemoryContextSwitchTo(so->tempCxt);
/*
* 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.
*/
-
- if (so->s_flags & GS_CURBEFORE)
+ if (so->flags & GS_CURBEFORE)
{
- so->s_flags &= ~GS_CURBEFORE;
+ so->flags &= ~GS_CURBEFORE;
n = OffsetNumberPrev(n);
}
while (n >= FirstOffsetNumber && n <= maxoff)
{
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
- if (gistindex_keytest(it,
- s->numberOfKeys, s->keyData, giststate,
- s->indexRelation, p, n))
+ if (gistindex_keytest(it, scan, n))
break;
if (ScanDirectionIsBackward(dir))
@@ -352,28 +355,16 @@ gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
n = OffsetNumberNext(n);
}
- return n;
-}
-
-static bool
-gistscancache(IndexScanDesc s, ScanDirection dir)
-{
- Buffer b;
- Page p;
- OffsetNumber n;
- IndexTuple it;
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextReset(so->tempCxt);
- if (!(ScanDirectionIsNoMovement(dir)
- && ItemPointerIsValid(&(s->currentItemData))))
- return false;
-
- b = ReadBuffer(s->indexRelation,
- ItemPointerGetBlockNumber(&(s->currentItemData)));
- p = BufferGetPage(b);
- n = ItemPointerGetOffsetNumber(&(s->currentItemData));
- it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
- s->xs_ctup.t_self = it->t_tid;
- ReleaseBuffer(b);
-
- return true;
+ /*
+ * If we found a matching entry, return its offset; otherwise
+ * return InvalidOffsetNumber to inform the caller to go to the
+ * next page.
+ */
+ if (n >= FirstOffsetNumber && n <= maxoff)
+ return n;
+ else
+ return InvalidOffsetNumber;
}