Commit eda6dd32 authored by Neil Conway's avatar Neil Conway

GiST improvements:

- make sure we always invoke user-supplied GiST methods in a short-lived
  memory context. This means the backend isn't exposed to any memory leaks
  that be in those methods (in fact, it is probably a net loss for most
  GiST methods to bother manually freeing memory now). This also means
  we can do away with a lot of ugly manual memory management in the
  GiST code itself.

- keep the current page of a GiST index scan pinned, rather than doing a
  ReadBuffer() for each tuple produced by the scan. Since ReadBuffer() is
  expensive, this is a perf. win

- implement dead tuple killing for GiST indexes (which is easy to do, now
  that we keep a pin on the current scan page). Now all the builtin indexes
  implement dead tuple killing.

- cleanup a lot of ugly code in GiST
parent 818bfda1
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/gist.sgml,v 1.17 2005/04/09 03:52:43 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/gist.sgml,v 1.18 2005/05/17 00:59:30 neilc Exp $
--> -->
<chapter id="GiST"> <chapter id="GiST">
...@@ -202,7 +202,7 @@ $PostgreSQL: pgsql/doc/src/sgml/gist.sgml,v 1.17 2005/04/09 03:52:43 momjian Exp ...@@ -202,7 +202,7 @@ $PostgreSQL: pgsql/doc/src/sgml/gist.sgml,v 1.17 2005/04/09 03:52:43 momjian Exp
<para> <para>
The lack of write-ahead logging is just a small matter of programming, The lack of write-ahead logging is just a small matter of programming,
but since it isn't done yet, a crash could render a <acronym>GiST</acronym> but since it isn't done yet, a crash could render a <acronym>GiST</acronym>
index inconsistent, forcing a REINDEX. index inconsistent, forcing a <command>REINDEX</command>.
</para> </para>
</sect1> </sect1>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.115 2005/05/15 04:08:29 neilc Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.116 2005/05/17 00:59:30 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "catalog/index.h" #include "catalog/index.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/memutils.h"
#undef GIST_PAGEADDITEM #undef GIST_PAGEADDITEM
...@@ -45,13 +46,13 @@ ...@@ -45,13 +46,13 @@
* and gistadjsubkey only * and gistadjsubkey only
*/ */
#define FILLITEM(evp, isnullkey, okey, okeyb, rkey, rkeyb) do { \ #define FILLITEM(evp, isnullkey, okey, okeyb, rkey, rkeyb) do { \
if (isnullkey) { \ if (isnullkey) { \
gistentryinit((evp), rkey, r, NULL, \ gistentryinit((evp), rkey, r, NULL, \
(OffsetNumber) 0, rkeyb, FALSE); \ (OffsetNumber) 0, rkeyb, FALSE); \
} else { \ } else { \
gistentryinit((evp), okey, r, NULL, \ gistentryinit((evp), okey, r, NULL, \
(OffsetNumber) 0, okeyb, FALSE); \ (OffsetNumber) 0, okeyb, FALSE); \
} \ } \
} while(0) } while(0)
#define FILLEV(isnull1, key1, key1b, isnull2, key2, key2b) do { \ #define FILLEV(isnull1, key1, key1b, isnull2, key2, key2b) do { \
...@@ -65,6 +66,7 @@ typedef struct ...@@ -65,6 +66,7 @@ typedef struct
GISTSTATE giststate; GISTSTATE giststate;
int numindexattrs; int numindexattrs;
double indtuples; double indtuples;
MemoryContext tmpCxt;
} GISTBuildState; } GISTBuildState;
...@@ -128,9 +130,8 @@ static void gistcentryinit(GISTSTATE *giststate, int nkey, ...@@ -128,9 +130,8 @@ static void gistcentryinit(GISTSTATE *giststate, int nkey,
Relation r, Page pg, Relation r, Page pg,
OffsetNumber o, int b, bool l, bool isNull); OffsetNumber o, int b, bool l, bool isNull);
static void gistDeCompressAtt(GISTSTATE *giststate, Relation r, static void gistDeCompressAtt(GISTSTATE *giststate, Relation r,
IndexTuple tuple, Page p, OffsetNumber o, IndexTuple tuple, Page p, OffsetNumber o,
GISTENTRY *attdata, bool *decompvec, bool *isnull); GISTENTRY *attdata, bool *isnull);
static void gistFreeAtt(Relation r, GISTENTRY *attdata, bool *decompvec);
static void gistpenalty(GISTSTATE *giststate, int attno, static void gistpenalty(GISTSTATE *giststate, int attno,
GISTENTRY *key1, bool isNull1, GISTENTRY *key1, bool isNull1,
GISTENTRY *key2, bool isNull2, GISTENTRY *key2, bool isNull2,
...@@ -143,7 +144,28 @@ static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber c ...@@ -143,7 +144,28 @@ static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber c
#endif #endif
/* /*
* routine to build an index. Basically calls insert over and over * Create and return a temporary memory context for use by GiST. We
* _always_ invoke user-provided methods in a temporary memory
* context, so that memory leaks in those functions cannot cause
* problems. Also, we use some additional temporary contexts in the
* GiST code itself, to avoid the need to do some awkward manual
* memory management.
*/
MemoryContext
createTempGistContext(void)
{
return AllocSetContextCreate(CurrentMemoryContext,
"GiST temporary context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
}
/*
* Routine to build an index. Basically calls insert over and over.
*
* XXX: it would be nice to implement some sort of bulk-loading
* algorithm, but it is not clear how to do that.
*/ */
Datum Datum
gistbuild(PG_FUNCTION_ARGS) gistbuild(PG_FUNCTION_ARGS)
...@@ -155,10 +177,6 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -155,10 +177,6 @@ gistbuild(PG_FUNCTION_ARGS)
GISTBuildState buildstate; GISTBuildState buildstate;
Buffer buffer; Buffer buffer;
/* no locking is needed */
initGISTstate(&buildstate.giststate, index);
/* /*
* We expect to be called exactly once for any index relation. If * We expect to be called exactly once for any index relation. If
* that's not the case, big trouble's what we have. * that's not the case, big trouble's what we have.
...@@ -167,6 +185,9 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -167,6 +185,9 @@ gistbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data", elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index)); RelationGetRelationName(index));
/* no locking is needed */
initGISTstate(&buildstate.giststate, index);
/* initialize the root page */ /* initialize the root page */
buffer = ReadBuffer(index, P_NEW); buffer = ReadBuffer(index, P_NEW);
GISTInitBuffer(buffer, F_LEAF); GISTInitBuffer(buffer, F_LEAF);
...@@ -175,21 +196,27 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -175,21 +196,27 @@ gistbuild(PG_FUNCTION_ARGS)
/* build the index */ /* build the index */
buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs; buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs;
buildstate.indtuples = 0; buildstate.indtuples = 0;
/*
* create a temporary memory context that is reset once for each
* tuple inserted into the index
*/
buildstate.tmpCxt = createTempGistContext();
/* do the heap scan */ /* do the heap scan */
reltuples = IndexBuildHeapScan(heap, index, indexInfo, reltuples = IndexBuildHeapScan(heap, index, indexInfo,
gistbuildCallback, (void *) &buildstate); gistbuildCallback, (void *) &buildstate);
/* okay, all heap tuples are indexed */ /* okay, all heap tuples are indexed */
MemoryContextDelete(buildstate.tmpCxt);
/* since we just counted the # of tuples, may as well update stats */ /* since we just counted the # of tuples, may as well update stats */
IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples);
freeGISTstate(&buildstate.giststate); freeGISTstate(&buildstate.giststate);
#ifdef GISTDEBUG #ifdef GISTDEBUG
gist_dumptree(index, 0, GISTP_ROOT, 0); gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0);
#endif #endif
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -206,32 +233,26 @@ gistbuildCallback(Relation index, ...@@ -206,32 +233,26 @@ gistbuildCallback(Relation index,
{ {
GISTBuildState *buildstate = (GISTBuildState *) state; GISTBuildState *buildstate = (GISTBuildState *) state;
IndexTuple itup; IndexTuple itup;
bool compvec[INDEX_MAX_KEYS];
GISTENTRY tmpcentry; GISTENTRY tmpcentry;
int i; int i;
MemoryContext oldCxt;
/* GiST cannot index tuples with leading NULLs */ /* GiST cannot index tuples with leading NULLs */
if (isnull[0]) if (isnull[0])
return; return;
oldCxt = MemoryContextSwitchTo(buildstate->tmpCxt);
/* immediately compress keys to normalize */ /* immediately compress keys to normalize */
for (i = 0; i < buildstate->numindexattrs; i++) for (i = 0; i < buildstate->numindexattrs; i++)
{ {
if (isnull[i]) if (isnull[i])
{
values[i] = (Datum) 0; values[i] = (Datum) 0;
compvec[i] = FALSE;
}
else else
{ {
gistcentryinit(&buildstate->giststate, i, &tmpcentry, values[i], gistcentryinit(&buildstate->giststate, i, &tmpcentry, values[i],
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
-1 /* size is currently bogus */ , TRUE, FALSE); -1 /* size is currently bogus */, TRUE, FALSE);
if (values[i] != tmpcentry.key &&
!(isAttByVal(&buildstate->giststate, i)))
compvec[i] = TRUE;
else
compvec[i] = FALSE;
values[i] = tmpcentry.key; values[i] = tmpcentry.key;
} }
} }
...@@ -250,12 +271,8 @@ gistbuildCallback(Relation index, ...@@ -250,12 +271,8 @@ gistbuildCallback(Relation index,
gistdoinsert(index, itup, &buildstate->giststate); gistdoinsert(index, itup, &buildstate->giststate);
buildstate->indtuples += 1; buildstate->indtuples += 1;
MemoryContextSwitchTo(oldCxt);
for (i = 0; i < buildstate->numindexattrs; i++) MemoryContextReset(buildstate->tmpCxt);
if (compvec[i])
pfree(DatumGetPointer(values[i]));
pfree(itup);
} }
/* /*
...@@ -271,7 +288,6 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -271,7 +288,6 @@ gistinsert(PG_FUNCTION_ARGS)
Datum *values = (Datum *) PG_GETARG_POINTER(1); Datum *values = (Datum *) PG_GETARG_POINTER(1);
bool *isnull = (bool *) PG_GETARG_POINTER(2); bool *isnull = (bool *) PG_GETARG_POINTER(2);
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3); ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
#ifdef NOT_USED #ifdef NOT_USED
Relation heapRel = (Relation) PG_GETARG_POINTER(4); Relation heapRel = (Relation) PG_GETARG_POINTER(4);
bool checkUnique = PG_GETARG_BOOL(5); bool checkUnique = PG_GETARG_BOOL(5);
...@@ -280,7 +296,8 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -280,7 +296,8 @@ gistinsert(PG_FUNCTION_ARGS)
GISTSTATE giststate; GISTSTATE giststate;
GISTENTRY tmpentry; GISTENTRY tmpentry;
int i; int i;
bool compvec[INDEX_MAX_KEYS]; MemoryContext oldCxt;
MemoryContext insertCxt;
/* /*
* Since GIST is not marked "amconcurrent" in pg_am, caller should * Since GIST is not marked "amconcurrent" in pg_am, caller should
...@@ -292,25 +309,21 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -292,25 +309,21 @@ gistinsert(PG_FUNCTION_ARGS)
if (isnull[0]) if (isnull[0])
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
insertCxt = createTempGistContext();
oldCxt = MemoryContextSwitchTo(insertCxt);
initGISTstate(&giststate, r); initGISTstate(&giststate, r);
/* immediately compress keys to normalize */ /* immediately compress keys to normalize */
for (i = 0; i < r->rd_att->natts; i++) for (i = 0; i < r->rd_att->natts; i++)
{ {
if (isnull[i]) if (isnull[i])
{
values[i] = (Datum) 0; values[i] = (Datum) 0;
compvec[i] = FALSE;
}
else else
{ {
gistcentryinit(&giststate, i, &tmpentry, values[i], gistcentryinit(&giststate, i, &tmpentry, values[i],
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
-1 /* size is currently bogus */ , TRUE, FALSE); -1 /* size is currently bogus */, TRUE, FALSE);
if (values[i] != tmpentry.key && !(isAttByVal(&giststate, i)))
compvec[i] = TRUE;
else
compvec[i] = FALSE;
values[i] = tmpentry.key; values[i] = tmpentry.key;
} }
} }
...@@ -319,11 +332,10 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -319,11 +332,10 @@ gistinsert(PG_FUNCTION_ARGS)
gistdoinsert(r, itup, &giststate); gistdoinsert(r, itup, &giststate);
for (i = 0; i < r->rd_att->natts; i++) /* cleanup */
if (compvec[i] == TRUE)
pfree(DatumGetPointer(values[i]));
pfree(itup);
freeGISTstate(&giststate); freeGISTstate(&giststate);
MemoryContextSwitchTo(oldCxt);
MemoryContextDelete(insertCxt);
PG_RETURN_BOOL(true); PG_RETURN_BOOL(true);
} }
...@@ -370,36 +382,29 @@ gistPageAddItem(GISTSTATE *giststate, ...@@ -370,36 +382,29 @@ gistPageAddItem(GISTSTATE *giststate,
if (retval == InvalidOffsetNumber) if (retval == InvalidOffsetNumber)
elog(ERROR, "failed to add index item to \"%s\"", elog(ERROR, "failed to add index item to \"%s\"",
RelationGetRelationName(r)); RelationGetRelationName(r));
/* be tidy */ return retval;
if (DatumGetPointer(tmpcentry.key) != NULL &&
tmpcentry.key != dentry->key &&
tmpcentry.key != datum)
pfree(DatumGetPointer(tmpcentry.key));
return (retval);
} }
#endif #endif
/*
* Workhouse routine for doing insertion into a GiST index. Note that
* this routine assumes it is invoked in a short-lived memory context,
* so it does not bother releasing palloc'd allocations.
*/
static void static void
gistdoinsert(Relation r, gistdoinsert(Relation r, IndexTuple itup, GISTSTATE *giststate)
IndexTuple itup,
GISTSTATE *giststate)
{ {
IndexTuple *instup; IndexTuple *instup;
int i, int ret,
ret,
len = 1; len = 1;
instup = (IndexTuple *) palloc(sizeof(IndexTuple)); instup = (IndexTuple *) palloc(sizeof(IndexTuple));
instup[0] = (IndexTuple) palloc(IndexTupleSize(itup)); instup[0] = (IndexTuple) palloc(IndexTupleSize(itup));
memcpy(instup[0], itup, IndexTupleSize(itup)); memcpy(instup[0], itup, IndexTupleSize(itup));
ret = gistlayerinsert(r, GISTP_ROOT, &instup, &len, giststate); ret = gistlayerinsert(r, GIST_ROOT_BLKNO, &instup, &len, giststate);
if (ret & SPLITED) if (ret & SPLITED)
gistnewroot(r, instup, len); gistnewroot(r, instup, len);
for (i = 0; i < len; i++)
pfree(instup[i]);
pfree(instup);
} }
static int static int
...@@ -410,7 +415,6 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -410,7 +415,6 @@ gistlayerinsert(Relation r, BlockNumber blkno,
{ {
Buffer buffer; Buffer buffer;
Page page; Page page;
OffsetNumber child;
int ret; int ret;
GISTPageOpaque opaque; GISTPageOpaque opaque;
...@@ -420,12 +424,20 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -420,12 +424,20 @@ gistlayerinsert(Relation r, BlockNumber blkno,
if (!(opaque->flags & F_LEAF)) if (!(opaque->flags & F_LEAF))
{ {
/* internal page, so we must walk on tree */ /*
/* len IS equal 1 */ * This is an internal page, so continue to walk down the
* tree. We find the child node that has the minimum insertion
* penalty and recursively invoke ourselves to modify that
* node. Once the recursive call returns, we may need to
* adjust the parent node for two reasons: the child node
* split, or the key in this node needs to be adjusted for the
* newly inserted key below us.
*/
ItemId iid; ItemId iid;
BlockNumber nblkno; BlockNumber nblkno;
ItemPointerData oldtid; ItemPointerData oldtid;
IndexTuple oldtup; IndexTuple oldtup;
OffsetNumber child;
child = gistchoose(r, page, *(*itup), giststate); child = gistchoose(r, page, *(*itup), giststate);
iid = PageGetItemId(page, child); iid = PageGetItemId(page, child);
...@@ -446,7 +458,7 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -446,7 +458,7 @@ gistlayerinsert(Relation r, BlockNumber blkno,
return 0x00; return 0x00;
} }
/* child does not splited */ /* child did not split */
if (!(ret & SPLITED)) if (!(ret & SPLITED))
{ {
IndexTuple newtup = gistgetadjusted(r, oldtup, (*itup)[0], giststate); IndexTuple newtup = gistgetadjusted(r, oldtup, (*itup)[0], giststate);
...@@ -458,11 +470,15 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -458,11 +470,15 @@ gistlayerinsert(Relation r, BlockNumber blkno,
return 0x00; return 0x00;
} }
pfree((*itup)[0]); /* !!! */
(*itup)[0] = newtup; (*itup)[0] = newtup;
} }
/* key is modified, so old version must be deleted */ /*
* This node's key has been modified, either because a child
* split occurred or because we needed to adjust our key for
* an insert in a child node. Therefore, remove the old
* version of this node's key.
*/
ItemPointerSet(&oldtid, blkno, child); ItemPointerSet(&oldtid, blkno, child);
gistdelete(r, &oldtid); gistdelete(r, &oldtid);
...@@ -491,11 +507,6 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -491,11 +507,6 @@ gistlayerinsert(Relation r, BlockNumber blkno,
oldlen = *len; oldlen = *len;
newitup = gistSplit(r, buffer, itvec, &tlen, giststate); newitup = gistSplit(r, buffer, itvec, &tlen, giststate);
ReleaseBuffer(buffer); ReleaseBuffer(buffer);
do
pfree((*itup)[oldlen - 1]);
while ((--oldlen) > 0);
pfree((*itup));
pfree(itvec);
*itup = newitup; *itup = newitup;
*len = tlen; /* now tlen >= 2 */ *len = tlen; /* now tlen >= 2 */
} }
...@@ -509,23 +520,17 @@ gistlayerinsert(Relation r, BlockNumber blkno, ...@@ -509,23 +520,17 @@ gistlayerinsert(Relation r, BlockNumber blkno,
FirstOffsetNumber FirstOffsetNumber
: :
OffsetNumberNext(PageGetMaxOffsetNumber(page)); OffsetNumberNext(PageGetMaxOffsetNumber(page));
l = gistwritebuffer(r, page, (*itup), *len, off); l = gistwritebuffer(r, page, *itup, *len, off);
WriteBuffer(buffer); WriteBuffer(buffer);
if (*len > 1) if (*len > 1)
{ /* previous insert ret & SPLITED != 0 */ { /* previous insert ret & SPLITED != 0 */
int i;
/* /*
* child was splited, so we must form union for insertion in * child was splited, so we must form union for insertion in
* parent * parent
*/ */
IndexTuple newtup = gistunion(r, (*itup), *len, giststate); IndexTuple newtup = gistunion(r, (*itup), *len, giststate);
ItemPointerSet(&(newtup->t_tid), blkno, 1); ItemPointerSet(&(newtup->t_tid), blkno, 1);
for (i = 0; i < *len; i++)
pfree((*itup)[i]);
(*itup)[0] = newtup; (*itup)[0] = newtup;
*len = 1; *len = 1;
} }
...@@ -544,23 +549,17 @@ gistwritebuffer(Relation r, Page page, IndexTuple *itup, ...@@ -544,23 +549,17 @@ gistwritebuffer(Relation r, Page page, IndexTuple *itup,
OffsetNumber l = InvalidOffsetNumber; OffsetNumber l = InvalidOffsetNumber;
int i; int i;
#ifdef GIST_PAGEADDITEM
GISTENTRY tmpdentry;
IndexTuple newtup;
bool IsNull;
#endif
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
#ifdef GIST_PAGEADDITEM #ifdef GIST_PAGEADDITEM
GISTENTRY tmpdentry;
IndexTuple newtup;
bool IsNull;
l = gistPageAddItem(giststate, r, page, l = gistPageAddItem(giststate, r, page,
(Item) itup[i], IndexTupleSize(itup[i]), (Item) itup[i], IndexTupleSize(itup[i]),
off, LP_USED, &tmpdentry, &newtup); off, LP_USED, &tmpdentry, &newtup);
off = OffsetNumberNext(off); off = OffsetNumberNext(off);
if (DatumGetPointer(tmpdentry.key) != NULL &&
tmpdentry.key != index_getattr(itup[i], 1, r->rd_att, &IsNull))
pfree(DatumGetPointer(tmpdentry.key));
if (itup[i] != newtup)
pfree(newtup);
#else #else
l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]), l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]),
off, LP_USED); off, LP_USED);
...@@ -620,101 +619,77 @@ gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen) ...@@ -620,101 +619,77 @@ gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
} }
/* /*
* return union of itup vector * Return an IndexTuple containing the result of applying the "union"
* method to the specified IndexTuple vector.
*/ */
static IndexTuple static IndexTuple
gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate) gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
{ {
Datum attr[INDEX_MAX_KEYS]; Datum attr[INDEX_MAX_KEYS];
bool whatfree[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS];
GistEntryVector *evec; GistEntryVector *evec;
Datum datum; int i;
int datumsize,
i,
j;
GISTENTRY centry[INDEX_MAX_KEYS]; GISTENTRY centry[INDEX_MAX_KEYS];
bool *needfree;
IndexTuple newtup;
bool IsNull;
int reallen;
needfree = (bool *) palloc(((len == 1) ? 2 : len) * sizeof(bool));
evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ);
for (j = 0; j < r->rd_att->natts; j++) for (i = 0; i < r->rd_att->natts; i++)
{ {
reallen = 0; Datum datum;
for (i = 0; i < len; i++) int j;
int real_len;
real_len = 0;
for (j = 0; j < len; j++)
{ {
datum = index_getattr(itvec[i], j + 1, giststate->tupdesc, &IsNull); bool IsNull;
datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull);
if (IsNull) if (IsNull)
continue; continue;
gistdentryinit(giststate, j, gistdentryinit(giststate, i,
&(evec->vector[reallen]), &(evec->vector[real_len]),
datum, datum,
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull); ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
if ((!isAttByVal(giststate, j)) && FALSE, IsNull);
evec->vector[reallen].key != datum) real_len++;
needfree[reallen] = TRUE;
else
needfree[reallen] = FALSE;
reallen++;
} }
if (reallen == 0) /* If this tuple vector was all NULLs, the union is NULL */
if (real_len == 0)
{ {
attr[j] = (Datum) 0; attr[i] = (Datum) 0;
isnull[j] = TRUE; isnull[i] = TRUE;
whatfree[j] = FALSE;
} }
else else
{ {
if (reallen == 1) int datumsize;
if (real_len == 1)
{ {
evec->n = 2; evec->n = 2;
gistentryinit(evec->vector[1], gistentryinit(evec->vector[1],
evec->vector[0].key, r, NULL, evec->vector[0].key, r, NULL,
(OffsetNumber) 0, evec->vector[0].bytes, FALSE); (OffsetNumber) 0, evec->vector[0].bytes, FALSE);
} }
else else
evec->n = reallen; evec->n = real_len;
datum = FunctionCall2(&giststate->unionFn[j],
/* Compress the result of the union and store in attr array */
datum = FunctionCall2(&giststate->unionFn[i],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(&datumsize));
for (i = 0; i < reallen; i++) gistcentryinit(giststate, i, &centry[i], datum,
if (needfree[i])
pfree(DatumGetPointer(evec->vector[i].key));
gistcentryinit(giststate, j, &centry[j], datum,
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
datumsize, FALSE, FALSE); datumsize, FALSE, FALSE);
isnull[j] = FALSE; isnull[i] = FALSE;
attr[j] = centry[j].key; attr[i] = centry[i].key;
if (!isAttByVal(giststate, j))
{
whatfree[j] = TRUE;
if (centry[j].key != datum)
pfree(DatumGetPointer(datum));
}
else
whatfree[j] = FALSE;
} }
} }
pfree(evec); return index_form_tuple(giststate->tupdesc, attr, isnull);
pfree(needfree);
newtup = index_form_tuple(giststate->tupdesc, attr, isnull);
for (j = 0; j < r->rd_att->natts; j++)
if (whatfree[j])
pfree(DatumGetPointer(attr[j]));
return newtup;
} }
...@@ -725,24 +700,18 @@ static IndexTuple ...@@ -725,24 +700,18 @@ static IndexTuple
gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
{ {
GistEntryVector *evec; GistEntryVector *evec;
Datum datum; bool neednew = false;
int datumsize; bool isnull[INDEX_MAX_KEYS];
bool result,
neednew = false;
bool isnull[INDEX_MAX_KEYS],
whatfree[INDEX_MAX_KEYS];
Datum attr[INDEX_MAX_KEYS]; Datum attr[INDEX_MAX_KEYS];
GISTENTRY centry[INDEX_MAX_KEYS], GISTENTRY centry[INDEX_MAX_KEYS],
oldatt[INDEX_MAX_KEYS], oldatt[INDEX_MAX_KEYS],
addatt[INDEX_MAX_KEYS], addatt[INDEX_MAX_KEYS],
*ev0p, *ev0p,
*ev1p; *ev1p;
bool olddec[INDEX_MAX_KEYS],
adddec[INDEX_MAX_KEYS];
bool oldisnull[INDEX_MAX_KEYS], bool oldisnull[INDEX_MAX_KEYS],
addisnull[INDEX_MAX_KEYS]; addisnull[INDEX_MAX_KEYS];
IndexTuple newtup = NULL; IndexTuple newtup = NULL;
int j; int i;
evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ); evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
evec->n = 2; evec->n = 2;
...@@ -750,39 +719,40 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis ...@@ -750,39 +719,40 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
ev1p = &(evec->vector[1]); ev1p = &(evec->vector[1]);
gistDeCompressAtt(giststate, r, oldtup, NULL, gistDeCompressAtt(giststate, r, oldtup, NULL,
(OffsetNumber) 0, oldatt, olddec, oldisnull); (OffsetNumber) 0, oldatt, oldisnull);
gistDeCompressAtt(giststate, r, addtup, NULL, gistDeCompressAtt(giststate, r, addtup, NULL,
(OffsetNumber) 0, addatt, adddec, addisnull); (OffsetNumber) 0, addatt, addisnull);
for (j = 0; j < r->rd_att->natts; j++) for (i = 0; i < r->rd_att->natts; i++)
{ {
if (oldisnull[j] && addisnull[j]) if (oldisnull[i] && addisnull[i])
{ {
attr[j] = (Datum) 0; attr[i] = (Datum) 0;
isnull[j] = TRUE; isnull[i] = TRUE;
whatfree[j] = FALSE;
} }
else else
{ {
FILLEV( Datum datum;
oldisnull[j], oldatt[j].key, oldatt[j].bytes, int datumsize;
addisnull[j], addatt[j].key, addatt[j].bytes
);
datum = FunctionCall2(&giststate->unionFn[j], FILLEV(oldisnull[i], oldatt[i].key, oldatt[i].bytes,
addisnull[i], addatt[i].key, addatt[i].bytes);
datum = FunctionCall2(&giststate->unionFn[i],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(&datumsize));
if (oldisnull[j] || addisnull[j]) if (oldisnull[i] || addisnull[i])
{ {
if (oldisnull[j]) if (oldisnull[i])
neednew = true; neednew = true;
} }
else else
{ {
FunctionCall3(&giststate->equalFn[j], bool result;
FunctionCall3(&giststate->equalFn[i],
ev0p->key, ev0p->key,
datum, datum,
PointerGetDatum(&result)); PointerGetDatum(&result));
...@@ -791,28 +761,14 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis ...@@ -791,28 +761,14 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
neednew = true; neednew = true;
} }
if (olddec[j]) gistcentryinit(giststate, i, &centry[i], datum,
pfree(DatumGetPointer(oldatt[j].key));
if (adddec[j])
pfree(DatumGetPointer(addatt[j].key));
gistcentryinit(giststate, j, &centry[j], datum,
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
datumsize, FALSE, FALSE); datumsize, FALSE, FALSE);
attr[j] = centry[j].key; attr[i] = centry[i].key;
isnull[j] = FALSE; isnull[i] = FALSE;
if ((!isAttByVal(giststate, j)))
{
whatfree[j] = TRUE;
if (centry[j].key != datum)
pfree(DatumGetPointer(datum));
}
else
whatfree[j] = FALSE;
} }
} }
pfree(evec);
if (neednew) if (neednew)
{ {
...@@ -821,33 +777,24 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis ...@@ -821,33 +777,24 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
newtup->t_tid = oldtup->t_tid; newtup->t_tid = oldtup->t_tid;
} }
for (j = 0; j < r->rd_att->natts; j++)
if (whatfree[j])
pfree(DatumGetPointer(attr[j]));
return newtup; return newtup;
} }
static void static void
gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl) gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
{ {
int i, int lr;
j,
lr;
Datum *attr;
bool *needfree,
IsNull;
int len,
*attrsize;
OffsetNumber *entries;
GistEntryVector *evec;
Datum datum;
int datumsize;
int reallen;
bool *isnull;
for (lr = 0; lr <= 1; lr++) for (lr = 0; lr < 2; lr++)
{ {
OffsetNumber *entries;
int i;
Datum *attr;
int len,
*attrsize;
bool *isnull;
GistEntryVector *evec;
if (lr) if (lr)
{ {
attrsize = spl->spl_lattrsize; attrsize = spl->spl_lattrsize;
...@@ -865,38 +812,41 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV ...@@ -865,38 +812,41 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
isnull = spl->spl_risnull; isnull = spl->spl_risnull;
} }
needfree = (bool *) palloc(((len == 1) ? 2 : len) * sizeof(bool));
evec = palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); evec = palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ);
for (j = 1; j < r->rd_att->natts; j++) for (i = 1; i < r->rd_att->natts; i++)
{ {
reallen = 0; int j;
for (i = 0; i < len; i++) Datum datum;
int datumsize;
int real_len;
real_len = 0;
for (j = 0; j < len; j++)
{ {
if (spl->spl_idgrp[entries[i]]) bool IsNull;
if (spl->spl_idgrp[entries[j]])
continue; continue;
datum = index_getattr(itvec[entries[i] - 1], j + 1, datum = index_getattr(itvec[entries[j] - 1], i + 1,
giststate->tupdesc, &IsNull); giststate->tupdesc, &IsNull);
if (IsNull) if (IsNull)
continue; continue;
gistdentryinit(giststate, j, gistdentryinit(giststate, i,
&(evec->vector[reallen]), &(evec->vector[real_len]),
datum, datum,
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull); ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
if ((!isAttByVal(giststate, j)) && FALSE, IsNull);
evec->vector[reallen].key != datum) real_len++;
needfree[reallen] = TRUE;
else
needfree[reallen] = FALSE;
reallen++;
} }
if (reallen == 0)
if (real_len == 0)
{ {
datum = (Datum) 0; datum = (Datum) 0;
datumsize = 0; datumsize = 0;
isnull[j] = true; isnull[i] = true;
} }
else else
{ {
...@@ -904,30 +854,23 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV ...@@ -904,30 +854,23 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
* evec->vector[0].bytes may be not defined, so form union * evec->vector[0].bytes may be not defined, so form union
* with itself * with itself
*/ */
if (reallen == 1) if (real_len == 1)
{ {
evec->n = 2; evec->n = 2;
memcpy((void *) &(evec->vector[1]), memcpy(&(evec->vector[1]), &(evec->vector[0]),
(void *) &(evec->vector[0]),
sizeof(GISTENTRY)); sizeof(GISTENTRY));
} }
else else
evec->n = reallen; evec->n = real_len;
datum = FunctionCall2(&giststate->unionFn[j], datum = FunctionCall2(&giststate->unionFn[i],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(&datumsize));
isnull[j] = false; isnull[i] = false;
} }
for (i = 0; i < reallen; i++) attr[i] = datum;
if (needfree[i]) attrsize[i] = datumsize;
pfree(DatumGetPointer(evec->vector[i].key));
attr[j] = datum;
attrsize[j] = datumsize;
} }
pfree(evec);
pfree(needfree);
} }
} }
...@@ -937,11 +880,8 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV ...@@ -937,11 +880,8 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
static int static int
gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
{ {
int i, int i;
j,
len;
int curid = 1; int curid = 1;
bool result;
/* /*
* first key is always not null (see gistinsert), so we may not check * first key is always not null (see gistinsert), so we may not check
...@@ -949,6 +889,10 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) ...@@ -949,6 +889,10 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
*/ */
for (i = 0; i < spl->spl_nleft; i++) for (i = 0; i < spl->spl_nleft; i++)
{ {
int j;
int len;
bool result;
if (spl->spl_idgrp[spl->spl_left[i]]) if (spl->spl_idgrp[spl->spl_left[i]])
continue; continue;
len = 0; len = 0;
...@@ -996,8 +940,8 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) ...@@ -996,8 +940,8 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
} }
/* /*
* Insert equivalent tuples to left or right page * Insert equivalent tuples to left or right page with minimum
* with minimize penalty * penalty
*/ */
static void static void
gistadjsubkey(Relation r, gistadjsubkey(Relation r,
...@@ -1008,7 +952,6 @@ gistadjsubkey(Relation r, ...@@ -1008,7 +952,6 @@ gistadjsubkey(Relation r,
{ {
int curlen; int curlen;
OffsetNumber *curwpos; OffsetNumber *curwpos;
bool decfree[INDEX_MAX_KEYS];
GISTENTRY entry, GISTENTRY entry,
identry[INDEX_MAX_KEYS], identry[INDEX_MAX_KEYS],
*ev0p, *ev0p,
...@@ -1020,12 +963,12 @@ gistadjsubkey(Relation r, ...@@ -1020,12 +963,12 @@ gistadjsubkey(Relation r,
bool isnull[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS];
int i, int i,
j; j;
Datum datum;
/* clear vectors */ /* clear vectors */
curlen = v->spl_nleft; curlen = v->spl_nleft;
curwpos = v->spl_left; curwpos = v->spl_left;
for (i = 0; i < v->spl_nleft; i++) for (i = 0; i < v->spl_nleft; i++)
{
if (v->spl_idgrp[v->spl_left[i]] == 0) if (v->spl_idgrp[v->spl_left[i]] == 0)
{ {
*curwpos = v->spl_left[i]; *curwpos = v->spl_left[i];
...@@ -1033,11 +976,13 @@ gistadjsubkey(Relation r, ...@@ -1033,11 +976,13 @@ gistadjsubkey(Relation r,
} }
else else
curlen--; curlen--;
}
v->spl_nleft = curlen; v->spl_nleft = curlen;
curlen = v->spl_nright; curlen = v->spl_nright;
curwpos = v->spl_right; curwpos = v->spl_right;
for (i = 0; i < v->spl_nright; i++) for (i = 0; i < v->spl_nright; i++)
{
if (v->spl_idgrp[v->spl_right[i]] == 0) if (v->spl_idgrp[v->spl_right[i]] == 0)
{ {
*curwpos = v->spl_right[i]; *curwpos = v->spl_right[i];
...@@ -1045,6 +990,7 @@ gistadjsubkey(Relation r, ...@@ -1045,6 +990,7 @@ gistadjsubkey(Relation r,
} }
else else
curlen--; curlen--;
}
v->spl_nright = curlen; v->spl_nright = curlen;
evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ); evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
...@@ -1055,16 +1001,17 @@ gistadjsubkey(Relation r, ...@@ -1055,16 +1001,17 @@ gistadjsubkey(Relation r,
/* add equivalent tuple */ /* add equivalent tuple */
for (i = 0; i < *len; i++) for (i = 0; i < *len; i++)
{ {
Datum datum;
if (v->spl_idgrp[i + 1] == 0) /* already inserted */ if (v->spl_idgrp[i + 1] == 0) /* already inserted */
continue; continue;
gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0, gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0,
identry, decfree, isnull); identry, isnull);
v->spl_ngrp[v->spl_idgrp[i + 1]]--; v->spl_ngrp[v->spl_idgrp[i + 1]]--;
if (v->spl_ngrp[v->spl_idgrp[i + 1]] == 0 && if (v->spl_ngrp[v->spl_idgrp[i + 1]] == 0 &&
(v->spl_grpflag[v->spl_idgrp[i + 1]] & BOTH_ADDED) != BOTH_ADDED) (v->spl_grpflag[v->spl_idgrp[i + 1]] & BOTH_ADDED) != BOTH_ADDED)
{ {
/* force last in group */ /* force last in group */
rpenalty = 1.0; rpenalty = 1.0;
lpenalty = (v->spl_grpflag[v->spl_idgrp[i + 1]] & LEFT_ADDED) ? 2.0 : 0.0; lpenalty = (v->spl_grpflag[v->spl_idgrp[i + 1]] & LEFT_ADDED) ? 2.0 : 0.0;
...@@ -1088,7 +1035,11 @@ gistadjsubkey(Relation r, ...@@ -1088,7 +1035,11 @@ gistadjsubkey(Relation r,
break; break;
} }
} }
/* add */
/*
* add
* XXX: refactor this to avoid duplicating code
*/
if (lpenalty < rpenalty) if (lpenalty < rpenalty)
{ {
v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED; v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED;
...@@ -1103,17 +1054,13 @@ gistadjsubkey(Relation r, ...@@ -1103,17 +1054,13 @@ gistadjsubkey(Relation r,
} }
else else
{ {
FILLEV( FILLEV(v->spl_lisnull[j], v->spl_lattr[j], v->spl_lattrsize[j],
v->spl_lisnull[j], v->spl_lattr[j], v->spl_lattrsize[j], isnull[j], identry[j].key, identry[j].bytes);
isnull[j], identry[j].key, identry[j].bytes
);
datum = FunctionCall2(&giststate->unionFn[j], datum = FunctionCall2(&giststate->unionFn[j],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(&datumsize));
if ((!isAttByVal(giststate, j)) && !v->spl_lisnull[j])
pfree(DatumGetPointer(v->spl_lattr[j]));
v->spl_lattr[j] = datum; v->spl_lattr[j] = datum;
v->spl_lattrsize[j] = datumsize; v->spl_lattrsize[j] = datumsize;
v->spl_lisnull[j] = false; v->spl_lisnull[j] = false;
...@@ -1134,28 +1081,20 @@ gistadjsubkey(Relation r, ...@@ -1134,28 +1081,20 @@ gistadjsubkey(Relation r,
} }
else else
{ {
FILLEV( FILLEV(v->spl_risnull[j], v->spl_rattr[j], v->spl_rattrsize[j],
v->spl_risnull[j], v->spl_rattr[j], v->spl_rattrsize[j], isnull[j], identry[j].key, identry[j].bytes);
isnull[j], identry[j].key, identry[j].bytes
);
datum = FunctionCall2(&giststate->unionFn[j], datum = FunctionCall2(&giststate->unionFn[j],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(&datumsize));
if ((!isAttByVal(giststate, j)) && !v->spl_risnull[j])
pfree(DatumGetPointer(v->spl_rattr[j]));
v->spl_rattr[j] = datum; v->spl_rattr[j] = datum;
v->spl_rattrsize[j] = datumsize; v->spl_rattrsize[j] = datumsize;
v->spl_risnull[j] = false; v->spl_risnull[j] = false;
} }
} }
} }
gistFreeAtt(r, identry, decfree);
} }
pfree(evec);
} }
/* /*
...@@ -1181,13 +1120,8 @@ gistSplit(Relation r, ...@@ -1181,13 +1120,8 @@ gistSplit(Relation r,
GISTPageOpaque opaque; GISTPageOpaque opaque;
GIST_SPLITVEC v; GIST_SPLITVEC v;
GistEntryVector *entryvec; GistEntryVector *entryvec;
bool *decompvec;
int i, int i,
j,
nlen; nlen;
int MaxGrpId = 1;
Datum datum;
bool IsNull;
p = (Page) BufferGetPage(buffer); p = (Page) BufferGetPage(buffer);
opaque = (GISTPageOpaque) PageGetSpecialPointer(p); opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
...@@ -1197,8 +1131,7 @@ gistSplit(Relation r, ...@@ -1197,8 +1131,7 @@ gistSplit(Relation r,
* about to split the root, we need to do some hocus-pocus to enforce * about to split the root, we need to do some hocus-pocus to enforce
* this guarantee. * this guarantee.
*/ */
if (BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO)
if (BufferGetBlockNumber(buffer) == GISTP_ROOT)
{ {
leftbuf = ReadBuffer(r, P_NEW); leftbuf = ReadBuffer(r, P_NEW);
GISTInitBuffer(leftbuf, opaque->flags); GISTInitBuffer(leftbuf, opaque->flags);
...@@ -1221,17 +1154,17 @@ gistSplit(Relation r, ...@@ -1221,17 +1154,17 @@ gistSplit(Relation r,
/* generate the item array */ /* generate the item array */
entryvec = palloc(GEVHDRSZ + (*len + 1) * sizeof(GISTENTRY)); entryvec = palloc(GEVHDRSZ + (*len + 1) * sizeof(GISTENTRY));
entryvec->n = *len + 1; entryvec->n = *len + 1;
decompvec = (bool *) palloc((*len + 1) * sizeof(bool));
for (i = 1; i <= *len; i++) for (i = 1; i <= *len; i++)
{ {
Datum datum;
bool IsNull;
datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull); datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull);
gistdentryinit(giststate, 0, &(entryvec->vector[i]), gistdentryinit(giststate, 0, &(entryvec->vector[i]),
datum, r, p, i, datum, r, p, i,
ATTSIZE(datum, giststate->tupdesc, 1, IsNull), FALSE, IsNull); ATTSIZE(datum, giststate->tupdesc, 1, IsNull),
if ((!isAttByVal(giststate, 0)) && entryvec->vector[i].key != datum) FALSE, IsNull);
decompvec[i] = TRUE;
else
decompvec[i] = FALSE;
} }
/* /*
...@@ -1259,6 +1192,8 @@ gistSplit(Relation r, ...@@ -1259,6 +1192,8 @@ gistSplit(Relation r,
*/ */
if (r->rd_att->natts > 1) if (r->rd_att->natts > 1)
{ {
int MaxGrpId;
v.spl_idgrp = (int *) palloc0(sizeof(int) * (*len + 1)); v.spl_idgrp = (int *) palloc0(sizeof(int) * (*len + 1));
v.spl_grpflag = (char *) palloc0(sizeof(char) * (*len + 1)); v.spl_grpflag = (char *) palloc0(sizeof(char) * (*len + 1));
v.spl_ngrp = (int *) palloc(sizeof(int) * (*len + 1)); v.spl_ngrp = (int *) palloc(sizeof(int) * (*len + 1));
...@@ -1274,19 +1209,8 @@ gistSplit(Relation r, ...@@ -1274,19 +1209,8 @@ gistSplit(Relation r,
*/ */
if (MaxGrpId > 1) if (MaxGrpId > 1)
gistadjsubkey(r, itup, len, &v, giststate); gistadjsubkey(r, itup, len, &v, giststate);
pfree(v.spl_idgrp);
pfree(v.spl_grpflag);
pfree(v.spl_ngrp);
} }
/* clean up the entry vector: its keys need to be deleted, too */
for (i = 1; i <= *len; i++)
if (decompvec[i])
pfree(DatumGetPointer(entryvec->vector[i].key));
pfree(entryvec);
pfree(decompvec);
/* form left and right vector */ /* form left and right vector */
lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nleft); lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nleft);
rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nright); rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nright);
...@@ -1298,15 +1222,12 @@ gistSplit(Relation r, ...@@ -1298,15 +1222,12 @@ gistSplit(Relation r,
rvectup[i] = itup[v.spl_right[i] - 1]; rvectup[i] = itup[v.spl_right[i] - 1];
/* write on disk (may be need another split) */ /* write on disk (may need another split) */
if (gistnospace(right, rvectup, v.spl_nright)) if (gistnospace(right, rvectup, v.spl_nright))
{ {
nlen = v.spl_nright; nlen = v.spl_nright;
newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate); newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate);
ReleaseBuffer(rightbuf); ReleaseBuffer(rightbuf);
for (j = 1; j < r->rd_att->natts; j++)
if ((!isAttByVal(giststate, j)) && !v.spl_risnull[j])
pfree(DatumGetPointer(v.spl_rattr[j]));
} }
else else
{ {
...@@ -1321,7 +1242,6 @@ gistSplit(Relation r, ...@@ -1321,7 +1242,6 @@ gistSplit(Relation r,
ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1); ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1);
} }
if (gistnospace(left, lvectup, v.spl_nleft)) if (gistnospace(left, lvectup, v.spl_nleft))
{ {
int llen = v.spl_nleft; int llen = v.spl_nleft;
...@@ -1330,35 +1250,24 @@ gistSplit(Relation r, ...@@ -1330,35 +1250,24 @@ gistSplit(Relation r,
lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate); lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate);
ReleaseBuffer(leftbuf); ReleaseBuffer(leftbuf);
for (j = 1; j < r->rd_att->natts; j++)
if ((!isAttByVal(giststate, j)) && !v.spl_lisnull[j])
pfree(DatumGetPointer(v.spl_lattr[j]));
newtup = gistjoinvector(newtup, &nlen, lntup, llen); newtup = gistjoinvector(newtup, &nlen, lntup, llen);
pfree(lntup);
} }
else else
{ {
OffsetNumber l; OffsetNumber l;
l = gistwritebuffer(r, left, lvectup, v.spl_nleft, FirstOffsetNumber); l = gistwritebuffer(r, left, lvectup, v.spl_nleft, FirstOffsetNumber);
if (BufferGetBlockNumber(buffer) != GISTP_ROOT) if (BufferGetBlockNumber(buffer) != GIST_ROOT_BLKNO)
PageRestoreTempPage(left, p); PageRestoreTempPage(left, p);
WriteBuffer(leftbuf); WriteBuffer(leftbuf);
nlen += 1; nlen += 1;
newtup = (IndexTuple *) repalloc((void *) newtup, sizeof(IndexTuple) * nlen); newtup = (IndexTuple *) repalloc(newtup, sizeof(IndexTuple) * nlen);
newtup[nlen - 1] = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lattrsize, v.spl_lisnull); newtup[nlen - 1] = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lattrsize, v.spl_lisnull);
ItemPointerSet(&(newtup[nlen - 1]->t_tid), lbknum, 1); ItemPointerSet(&(newtup[nlen - 1]->t_tid), lbknum, 1);
} }
/* !!! pfree */
pfree(rvectup);
pfree(lvectup);
pfree(v.spl_left);
pfree(v.spl_right);
*len = nlen; *len = nlen;
return newtup; return newtup;
} }
...@@ -1369,7 +1278,7 @@ gistnewroot(Relation r, IndexTuple *itup, int len) ...@@ -1369,7 +1278,7 @@ gistnewroot(Relation r, IndexTuple *itup, int len)
Buffer b; Buffer b;
Page p; Page p;
b = ReadBuffer(r, GISTP_ROOT); b = ReadBuffer(r, GIST_ROOT_BLKNO);
GISTInitBuffer(b, 0); GISTInitBuffer(b, 0);
p = BufferGetPage(b); p = BufferGetPage(b);
...@@ -1385,16 +1294,13 @@ GISTInitBuffer(Buffer b, uint32 f) ...@@ -1385,16 +1294,13 @@ GISTInitBuffer(Buffer b, uint32 f)
Size pageSize; Size pageSize;
pageSize = BufferGetPageSize(b); pageSize = BufferGetPageSize(b);
page = BufferGetPage(b); page = BufferGetPage(b);
PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
opaque = (GISTPageOpaque) PageGetSpecialPointer(page); opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
opaque->flags = f; opaque->flags = f;
} }
/* /*
* find entry with lowest penalty * find entry with lowest penalty
*/ */
...@@ -1404,17 +1310,12 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ ...@@ -1404,17 +1310,12 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
{ {
OffsetNumber maxoff; OffsetNumber maxoff;
OffsetNumber i; OffsetNumber i;
Datum datum;
float usize;
OffsetNumber which; OffsetNumber which;
float sum_grow, float sum_grow,
which_grow[INDEX_MAX_KEYS]; which_grow[INDEX_MAX_KEYS];
GISTENTRY entry, GISTENTRY entry,
identry[INDEX_MAX_KEYS]; identry[INDEX_MAX_KEYS];
bool IsNull, bool isnull[INDEX_MAX_KEYS];
decompvec[INDEX_MAX_KEYS],
isnull[INDEX_MAX_KEYS];
int j;
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
*which_grow = -1.0; *which_grow = -1.0;
...@@ -1422,21 +1323,26 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ ...@@ -1422,21 +1323,26 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
sum_grow = 1; sum_grow = 1;
gistDeCompressAtt(giststate, r, gistDeCompressAtt(giststate, r,
it, NULL, (OffsetNumber) 0, it, NULL, (OffsetNumber) 0,
identry, decompvec, isnull); identry, isnull);
for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i)) for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i))
{ {
int j;
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
sum_grow = 0; sum_grow = 0;
for (j = 0; j < r->rd_att->natts; j++) for (j = 0; j < r->rd_att->natts; j++)
{ {
datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull); Datum datum;
gistdentryinit(giststate, j, &entry, datum, r, p, i, ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull); float usize;
gistpenalty(giststate, j, &entry, IsNull, &identry[j], isnull[j], &usize); bool IsNull;
if ((!isAttByVal(giststate, j)) && entry.key != datum) datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
pfree(DatumGetPointer(entry.key)); gistdentryinit(giststate, j, &entry, datum, r, p, i,
ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull),
FALSE, IsNull);
gistpenalty(giststate, j, &entry, IsNull,
&identry[j], isnull[j], &usize);
if (which_grow[j] < 0 || usize < which_grow[j]) if (which_grow[j] < 0 || usize < which_grow[j])
{ {
...@@ -1456,24 +1362,9 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ ...@@ -1456,24 +1362,9 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
} }
} }
gistFreeAtt(r, identry, decompvec);
return which; return which;
} }
void
gistfreestack(GISTSTACK *s)
{
GISTSTACK *p;
while (s != NULL)
{
p = s->gs_parent;
pfree(s);
s = p;
}
}
/* /*
* Retail deletion of a single tuple. * Retail deletion of a single tuple.
* *
...@@ -1593,7 +1484,6 @@ gistbulkdelete(PG_FUNCTION_ARGS) ...@@ -1593,7 +1484,6 @@ gistbulkdelete(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result); PG_RETURN_POINTER(result);
} }
void void
initGISTstate(GISTSTATE *giststate, Relation index) initGISTstate(GISTSTATE *giststate, Relation index)
{ {
...@@ -1608,22 +1498,22 @@ initGISTstate(GISTSTATE *giststate, Relation index) ...@@ -1608,22 +1498,22 @@ initGISTstate(GISTSTATE *giststate, Relation index)
for (i = 0; i < index->rd_att->natts; i++) for (i = 0; i < index->rd_att->natts; i++)
{ {
fmgr_info_copy(&(giststate->consistentFn[i]), fmgr_info_copy(&(giststate->consistentFn[i]),
index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC), index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->unionFn[i]), fmgr_info_copy(&(giststate->unionFn[i]),
index_getprocinfo(index, i + 1, GIST_UNION_PROC), index_getprocinfo(index, i + 1, GIST_UNION_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->compressFn[i]), fmgr_info_copy(&(giststate->compressFn[i]),
index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC), index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->decompressFn[i]), fmgr_info_copy(&(giststate->decompressFn[i]),
index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC), index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->penaltyFn[i]), fmgr_info_copy(&(giststate->penaltyFn[i]),
index_getprocinfo(index, i + 1, GIST_PENALTY_PROC), index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->picksplitFn[i]), fmgr_info_copy(&(giststate->picksplitFn[i]),
index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC), index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_copy(&(giststate->equalFn[i]), fmgr_info_copy(&(giststate->equalFn[i]),
index_getprocinfo(index, i + 1, GIST_EQUAL_PROC), index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
...@@ -1703,11 +1593,8 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, ...@@ -1703,11 +1593,8 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
PointerGetDatum(e))); PointerGetDatum(e)));
/* decompressFn may just return the given pointer */ /* decompressFn may just return the given pointer */
if (dep != e) if (dep != e)
{
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset, gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
dep->bytes, dep->leafkey); dep->bytes, dep->leafkey);
pfree(dep);
}
} }
else else
gistentryinit(*e, (Datum) 0, r, pg, o, 0, l); gistentryinit(*e, (Datum) 0, r, pg, o, 0, l);
...@@ -1732,11 +1619,8 @@ gistcentryinit(GISTSTATE *giststate, int nkey, ...@@ -1732,11 +1619,8 @@ gistcentryinit(GISTSTATE *giststate, int nkey,
PointerGetDatum(e))); PointerGetDatum(e)));
/* compressFn may just return the given pointer */ /* compressFn may just return the given pointer */
if (cep != e) if (cep != e)
{
gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset, gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset,
cep->bytes, cep->leafkey); cep->bytes, cep->leafkey);
pfree(cep);
}
} }
else else
gistentryinit(*e, (Datum) 0, r, pg, o, 0, l); gistentryinit(*e, (Datum) 0, r, pg, o, 0, l);
...@@ -1746,79 +1630,42 @@ static IndexTuple ...@@ -1746,79 +1630,42 @@ static IndexTuple
gistFormTuple(GISTSTATE *giststate, Relation r, gistFormTuple(GISTSTATE *giststate, Relation r,
Datum attdata[], int datumsize[], bool isnull[]) Datum attdata[], int datumsize[], bool isnull[])
{ {
IndexTuple tup;
bool whatfree[INDEX_MAX_KEYS];
GISTENTRY centry[INDEX_MAX_KEYS]; GISTENTRY centry[INDEX_MAX_KEYS];
Datum compatt[INDEX_MAX_KEYS]; Datum compatt[INDEX_MAX_KEYS];
int j; int i;
for (j = 0; j < r->rd_att->natts; j++) for (i = 0; i < r->rd_att->natts; i++)
{ {
if (isnull[j]) if (isnull[i])
{ compatt[i] = (Datum) 0;
compatt[j] = (Datum) 0;
whatfree[j] = FALSE;
}
else else
{ {
gistcentryinit(giststate, j, &centry[j], attdata[j], gistcentryinit(giststate, i, &centry[i], attdata[i],
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
datumsize[j], FALSE, FALSE); datumsize[i], FALSE, FALSE);
compatt[j] = centry[j].key; compatt[i] = centry[i].key;
if (!isAttByVal(giststate, j))
{
whatfree[j] = TRUE;
if (centry[j].key != attdata[j])
pfree(DatumGetPointer(attdata[j]));
}
else
whatfree[j] = FALSE;
} }
} }
tup = index_form_tuple(giststate->tupdesc, compatt, isnull); return index_form_tuple(giststate->tupdesc, compatt, isnull);
for (j = 0; j < r->rd_att->natts; j++)
if (whatfree[j])
pfree(DatumGetPointer(compatt[j]));
return tup;
} }
static void static void
gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
OffsetNumber o, GISTENTRY *attdata, bool *decompvec, bool *isnull) OffsetNumber o, GISTENTRY *attdata, bool *isnull)
{ {
int i; int i;
Datum datum;
for (i = 0; i < r->rd_att->natts; i++) for (i = 0; i < r->rd_att->natts; i++)
{ {
datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
gistdentryinit(giststate, i, &attdata[i], gistdentryinit(giststate, i, &attdata[i],
datum, r, p, o, datum, r, p, o,
ATTSIZE(datum, giststate->tupdesc, i + 1, isnull[i]), FALSE, isnull[i]); ATTSIZE(datum, giststate->tupdesc, i + 1, isnull[i]),
if (isAttByVal(giststate, i)) FALSE, isnull[i]);
decompvec[i] = FALSE;
else
{
if (attdata[i].key == datum || isnull[i])
decompvec[i] = FALSE;
else
decompvec[i] = TRUE;
}
} }
} }
static void
gistFreeAtt(Relation r, GISTENTRY *attdata, bool *decompvec)
{
int i;
for (i = 0; i < r->rd_att->natts; i++)
if (decompvec[i])
pfree(DatumGetPointer(attdata[i].key));
}
static void static void
gistpenalty(GISTSTATE *giststate, int attno, gistpenalty(GISTSTATE *giststate, int attno,
GISTENTRY *key1, bool isNull1, GISTENTRY *key1, bool isNull1,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 @@ ...@@ -16,41 +16,71 @@
#include "access/gist.h" #include "access/gist.h"
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "utils/memutils.h"
static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir);
ScanDirection dir); static bool gistnext(IndexScanDesc scan, ScanDirection dir);
static bool gistscancache(IndexScanDesc s, ScanDirection dir); static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
static bool gistfirst(IndexScanDesc s, ScanDirection dir); OffsetNumber offset);
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);
/*
* gistgettuple() -- Get the next tuple in the scan
*/
Datum Datum
gistgettuple(PG_FUNCTION_ARGS) gistgettuple(PG_FUNCTION_ARGS)
{ {
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1); ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
bool res; Page page;
OffsetNumber offnum;
GISTScanOpaque so;
/* if we have it cached in the scan desc, just return the value */ so = (GISTScanOpaque) scan->opaque;
if (gistscancache(s, dir))
PG_RETURN_BOOL(true);
/* not cached, so we'll have to do some work */ /*
if (ItemPointerIsValid(&(s->currentItemData))) * If we have produced an index tuple in the past and the executor
res = gistnext(s, dir); * has informed us we need to mark it as "killed", do so now.
else *
res = gistfirst(s, dir); * XXX: right now there is no concurrent access. In the
PG_RETURN_BOOL(res); * 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 Datum
gistgetmulti(PG_FUNCTION_ARGS) 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); ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
int32 max_tids = PG_GETARG_INT32(2); int32 max_tids = PG_GETARG_INT32(2);
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3); int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
...@@ -60,13 +90,10 @@ gistgetmulti(PG_FUNCTION_ARGS) ...@@ -60,13 +90,10 @@ gistgetmulti(PG_FUNCTION_ARGS)
/* XXX generic implementation: loop around guts of gistgettuple */ /* XXX generic implementation: loop around guts of gistgettuple */
while (ntids < max_tids) while (ntids < max_tids)
{ {
if (ItemPointerIsValid(&(s->currentItemData))) res = gistnext(scan, ForwardScanDirection);
res = gistnext(s, ForwardScanDirection);
else
res = gistfirst(s, ForwardScanDirection);
if (!res) if (!res)
break; break;
tids[ntids] = s->xs_ctup.t_self; tids[ntids] = scan->xs_ctup.t_self;
ntids++; ntids++;
} }
...@@ -74,166 +101,123 @@ gistgetmulti(PG_FUNCTION_ARGS) ...@@ -74,166 +101,123 @@ gistgetmulti(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res); 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 static bool
gistfirst(IndexScanDesc s, ScanDirection dir) gistnext(IndexScanDesc scan, ScanDirection dir)
{ {
Buffer b;
Page p; Page p;
OffsetNumber n; OffsetNumber n;
OffsetNumber maxoff;
GISTPageOpaque po; GISTPageOpaque po;
GISTScanOpaque so; GISTScanOpaque so;
GISTSTACK *stk; GISTSTACK *stk;
BlockNumber blk;
IndexTuple it; 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(so->curbuf);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
for (;;) if (ItemPointerIsValid(&scan->currentItemData) == false)
{ {
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir)) if (ScanDirectionIsBackward(dir))
n = gistfindnext(s, p, maxoff, dir); n = PageGetMaxOffsetNumber(p);
else else
n = gistfindnext(s, p, FirstOffsetNumber, dir); n = FirstOffsetNumber;
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);
}
} }
}
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 else
n = OffsetNumberPrev(n); {
n = ItemPointerGetOffsetNumber(&(scan->currentItemData));
b = ReadBuffer(s->indexRelation, blk); if (ScanDirectionIsBackward(dir))
p = BufferGetPage(b); n = OffsetNumberPrev(n);
po = (GISTPageOpaque) PageGetSpecialPointer(p); else
n = OffsetNumberNext(n);
}
for (;;) for (;;)
{ {
maxoff = PageGetMaxOffsetNumber(p); n = gistfindnext(scan, n, dir);
n = gistfindnext(s, p, 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; return false;
} }
b = ReleaseAndReadBuffer(b, s->indexRelation, stk->gs_blk); stk = so->stack;
p = BufferGetPage(b); so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
stk->block);
p = BufferGetPage(so->curbuf);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir)) if (ScanDirectionIsBackward(dir))
n = OffsetNumberPrev(stk->gs_child); n = OffsetNumberPrev(stk->offset);
else else
n = OffsetNumberNext(stk->gs_child); n = OffsetNumberNext(stk->offset);
so->s_stack = stk->gs_parent; so->stack = stk->parent;
pfree(stk); pfree(stk);
n = gistfindnext(s, p, n, dir); continue;
} }
if (po->flags & F_LEAF) 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)); it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
scan->xs_ctup.t_self = it->t_tid;
s->xs_ctup.t_self = it->t_tid;
ReleaseBuffer(b);
return true; return true;
} }
else 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 = (GISTSTACK *) palloc(sizeof(GISTSTACK));
stk->gs_child = n; stk->offset = n;
stk->gs_blk = BufferGetBlockNumber(b); stk->block = BufferGetBlockNumber(so->curbuf);
stk->gs_parent = so->s_stack; stk->parent = so->stack;
so->s_stack = stk; so->stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid)); child_block = ItemPointerGetBlockNumber(&(it->t_tid));
b = ReleaseAndReadBuffer(b, s->indexRelation, blk); so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
p = BufferGetPage(b); child_block);
p = BufferGetPage(so->curbuf);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir)) if (ScanDirectionIsBackward(dir))
...@@ -244,19 +228,34 @@ gistnext(IndexScanDesc s, ScanDirection 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 static bool
gistindex_keytest(IndexTuple tuple, gistindex_keytest(IndexTuple tuple,
int scanKeySize, IndexScanDesc scan,
ScanKey key,
GISTSTATE *giststate,
Relation r,
Page p,
OffsetNumber offset) 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(); IncrIndexProcessed();
while (scanKeySize > 0) while (keySize > 0)
{ {
Datum datum; Datum datum;
bool isNull; bool isNull;
...@@ -297,53 +296,57 @@ gistindex_keytest(IndexTuple tuple, ...@@ -297,53 +296,57 @@ gistindex_keytest(IndexTuple tuple,
Int32GetDatum(key->sk_strategy), Int32GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype)); 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)) if (!DatumGetBool(test))
return false; return false;
scanKeySize--; keySize--;
key++; key++;
} }
return true; 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 static OffsetNumber
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) gistfindnext(IndexScanDesc scan, OffsetNumber n, ScanDirection dir)
{ {
OffsetNumber maxoff; OffsetNumber maxoff;
IndexTuple it; IndexTuple it;
GISTPageOpaque po; GISTPageOpaque po;
GISTScanOpaque so; GISTScanOpaque so;
GISTSTATE *giststate; MemoryContext oldcxt;
Page p;
so = (GISTScanOpaque) scan->opaque;
p = BufferGetPage(so->curbuf);
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(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 * 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. * a ghost tuple, before the scan. If this is the case, back up one.
*/ */
if (so->flags & GS_CURBEFORE)
if (so->s_flags & GS_CURBEFORE)
{ {
so->s_flags &= ~GS_CURBEFORE; so->flags &= ~GS_CURBEFORE;
n = OffsetNumberPrev(n); n = OffsetNumberPrev(n);
} }
while (n >= FirstOffsetNumber && n <= maxoff) while (n >= FirstOffsetNumber && n <= maxoff)
{ {
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
if (gistindex_keytest(it, if (gistindex_keytest(it, scan, n))
s->numberOfKeys, s->keyData, giststate,
s->indexRelation, p, n))
break; break;
if (ScanDirectionIsBackward(dir)) if (ScanDirectionIsBackward(dir))
...@@ -352,28 +355,16 @@ gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) ...@@ -352,28 +355,16 @@ gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
n = OffsetNumberNext(n); n = OffsetNumberNext(n);
} }
return n; MemoryContextSwitchTo(oldcxt);
} MemoryContextReset(so->tempCxt);
static bool
gistscancache(IndexScanDesc s, ScanDirection dir)
{
Buffer b;
Page p;
OffsetNumber n;
IndexTuple it;
if (!(ScanDirectionIsNoMovement(dir) /*
&& ItemPointerIsValid(&(s->currentItemData)))) * If we found a matching entry, return its offset; otherwise
return false; * return InvalidOffsetNumber to inform the caller to go to the
* next page.
b = ReadBuffer(s->indexRelation, */
ItemPointerGetBlockNumber(&(s->currentItemData))); if (n >= FirstOffsetNumber && n <= maxoff)
p = BufferGetPage(b); return n;
n = ItemPointerGetOffsetNumber(&(s->currentItemData)); else
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); return InvalidOffsetNumber;
s->xs_ctup.t_self = it->t_tid;
ReleaseBuffer(b);
return true;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.56 2004/12/31 21:59:10 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.57 2005/05/17 00:59:30 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,28 +17,29 @@ ...@@ -17,28 +17,29 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/gist.h" #include "access/gist.h"
#include "access/gistscan.h" #include "access/gistscan.h"
#include "utils/memutils.h"
#include "utils/resowner.h" #include "utils/resowner.h"
/* routines defined and used here */ /* routines defined and used here */
static void gistregscan(IndexScanDesc s); static void gistregscan(IndexScanDesc scan);
static void gistdropscan(IndexScanDesc s); static void gistdropscan(IndexScanDesc scan);
static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno, static void gistadjone(IndexScanDesc scan, int op, BlockNumber blkno,
OffsetNumber offnum); OffsetNumber offnum);
static void adjuststack(GISTSTACK *stk, BlockNumber blkno); static void adjuststack(GISTSTACK *stk, BlockNumber blkno);
static void adjustiptr(IndexScanDesc s, ItemPointer iptr, static void adjustiptr(IndexScanDesc scan, ItemPointer iptr,
int op, BlockNumber blkno, OffsetNumber offnum); int op, BlockNumber blkno, OffsetNumber offnum);
static void gistfreestack(GISTSTACK *s);
/* /*
* Whenever we start a GiST scan in a backend, we register it in private * Whenever we start a GiST scan in a backend, we register it in
* space. Then if the GiST index gets updated, we check all registered * private space. Then if the GiST index gets updated, we check all
* scans and adjust them if the tuple they point at got moved by the * registered scans and adjust them if the tuple they point at got
* update. We only need to do this in private space, because when we update * moved by the update. We only need to do this in private space,
* an GiST we have a write lock on the tree, so no other process can have * because when we update an GiST we have a write lock on the tree, so
* any locks at all on it. A single transaction can have write and read * no other process can have any locks at all on it. A single
* locks on the same object, so that's why we need to handle this case. * transaction can have write and read locks on the same object, so
* that's why we need to handle this case.
*/ */
typedef struct GISTScanListData typedef struct GISTScanListData
{ {
IndexScanDesc gsl_scan; IndexScanDesc gsl_scan;
...@@ -57,65 +58,77 @@ gistbeginscan(PG_FUNCTION_ARGS) ...@@ -57,65 +58,77 @@ gistbeginscan(PG_FUNCTION_ARGS)
Relation r = (Relation) PG_GETARG_POINTER(0); Relation r = (Relation) PG_GETARG_POINTER(0);
int nkeys = PG_GETARG_INT32(1); int nkeys = PG_GETARG_INT32(1);
ScanKey key = (ScanKey) PG_GETARG_POINTER(2); ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
IndexScanDesc s; IndexScanDesc scan;
s = RelationGetIndexScan(r, nkeys, key); scan = RelationGetIndexScan(r, nkeys, key);
gistregscan(scan);
gistregscan(s); PG_RETURN_POINTER(scan);
PG_RETURN_POINTER(s);
} }
Datum Datum
gistrescan(PG_FUNCTION_ARGS) gistrescan(PG_FUNCTION_ARGS)
{ {
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ScanKey key = (ScanKey) PG_GETARG_POINTER(1); ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
GISTScanOpaque p; GISTScanOpaque so;
int i; int i;
/* /*
* Clear all the pointers. * Clear all the pointers.
*/ */
ItemPointerSetInvalid(&s->currentItemData); ItemPointerSetInvalid(&scan->currentItemData);
ItemPointerSetInvalid(&s->currentMarkData); ItemPointerSetInvalid(&scan->currentMarkData);
p = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
if (p != NULL) if (so != NULL)
{ {
/* rescan an existing indexscan --- reset state */ /* rescan an existing indexscan --- reset state */
gistfreestack(p->s_stack); gistfreestack(so->stack);
gistfreestack(p->s_markstk); gistfreestack(so->markstk);
p->s_stack = p->s_markstk = NULL; so->stack = so->markstk = NULL;
p->s_flags = 0x0; so->flags = 0x0;
/* drop pins on buffers -- no locks held */
if (BufferIsValid(so->curbuf))
{
ReleaseBuffer(so->curbuf);
so->curbuf = InvalidBuffer;
}
if (BufferIsValid(so->markbuf))
{
ReleaseBuffer(so->markbuf);
so->markbuf = InvalidBuffer;
}
} }
else else
{ {
/* initialize opaque data */ /* initialize opaque data */
p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData)); so = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
p->s_stack = p->s_markstk = NULL; so->stack = so->markstk = NULL;
p->s_flags = 0x0; so->flags = 0x0;
s->opaque = p; so->tempCxt = createTempGistContext();
p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE)); so->curbuf = so->markbuf = InvalidBuffer;
initGISTstate(p->giststate, s->indexRelation); so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
initGISTstate(so->giststate, scan->indexRelation);
scan->opaque = so;
} }
/* Update scan key, if a new one is given */ /* Update scan key, if a new one is given */
if (key && s->numberOfKeys > 0) if (key && scan->numberOfKeys > 0)
{ {
memmove(s->keyData, memmove(scan->keyData, key,
key, scan->numberOfKeys * sizeof(ScanKeyData));
s->numberOfKeys * sizeof(ScanKeyData));
/* /*
* Modify the scan key so that the Consistent function is called * Modify the scan key so that all the Consistent method is
* for all comparisons. The original operator is passed to the * called for all comparisons. The original operator is passed
* Consistent function in the form of its strategy number, which * to the Consistent function in the form of its strategy
* is available from the sk_strategy field, and its subtype from * number, which is available from the sk_strategy field, and
* the sk_subtype field. * its subtype from the sk_subtype field.
*/ */
for (i = 0; i < s->numberOfKeys; i++) for (i = 0; i < scan->numberOfKeys; i++)
s->keyData[i].sk_func = p->giststate->consistentFn[s->keyData[i].sk_attno - 1]; scan->keyData[i].sk_func = so->giststate->consistentFn[scan->keyData[i].sk_attno - 1];
} }
PG_RETURN_VOID(); PG_RETURN_VOID();
...@@ -124,35 +137,47 @@ gistrescan(PG_FUNCTION_ARGS) ...@@ -124,35 +137,47 @@ gistrescan(PG_FUNCTION_ARGS)
Datum Datum
gistmarkpos(PG_FUNCTION_ARGS) gistmarkpos(PG_FUNCTION_ARGS)
{ {
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque p; GISTScanOpaque so;
GISTSTACK *o, GISTSTACK *o,
*n, *n,
*tmp; *tmp;
s->currentMarkData = s->currentItemData; scan->currentMarkData = scan->currentItemData;
p = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
if (p->s_flags & GS_CURBEFORE) if (so->flags & GS_CURBEFORE)
p->s_flags |= GS_MRKBEFORE; so->flags |= GS_MRKBEFORE;
else else
p->s_flags &= ~GS_MRKBEFORE; so->flags &= ~GS_MRKBEFORE;
o = NULL; o = NULL;
n = p->s_stack; n = so->stack;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != NULL) while (n != NULL)
{ {
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child; tmp->offset = n->offset;
tmp->gs_blk = n->gs_blk; tmp->block = n->block;
tmp->gs_parent = o; tmp->parent = o;
o = tmp; o = tmp;
n = n->gs_parent; n = n->parent;
} }
gistfreestack(p->s_markstk); gistfreestack(so->markstk);
p->s_markstk = o; so->markstk = o;
/* Update markbuf: make sure to bump ref count on curbuf */
if (BufferIsValid(so->markbuf))
{
ReleaseBuffer(so->markbuf);
so->markbuf = InvalidBuffer;
}
if (BufferIsValid(so->curbuf))
{
IncrBufferRefCount(so->curbuf);
so->markbuf = so->curbuf;
}
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -160,35 +185,47 @@ gistmarkpos(PG_FUNCTION_ARGS) ...@@ -160,35 +185,47 @@ gistmarkpos(PG_FUNCTION_ARGS)
Datum Datum
gistrestrpos(PG_FUNCTION_ARGS) gistrestrpos(PG_FUNCTION_ARGS)
{ {
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque p; GISTScanOpaque so;
GISTSTACK *o, GISTSTACK *o,
*n, *n,
*tmp; *tmp;
s->currentItemData = s->currentMarkData; scan->currentItemData = scan->currentMarkData;
p = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
if (p->s_flags & GS_MRKBEFORE) if (so->flags & GS_MRKBEFORE)
p->s_flags |= GS_CURBEFORE; so->flags |= GS_CURBEFORE;
else else
p->s_flags &= ~GS_CURBEFORE; so->flags &= ~GS_CURBEFORE;
o = NULL; o = NULL;
n = p->s_markstk; n = so->markstk;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != NULL) while (n != NULL)
{ {
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child; tmp->offset = n->offset;
tmp->gs_blk = n->gs_blk; tmp->block = n->block;
tmp->gs_parent = o; tmp->parent = o;
o = tmp; o = tmp;
n = n->gs_parent; n = n->parent;
} }
gistfreestack(p->s_stack); gistfreestack(so->stack);
p->s_stack = o; so->stack = o;
/* Update curbuf: be sure to bump ref count on markbuf */
if (BufferIsValid(so->curbuf))
{
ReleaseBuffer(so->curbuf);
so->curbuf = InvalidBuffer;
}
if (BufferIsValid(so->markbuf))
{
IncrBufferRefCount(so->markbuf);
so->curbuf = so->markbuf;
}
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -196,52 +233,57 @@ gistrestrpos(PG_FUNCTION_ARGS) ...@@ -196,52 +233,57 @@ gistrestrpos(PG_FUNCTION_ARGS)
Datum Datum
gistendscan(PG_FUNCTION_ARGS) gistendscan(PG_FUNCTION_ARGS)
{ {
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque p; GISTScanOpaque so;
p = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
if (p != NULL) if (so != NULL)
{ {
gistfreestack(p->s_stack); gistfreestack(so->stack);
gistfreestack(p->s_markstk); gistfreestack(so->markstk);
if (p->giststate != NULL) if (so->giststate != NULL)
freeGISTstate(p->giststate); freeGISTstate(so->giststate);
pfree(s->opaque); /* drop pins on buffers -- we aren't holding any locks */
if (BufferIsValid(so->curbuf))
ReleaseBuffer(so->curbuf);
if (BufferIsValid(so->markbuf))
ReleaseBuffer(so->markbuf);
MemoryContextDelete(so->tempCxt);
pfree(scan->opaque);
} }
gistdropscan(s); gistdropscan(scan);
/* XXX don't unset read lock -- two-phase locking */
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
static void static void
gistregscan(IndexScanDesc s) gistregscan(IndexScanDesc scan)
{ {
GISTScanList l; GISTScanList l;
l = (GISTScanList) palloc(sizeof(GISTScanListData)); l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s; l->gsl_scan = scan;
l->gsl_owner = CurrentResourceOwner; l->gsl_owner = CurrentResourceOwner;
l->gsl_next = GISTScans; l->gsl_next = GISTScans;
GISTScans = l; GISTScans = l;
} }
static void static void
gistdropscan(IndexScanDesc s) gistdropscan(IndexScanDesc scan)
{ {
GISTScanList l; GISTScanList l;
GISTScanList prev; GISTScanList prev;
prev = NULL; prev = NULL;
for (l = GISTScans; l != NULL && l->gsl_scan != s; l = l->gsl_next) for (l = GISTScans; l != NULL && l->gsl_scan != scan; l = l->gsl_next)
prev = l; prev = l;
if (l == NULL) if (l == NULL)
elog(ERROR, "GiST scan list corrupted -- could not find 0x%p", elog(ERROR, "GiST scan list corrupted -- could not find 0x%p",
(void *) s); (void *) scan);
if (prev == NULL) if (prev == NULL)
GISTScans = l->gsl_next; GISTScans = l->gsl_next;
...@@ -313,22 +355,22 @@ gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum) ...@@ -313,22 +355,22 @@ gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum)
* update. If so, we make the change here. * update. If so, we make the change here.
*/ */
static void static void
gistadjone(IndexScanDesc s, gistadjone(IndexScanDesc scan,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
GISTScanOpaque so; GISTScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum); adjustiptr(scan, &(scan->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); adjustiptr(scan, &(scan->currentMarkData), op, blkno, offnum);
so = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
if (op == GISTOP_SPLIT) if (op == GISTOP_SPLIT)
{ {
adjuststack(so->s_stack, blkno); adjuststack(so->stack, blkno);
adjuststack(so->s_markstk, blkno); adjuststack(so->markstk, blkno);
} }
} }
...@@ -340,7 +382,7 @@ gistadjone(IndexScanDesc s, ...@@ -340,7 +382,7 @@ gistadjone(IndexScanDesc s,
* the same page. * the same page.
*/ */
static void static void
adjustiptr(IndexScanDesc s, adjustiptr(IndexScanDesc scan,
ItemPointer iptr, ItemPointer iptr,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
...@@ -354,7 +396,7 @@ adjustiptr(IndexScanDesc s, ...@@ -354,7 +396,7 @@ adjustiptr(IndexScanDesc s,
if (ItemPointerGetBlockNumber(iptr) == blkno) if (ItemPointerGetBlockNumber(iptr) == blkno)
{ {
curoff = ItemPointerGetOffsetNumber(iptr); curoff = ItemPointerGetOffsetNumber(iptr);
so = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) scan->opaque;
switch (op) switch (op)
{ {
...@@ -362,7 +404,6 @@ adjustiptr(IndexScanDesc s, ...@@ -362,7 +404,6 @@ adjustiptr(IndexScanDesc s,
/* back up one if we need to */ /* back up one if we need to */
if (curoff >= offnum) if (curoff >= offnum)
{ {
if (curoff > FirstOffsetNumber) if (curoff > FirstOffsetNumber)
{ {
/* just adjust the item pointer */ /* just adjust the item pointer */
...@@ -375,10 +416,10 @@ adjustiptr(IndexScanDesc s, ...@@ -375,10 +416,10 @@ adjustiptr(IndexScanDesc s,
* tuple * tuple
*/ */
ItemPointerSet(iptr, blkno, FirstOffsetNumber); ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData)) if (iptr == &(scan->currentItemData))
so->s_flags |= GS_CURBEFORE; so->flags |= GS_CURBEFORE;
else else
so->s_flags |= GS_MRKBEFORE; so->flags |= GS_MRKBEFORE;
} }
} }
break; break;
...@@ -386,10 +427,10 @@ adjustiptr(IndexScanDesc s, ...@@ -386,10 +427,10 @@ adjustiptr(IndexScanDesc s,
case GISTOP_SPLIT: case GISTOP_SPLIT:
/* back to start of page on split */ /* back to start of page on split */
ItemPointerSet(iptr, blkno, FirstOffsetNumber); ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData)) if (iptr == &(scan->currentItemData))
so->s_flags &= ~GS_CURBEFORE; so->flags &= ~GS_CURBEFORE;
else else
so->s_flags &= ~GS_MRKBEFORE; so->flags &= ~GS_MRKBEFORE;
break; break;
default: default:
...@@ -417,9 +458,20 @@ adjuststack(GISTSTACK *stk, BlockNumber blkno) ...@@ -417,9 +458,20 @@ adjuststack(GISTSTACK *stk, BlockNumber blkno)
{ {
while (stk != NULL) while (stk != NULL)
{ {
if (stk->gs_blk == blkno) if (stk->block == blkno)
stk->gs_child = FirstOffsetNumber; stk->offset = FirstOffsetNumber;
stk = stk->parent;
}
}
stk = stk->gs_parent; static void
gistfreestack(GISTSTACK *s)
{
while (s != NULL)
{
GISTSTACK *p = s->parent;
pfree(s);
s = p;
} }
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/gist.h,v 1.44 2005/03/27 23:53:04 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/gist.h,v 1.45 2005/05/17 00:59:30 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -54,13 +54,21 @@ typedef GISTPageOpaqueData *GISTPageOpaque; ...@@ -54,13 +54,21 @@ typedef GISTPageOpaqueData *GISTPageOpaque;
#define GIST_LEAF(entry) (((GISTPageOpaque) PageGetSpecialPointer((entry)->page))->flags & F_LEAF) #define GIST_LEAF(entry) (((GISTPageOpaque) PageGetSpecialPointer((entry)->page))->flags & F_LEAF)
/* /*
* When we descend a tree, we keep a stack of parent pointers. * When we descend a tree, we keep a stack of parent pointers. This
* allows us to follow a chain of internal node points until we reach
* a leaf node, and then back up the stack to re-examine the internal
* nodes.
*
* 'parent' is the previous stack entry -- i.e. the node we arrived
* from. 'block' is the node's block number. 'offset' is the offset in
* the node's page that we stopped at (i.e. we followed the child
* pointer located at the specified offset).
*/ */
typedef struct GISTSTACK typedef struct GISTSTACK
{ {
struct GISTSTACK *gs_parent; struct GISTSTACK *parent;
OffsetNumber gs_child; OffsetNumber offset;
BlockNumber gs_blk; BlockNumber block;
} GISTSTACK; } GISTSTACK;
typedef struct GISTSTATE typedef struct GISTSTATE
...@@ -84,10 +92,13 @@ typedef struct GISTSTATE ...@@ -84,10 +92,13 @@ typedef struct GISTSTATE
*/ */
typedef struct GISTScanOpaqueData typedef struct GISTScanOpaqueData
{ {
struct GISTSTACK *s_stack; GISTSTACK *stack;
struct GISTSTACK *s_markstk; GISTSTACK *markstk;
uint16 s_flags; uint16 flags;
struct GISTSTATE *giststate; GISTSTATE *giststate;
MemoryContext tempCxt;
Buffer curbuf;
Buffer markbuf;
} GISTScanOpaqueData; } GISTScanOpaqueData;
typedef GISTScanOpaqueData *GISTScanOpaque; typedef GISTScanOpaqueData *GISTScanOpaque;
...@@ -101,8 +112,8 @@ typedef GISTScanOpaqueData *GISTScanOpaque; ...@@ -101,8 +112,8 @@ typedef GISTScanOpaqueData *GISTScanOpaque;
#define GS_CURBEFORE ((uint16) (1 << 0)) #define GS_CURBEFORE ((uint16) (1 << 0))
#define GS_MRKBEFORE ((uint16) (1 << 1)) #define GS_MRKBEFORE ((uint16) (1 << 1))
/* root page of a gist */ /* root page of a gist index */
#define GISTP_ROOT 0 #define GIST_ROOT_BLKNO 0
/* /*
* When we update a relation on which we're doing a scan, we need to * When we update a relation on which we're doing a scan, we need to
...@@ -183,7 +194,6 @@ extern Datum gistbuild(PG_FUNCTION_ARGS); ...@@ -183,7 +194,6 @@ extern Datum gistbuild(PG_FUNCTION_ARGS);
extern Datum gistinsert(PG_FUNCTION_ARGS); extern Datum gistinsert(PG_FUNCTION_ARGS);
extern Datum gistbulkdelete(PG_FUNCTION_ARGS); extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
extern void _gistdump(Relation r); extern void _gistdump(Relation r);
extern void gistfreestack(GISTSTACK *s);
extern void initGISTstate(GISTSTATE *giststate, Relation index); extern void initGISTstate(GISTSTATE *giststate, Relation index);
extern void freeGISTstate(GISTSTATE *giststate); extern void freeGISTstate(GISTSTATE *giststate);
extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
...@@ -193,6 +203,7 @@ extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, ...@@ -193,6 +203,7 @@ extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
extern void gist_redo(XLogRecPtr lsn, XLogRecord *record); extern void gist_redo(XLogRecPtr lsn, XLogRecord *record);
extern void gist_undo(XLogRecPtr lsn, XLogRecord *record); extern void gist_undo(XLogRecPtr lsn, XLogRecord *record);
extern void gist_desc(char *buf, uint8 xl_info, char *rec); extern void gist_desc(char *buf, uint8 xl_info, char *rec);
extern MemoryContext createTempGistContext(void);
/* gistget.c */ /* gistget.c */
extern Datum gistgettuple(PG_FUNCTION_ARGS); extern Datum gistgettuple(PG_FUNCTION_ARGS);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment