GiST improvements:
authorNeil Conway <neilc@samurai.com>
Tue, 17 May 2005 00:59:30 +0000 (00:59 +0000)
committerNeil Conway <neilc@samurai.com>
Tue, 17 May 2005 00:59:30 +0000 (00:59 +0000)
- 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

doc/src/sgml/gist.sgml
src/backend/access/gist/gist.c
src/backend/access/gist/gistget.c
src/backend/access/gist/gistscan.c
src/include/access/gist.h

index e5c96d7e5428aa0190c343469d754d9929595fcb..9577a0768a92aee3197a269506d243b3e47d0444 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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">
@@ -202,7 +202,7 @@ $PostgreSQL: pgsql/doc/src/sgml/gist.sgml,v 1.17 2005/04/09 03:52:43 momjian Exp
  <para>
   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>
-  index inconsistent, forcing a REINDEX.
+  index inconsistent, forcing a <command>REINDEX</command>.
  </para>
 
 </sect1>
index 9445d1086f77dc740e041bdfd375b50cf348eb89..66d4347fbbc5d5a8723e3841260512e1da76f6c4 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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 @@
 #include "catalog/index.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
+#include "utils/memutils.h"
 
 
 #undef GIST_PAGEADDITEM
  * and gistadjsubkey only
  */
 #define FILLITEM(evp, isnullkey, okey, okeyb, rkey, rkeyb)  do { \
-       if (isnullkey) {                                          \
-           gistentryinit((evp), rkey, r, NULL,                   \
-                         (OffsetNumber) 0, rkeyb, FALSE);        \
-       } else {                                                  \
-           gistentryinit((evp), okey, r, NULL,                   \
-                         (OffsetNumber) 0, okeyb, FALSE);        \
-       }                                                         \
+   if (isnullkey) {                                              \
+       gistentryinit((evp), rkey, r, NULL,                       \
+                     (OffsetNumber) 0, rkeyb, FALSE);            \
+   } else {                                                      \
+       gistentryinit((evp), okey, r, NULL,                       \
+                     (OffsetNumber) 0, okeyb, FALSE);            \
+   }                                                             \
 } while(0)
 
 #define FILLEV(isnull1, key1, key1b, isnull2, key2, key2b) do { \
@@ -65,6 +66,7 @@ typedef struct
    GISTSTATE   giststate;
    int         numindexattrs;
    double      indtuples;
+   MemoryContext tmpCxt;
 } GISTBuildState;
 
 
@@ -128,9 +130,8 @@ static void gistcentryinit(GISTSTATE *giststate, int nkey,
               Relation r, Page pg,
               OffsetNumber o, int b, bool l, bool isNull);
 static void gistDeCompressAtt(GISTSTATE *giststate, Relation r,
-                 IndexTuple tuple, Page p, OffsetNumber o,
-                 GISTENTRY *attdata, bool *decompvec, bool *isnull);
-static void gistFreeAtt(Relation r, GISTENTRY *attdata, bool *decompvec);
+                             IndexTuple tuple, Page p, OffsetNumber o,
+                             GISTENTRY *attdata, bool *isnull);
 static void gistpenalty(GISTSTATE *giststate, int attno,
            GISTENTRY *key1, bool isNull1,
            GISTENTRY *key2, bool isNull2,
@@ -143,7 +144,28 @@ static void gist_dumptree(Relation r, int level, BlockNumber blk, OffsetNumber c
 #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
 gistbuild(PG_FUNCTION_ARGS)
@@ -155,10 +177,6 @@ gistbuild(PG_FUNCTION_ARGS)
    GISTBuildState buildstate;
    Buffer      buffer;
 
-   /* no locking is needed */
-
-   initGISTstate(&buildstate.giststate, index);
-
    /*
     * We expect to be called exactly once for any index relation. If
     * that's not the case, big trouble's what we have.
@@ -167,6 +185,9 @@ gistbuild(PG_FUNCTION_ARGS)
        elog(ERROR, "index \"%s\" already contains data",
             RelationGetRelationName(index));
 
+   /* no locking is needed */
+   initGISTstate(&buildstate.giststate, index);
+
    /* initialize the root page */
    buffer = ReadBuffer(index, P_NEW);
    GISTInitBuffer(buffer, F_LEAF);
@@ -175,21 +196,27 @@ gistbuild(PG_FUNCTION_ARGS)
    /* build the index */
    buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs;
    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 */
    reltuples = IndexBuildHeapScan(heap, index, indexInfo,
-                               gistbuildCallback, (void *) &buildstate);
+                                  gistbuildCallback, (void *) &buildstate);
 
    /* okay, all heap tuples are indexed */
+   MemoryContextDelete(buildstate.tmpCxt);
 
    /* since we just counted the # of tuples, may as well update stats */
    IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples);
 
    freeGISTstate(&buildstate.giststate);
+
 #ifdef GISTDEBUG
-   gist_dumptree(index, 0, GISTP_ROOT, 0);
+   gist_dumptree(index, 0, GIST_ROOT_BLKNO, 0);
 #endif
-
    PG_RETURN_VOID();
 }
 
@@ -206,32 +233,26 @@ gistbuildCallback(Relation index,
 {
    GISTBuildState *buildstate = (GISTBuildState *) state;
    IndexTuple  itup;
-   bool        compvec[INDEX_MAX_KEYS];
    GISTENTRY   tmpcentry;
    int         i;
+   MemoryContext oldCxt;
 
    /* GiST cannot index tuples with leading NULLs */
    if (isnull[0])
        return;
 
+   oldCxt = MemoryContextSwitchTo(buildstate->tmpCxt);
+
    /* immediately compress keys to normalize */
    for (i = 0; i < buildstate->numindexattrs; i++)
    {
        if (isnull[i])
-       {
            values[i] = (Datum) 0;
-           compvec[i] = FALSE;
-       }
        else
        {
            gistcentryinit(&buildstate->giststate, i, &tmpcentry, values[i],
                           NULL, NULL, (OffsetNumber) 0,
-                        -1 /* size is currently bogus */ , TRUE, FALSE);
-           if (values[i] != tmpcentry.key &&
-               !(isAttByVal(&buildstate->giststate, i)))
-               compvec[i] = TRUE;
-           else
-               compvec[i] = FALSE;
+                          -1 /* size is currently bogus */, TRUE, FALSE);
            values[i] = tmpcentry.key;
        }
    }
@@ -250,12 +271,8 @@ gistbuildCallback(Relation index,
    gistdoinsert(index, itup, &buildstate->giststate);
 
    buildstate->indtuples += 1;
-
-   for (i = 0; i < buildstate->numindexattrs; i++)
-       if (compvec[i])
-           pfree(DatumGetPointer(values[i]));
-
-   pfree(itup);
+   MemoryContextSwitchTo(oldCxt);
+   MemoryContextReset(buildstate->tmpCxt);
 }
 
 /*
@@ -271,7 +288,6 @@ gistinsert(PG_FUNCTION_ARGS)
    Datum      *values = (Datum *) PG_GETARG_POINTER(1);
    bool       *isnull = (bool *) PG_GETARG_POINTER(2);
    ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
 #ifdef NOT_USED
    Relation    heapRel = (Relation) PG_GETARG_POINTER(4);
    bool        checkUnique = PG_GETARG_BOOL(5);
@@ -280,7 +296,8 @@ gistinsert(PG_FUNCTION_ARGS)
    GISTSTATE   giststate;
    GISTENTRY   tmpentry;
    int         i;
-   bool        compvec[INDEX_MAX_KEYS];
+   MemoryContext oldCxt;
+   MemoryContext insertCxt;
 
    /*
     * Since GIST is not marked "amconcurrent" in pg_am, caller should
@@ -292,25 +309,21 @@ gistinsert(PG_FUNCTION_ARGS)
    if (isnull[0])
        PG_RETURN_BOOL(false);
 
+   insertCxt = createTempGistContext();
+   oldCxt = MemoryContextSwitchTo(insertCxt);
+
    initGISTstate(&giststate, r);
 
    /* immediately compress keys to normalize */
    for (i = 0; i < r->rd_att->natts; i++)
    {
        if (isnull[i])
-       {
            values[i] = (Datum) 0;
-           compvec[i] = FALSE;
-       }
        else
        {
            gistcentryinit(&giststate, i, &tmpentry, values[i],
                           NULL, NULL, (OffsetNumber) 0,
-                        -1 /* size is currently bogus */ , TRUE, FALSE);
-           if (values[i] != tmpentry.key && !(isAttByVal(&giststate, i)))
-               compvec[i] = TRUE;
-           else
-               compvec[i] = FALSE;
+                          -1 /* size is currently bogus */, TRUE, FALSE);
            values[i] = tmpentry.key;
        }
    }
@@ -319,11 +332,10 @@ gistinsert(PG_FUNCTION_ARGS)
 
    gistdoinsert(r, itup, &giststate);
 
-   for (i = 0; i < r->rd_att->natts; i++)
-       if (compvec[i] == TRUE)
-           pfree(DatumGetPointer(values[i]));
-   pfree(itup);
+   /* cleanup */
    freeGISTstate(&giststate);
+   MemoryContextSwitchTo(oldCxt);
+   MemoryContextDelete(insertCxt);
 
    PG_RETURN_BOOL(true);
 }
@@ -370,36 +382,29 @@ gistPageAddItem(GISTSTATE *giststate,
    if (retval == InvalidOffsetNumber)
        elog(ERROR, "failed to add index item to \"%s\"",
             RelationGetRelationName(r));
-   /* be tidy */
-   if (DatumGetPointer(tmpcentry.key) != NULL &&
-       tmpcentry.key != dentry->key &&
-       tmpcentry.key != datum)
-       pfree(DatumGetPointer(tmpcentry.key));
-   return (retval);
+   return retval;
 }
 #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
-gistdoinsert(Relation r,
-            IndexTuple itup,
-            GISTSTATE *giststate)
+gistdoinsert(Relation r, IndexTuple itup, GISTSTATE *giststate)
 {
    IndexTuple *instup;
-   int         i,
-               ret,
+   int         ret,
                len = 1;
 
    instup = (IndexTuple *) palloc(sizeof(IndexTuple));
    instup[0] = (IndexTuple) palloc(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)
        gistnewroot(r, instup, len);
-
-   for (i = 0; i < len; i++)
-       pfree(instup[i]);
-   pfree(instup);
 }
 
 static int
@@ -410,7 +415,6 @@ gistlayerinsert(Relation r, BlockNumber blkno,
 {
    Buffer      buffer;
    Page        page;
-   OffsetNumber child;
    int         ret;
    GISTPageOpaque opaque;
 
@@ -420,12 +424,20 @@ gistlayerinsert(Relation r, BlockNumber blkno,
 
    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;
        BlockNumber nblkno;
        ItemPointerData oldtid;
        IndexTuple  oldtup;
+       OffsetNumber child;
 
        child = gistchoose(r, page, *(*itup), giststate);
        iid = PageGetItemId(page, child);
@@ -446,7 +458,7 @@ gistlayerinsert(Relation r, BlockNumber blkno,
            return 0x00;
        }
 
-       /* child does not splited */
+       /* child did not split */
        if (!(ret & SPLITED))
        {
            IndexTuple  newtup = gistgetadjusted(r, oldtup, (*itup)[0], giststate);
@@ -458,11 +470,15 @@ gistlayerinsert(Relation r, BlockNumber blkno,
                return 0x00;
            }
 
-           pfree((*itup)[0]);  /* !!! */
            (*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);
        gistdelete(r, &oldtid);
 
@@ -491,11 +507,6 @@ gistlayerinsert(Relation r, BlockNumber blkno,
        oldlen = *len;
        newitup = gistSplit(r, buffer, itvec, &tlen, giststate);
        ReleaseBuffer(buffer);
-       do
-           pfree((*itup)[oldlen - 1]);
-       while ((--oldlen) > 0);
-       pfree((*itup));
-       pfree(itvec);
        *itup = newitup;
        *len = tlen;            /* now tlen >= 2 */
    }
@@ -509,23 +520,17 @@ gistlayerinsert(Relation r, BlockNumber blkno,
            FirstOffsetNumber
            :
            OffsetNumberNext(PageGetMaxOffsetNumber(page));
-       l = gistwritebuffer(r, page, (*itup), *len, off);
+       l = gistwritebuffer(r, page, *itup, *len, off);
        WriteBuffer(buffer);
 
        if (*len > 1)
        {                       /* previous insert ret & SPLITED != 0 */
-           int         i;
-
            /*
             * child was splited, so we must form union for insertion in
             * parent
             */
            IndexTuple  newtup = gistunion(r, (*itup), *len, giststate);
-
            ItemPointerSet(&(newtup->t_tid), blkno, 1);
-
-           for (i = 0; i < *len; i++)
-               pfree((*itup)[i]);
            (*itup)[0] = newtup;
            *len = 1;
        }
@@ -544,23 +549,17 @@ gistwritebuffer(Relation r, Page page, IndexTuple *itup,
    OffsetNumber l = InvalidOffsetNumber;
    int         i;
 
-#ifdef GIST_PAGEADDITEM
-   GISTENTRY   tmpdentry;
-   IndexTuple  newtup;
-   bool        IsNull;
-#endif
    for (i = 0; i < len; i++)
    {
 #ifdef GIST_PAGEADDITEM
+       GISTENTRY   tmpdentry;
+       IndexTuple  newtup;
+       bool        IsNull;
+
        l = gistPageAddItem(giststate, r, page,
                            (Item) itup[i], IndexTupleSize(itup[i]),
                            off, LP_USED, &tmpdentry, &newtup);
        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
        l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]),
                        off, LP_USED);
@@ -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
 gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 {
    Datum       attr[INDEX_MAX_KEYS];
-   bool        whatfree[INDEX_MAX_KEYS];
    bool        isnull[INDEX_MAX_KEYS];
    GistEntryVector *evec;
-   Datum       datum;
-   int         datumsize,
-               i,
-               j;
+   int         i;
    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);
 
-   for (j = 0; j < r->rd_att->natts; j++)
+   for (i = 0; i < r->rd_att->natts; i++)
    {
-       reallen = 0;
-       for (i = 0; i < len; i++)
+       Datum       datum;
+       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)
                continue;
 
-           gistdentryinit(giststate, j,
-                          &(evec->vector[reallen]),
+           gistdentryinit(giststate, i,
+                          &(evec->vector[real_len]),
                           datum,
                           NULL, NULL, (OffsetNumber) 0,
-                          ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull);
-           if ((!isAttByVal(giststate, j)) &&
-               evec->vector[reallen].key != datum)
-               needfree[reallen] = TRUE;
-           else
-               needfree[reallen] = FALSE;
-           reallen++;
+                          ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
+                          FALSE, IsNull);
+           real_len++;
        }
 
-       if (reallen == 0)
+       /* If this tuple vector was all NULLs, the union is NULL */
+       if (real_len == 0)
        {
-           attr[j] = (Datum) 0;
-           isnull[j] = TRUE;
-           whatfree[j] = FALSE;
+           attr[i] = (Datum) 0;
+           isnull[i] = TRUE;
        }
        else
        {
-           if (reallen == 1)
+           int datumsize;
+
+           if (real_len == 1)
            {
                evec->n = 2;
                gistentryinit(evec->vector[1],
                              evec->vector[0].key, r, NULL,
-                        (OffsetNumber) 0, evec->vector[0].bytes, FALSE);
-
+                             (OffsetNumber) 0, evec->vector[0].bytes, FALSE);
            }
            else
-               evec->n = reallen;
-           datum = FunctionCall2(&giststate->unionFn[j],
+               evec->n = real_len;
+
+           /* Compress the result of the union and store in attr array */
+           datum = FunctionCall2(&giststate->unionFn[i],
                                  PointerGetDatum(evec),
                                  PointerGetDatum(&datumsize));
 
-           for (i = 0; i < reallen; i++)
-               if (needfree[i])
-                   pfree(DatumGetPointer(evec->vector[i].key));
-
-           gistcentryinit(giststate, j, &centry[j], datum,
+           gistcentryinit(giststate, i, &centry[i], datum,
                           NULL, NULL, (OffsetNumber) 0,
                           datumsize, FALSE, FALSE);
-           isnull[j] = FALSE;
-           attr[j] = centry[j].key;
-           if (!isAttByVal(giststate, j))
-           {
-               whatfree[j] = TRUE;
-               if (centry[j].key != datum)
-                   pfree(DatumGetPointer(datum));
-           }
-           else
-               whatfree[j] = FALSE;
+           isnull[i] = FALSE;
+           attr[i] = centry[i].key;
        }
    }
 
-   pfree(evec);
-   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;
+   return index_form_tuple(giststate->tupdesc, attr, isnull);
 }
 
 
@@ -725,24 +700,18 @@ static IndexTuple
 gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 {
    GistEntryVector *evec;
-   Datum       datum;
-   int         datumsize;
-   bool        result,
-               neednew = false;
-   bool        isnull[INDEX_MAX_KEYS],
-               whatfree[INDEX_MAX_KEYS];
+   bool        neednew = false;
+   bool        isnull[INDEX_MAX_KEYS];
    Datum       attr[INDEX_MAX_KEYS];
    GISTENTRY   centry[INDEX_MAX_KEYS],
                oldatt[INDEX_MAX_KEYS],
                addatt[INDEX_MAX_KEYS],
               *ev0p,
               *ev1p;
-   bool        olddec[INDEX_MAX_KEYS],
-               adddec[INDEX_MAX_KEYS];
    bool        oldisnull[INDEX_MAX_KEYS],
                addisnull[INDEX_MAX_KEYS];
    IndexTuple  newtup = NULL;
-   int         j;
+   int         i;
 
    evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
    evec->n = 2;
@@ -750,39 +719,40 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
    ev1p = &(evec->vector[1]);
 
    gistDeCompressAtt(giststate, r, oldtup, NULL,
-                     (OffsetNumber) 0, oldatt, olddec, oldisnull);
+                     (OffsetNumber) 0, oldatt, oldisnull);
 
    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;
-           isnull[j] = TRUE;
-           whatfree[j] = FALSE;
+           attr[i] = (Datum) 0;
+           isnull[i] = TRUE;
        }
        else
        {
-           FILLEV(
-                  oldisnull[j], oldatt[j].key, oldatt[j].bytes,
-                  addisnull[j], addatt[j].key, addatt[j].bytes
-               );
+           Datum       datum;
+           int         datumsize;
 
-           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(&datumsize));
 
-           if (oldisnull[j] || addisnull[j])
+           if (oldisnull[i] || addisnull[i])
            {
-               if (oldisnull[j])
+               if (oldisnull[i])
                    neednew = true;
            }
            else
            {
-               FunctionCall3(&giststate->equalFn[j],
+               bool    result;
+
+               FunctionCall3(&giststate->equalFn[i],
                              ev0p->key,
                              datum,
                              PointerGetDatum(&result));
@@ -791,28 +761,14 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
                    neednew = true;
            }
 
-           if (olddec[j])
-               pfree(DatumGetPointer(oldatt[j].key));
-           if (adddec[j])
-               pfree(DatumGetPointer(addatt[j].key));
-
-           gistcentryinit(giststate, j, &centry[j], datum,
+           gistcentryinit(giststate, i, &centry[i], datum,
                           NULL, NULL, (OffsetNumber) 0,
                           datumsize, FALSE, FALSE);
 
-           attr[j] = centry[j].key;
-           isnull[j] = FALSE;
-           if ((!isAttByVal(giststate, j)))
-           {
-               whatfree[j] = TRUE;
-               if (centry[j].key != datum)
-                   pfree(DatumGetPointer(datum));
-           }
-           else
-               whatfree[j] = FALSE;
+           attr[i] = centry[i].key;
+           isnull[i] = FALSE;
        }
    }
-   pfree(evec);
 
    if (neednew)
    {
@@ -821,33 +777,24 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
        newtup->t_tid = oldtup->t_tid;
    }
 
-   for (j = 0; j < r->rd_att->natts; j++)
-       if (whatfree[j])
-           pfree(DatumGetPointer(attr[j]));
-
    return newtup;
 }
 
 static void
 gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
 {
-   int         i,
-               j,
-               lr;
-   Datum      *attr;
-   bool       *needfree,
-               IsNull;
-   int         len,
-              *attrsize;
-   OffsetNumber *entries;
-   GistEntryVector *evec;
-   Datum       datum;
-   int         datumsize;
-   int         reallen;
-   bool       *isnull;
+   int lr;
 
-   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)
        {
            attrsize = spl->spl_lattrsize;
@@ -865,38 +812,41 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
            isnull = spl->spl_risnull;
        }
 
-       needfree = (bool *) palloc(((len == 1) ? 2 : len) * sizeof(bool));
        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;
-           for (i = 0; i < len; i++)
+           int         j;
+           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;
-               datum = index_getattr(itvec[entries[i] - 1], j + 1,
+               datum = index_getattr(itvec[entries[j] - 1], i + 1,
                                      giststate->tupdesc, &IsNull);
                if (IsNull)
                    continue;
-               gistdentryinit(giststate, j,
-                              &(evec->vector[reallen]),
+               gistdentryinit(giststate, i,
+                              &(evec->vector[real_len]),
                               datum,
                               NULL, NULL, (OffsetNumber) 0,
-                              ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), FALSE, IsNull);
-               if ((!isAttByVal(giststate, j)) &&
-                   evec->vector[reallen].key != datum)
-                   needfree[reallen] = TRUE;
-               else
-                   needfree[reallen] = FALSE;
-               reallen++;
+                              ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
+                              FALSE, IsNull);
+               real_len++;
 
            }
-           if (reallen == 0)
+
+           if (real_len == 0)
            {
                datum = (Datum) 0;
                datumsize = 0;
-               isnull[j] = true;
+               isnull[i] = true;
            }
            else
            {
@@ -904,30 +854,23 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
                 * evec->vector[0].bytes may be not defined, so form union
                 * with itself
                 */
-               if (reallen == 1)
+               if (real_len == 1)
                {
                    evec->n = 2;
-                   memcpy((void *) &(evec->vector[1]),
-                          (void *) &(evec->vector[0]),
+                   memcpy(&(evec->vector[1]), &(evec->vector[0]),
                           sizeof(GISTENTRY));
                }
                else
-                   evec->n = reallen;
-               datum = FunctionCall2(&giststate->unionFn[j],
+                   evec->n = real_len;
+               datum = FunctionCall2(&giststate->unionFn[i],
                                      PointerGetDatum(evec),
                                      PointerGetDatum(&datumsize));
-               isnull[j] = false;
+               isnull[i] = false;
            }
 
-           for (i = 0; i < reallen; i++)
-               if (needfree[i])
-                   pfree(DatumGetPointer(evec->vector[i].key));
-
-           attr[j] = datum;
-           attrsize[j] = datumsize;
+           attr[i] = datum;
+           attrsize[i] = datumsize;
        }
-       pfree(evec);
-       pfree(needfree);
    }
 }
 
@@ -937,11 +880,8 @@ gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITV
 static int
 gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
 {
-   int         i,
-               j,
-               len;
+   int         i;
    int         curid = 1;
-   bool        result;
 
    /*
     * 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)
     */
    for (i = 0; i < spl->spl_nleft; i++)
    {
+       int j;
+       int len;
+       bool result;
+
        if (spl->spl_idgrp[spl->spl_left[i]])
            continue;
        len = 0;
@@ -996,8 +940,8 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
 }
 
 /*
- * Insert equivalent tuples to left or right page
- * with minimize penalty
+ * Insert equivalent tuples to left or right page with minimum
+ * penalty
  */
 static void
 gistadjsubkey(Relation r,
@@ -1008,7 +952,6 @@ gistadjsubkey(Relation r,
 {
    int         curlen;
    OffsetNumber *curwpos;
-   bool        decfree[INDEX_MAX_KEYS];
    GISTENTRY   entry,
                identry[INDEX_MAX_KEYS],
               *ev0p,
@@ -1020,12 +963,12 @@ gistadjsubkey(Relation r,
    bool        isnull[INDEX_MAX_KEYS];
    int         i,
                j;
-   Datum       datum;
 
    /* clear vectors */
    curlen = v->spl_nleft;
    curwpos = v->spl_left;
    for (i = 0; i < v->spl_nleft; i++)
+   {
        if (v->spl_idgrp[v->spl_left[i]] == 0)
        {
            *curwpos = v->spl_left[i];
@@ -1033,11 +976,13 @@ gistadjsubkey(Relation r,
        }
        else
            curlen--;
+   }
    v->spl_nleft = curlen;
 
    curlen = v->spl_nright;
    curwpos = v->spl_right;
    for (i = 0; i < v->spl_nright; i++)
+   {
        if (v->spl_idgrp[v->spl_right[i]] == 0)
        {
            *curwpos = v->spl_right[i];
@@ -1045,6 +990,7 @@ gistadjsubkey(Relation r,
        }
        else
            curlen--;
+   }
    v->spl_nright = curlen;
 
    evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
@@ -1055,16 +1001,17 @@ gistadjsubkey(Relation r,
    /* add equivalent tuple */
    for (i = 0; i < *len; i++)
    {
+       Datum       datum;
+
        if (v->spl_idgrp[i + 1] == 0)   /* already inserted */
            continue;
        gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0,
-                         identry, decfree, isnull);
+                         identry, isnull);
 
        v->spl_ngrp[v->spl_idgrp[i + 1]]--;
        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 */
            rpenalty = 1.0;
            lpenalty = (v->spl_grpflag[v->spl_idgrp[i + 1]] & LEFT_ADDED) ? 2.0 : 0.0;
@@ -1088,7 +1035,11 @@ gistadjsubkey(Relation r,
                    break;
            }
        }
-       /* add */
+
+       /*
+        * add
+        * XXX: refactor this to avoid duplicating code
+        */
        if (lpenalty < rpenalty)
        {
            v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED;
@@ -1103,17 +1054,13 @@ gistadjsubkey(Relation r,
                }
                else
                {
-                   FILLEV(
-                          v->spl_lisnull[j], v->spl_lattr[j], v->spl_lattrsize[j],
-                          isnull[j], identry[j].key, identry[j].bytes
-                       );
+                   FILLEV(v->spl_lisnull[j], v->spl_lattr[j], v->spl_lattrsize[j],
+                          isnull[j], identry[j].key, identry[j].bytes);
 
                    datum = FunctionCall2(&giststate->unionFn[j],
                                          PointerGetDatum(evec),
                                          PointerGetDatum(&datumsize));
 
-                   if ((!isAttByVal(giststate, j)) && !v->spl_lisnull[j])
-                       pfree(DatumGetPointer(v->spl_lattr[j]));
                    v->spl_lattr[j] = datum;
                    v->spl_lattrsize[j] = datumsize;
                    v->spl_lisnull[j] = false;
@@ -1134,28 +1081,20 @@ gistadjsubkey(Relation r,
                }
                else
                {
-                   FILLEV(
-                          v->spl_risnull[j], v->spl_rattr[j], v->spl_rattrsize[j],
-                          isnull[j], identry[j].key, identry[j].bytes
-                       );
+                   FILLEV(v->spl_risnull[j], v->spl_rattr[j], v->spl_rattrsize[j],
+                          isnull[j], identry[j].key, identry[j].bytes);
 
                    datum = FunctionCall2(&giststate->unionFn[j],
                                          PointerGetDatum(evec),
                                          PointerGetDatum(&datumsize));
 
-                   if ((!isAttByVal(giststate, j)) && !v->spl_risnull[j])
-                       pfree(DatumGetPointer(v->spl_rattr[j]));
-
                    v->spl_rattr[j] = datum;
                    v->spl_rattrsize[j] = datumsize;
                    v->spl_risnull[j] = false;
                }
            }
-
        }
-       gistFreeAtt(r, identry, decfree);
    }
-   pfree(evec);
 }
 
 /*
@@ -1181,13 +1120,8 @@ gistSplit(Relation r,
    GISTPageOpaque opaque;
    GIST_SPLITVEC v;
    GistEntryVector *entryvec;
-   bool       *decompvec;
    int         i,
-               j,
                nlen;
-   int         MaxGrpId = 1;
-   Datum       datum;
-   bool        IsNull;
 
    p = (Page) BufferGetPage(buffer);
    opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
@@ -1197,8 +1131,7 @@ gistSplit(Relation r,
     * about to split the root, we need to do some hocus-pocus to enforce
     * this guarantee.
     */
-
-   if (BufferGetBlockNumber(buffer) == GISTP_ROOT)
+   if (BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO)
    {
        leftbuf = ReadBuffer(r, P_NEW);
        GISTInitBuffer(leftbuf, opaque->flags);
@@ -1221,17 +1154,17 @@ gistSplit(Relation r,
    /* generate the item array */
    entryvec = palloc(GEVHDRSZ + (*len + 1) * sizeof(GISTENTRY));
    entryvec->n = *len + 1;
-   decompvec = (bool *) palloc((*len + 1) * sizeof(bool));
+
    for (i = 1; i <= *len; i++)
    {
+       Datum       datum;
+       bool        IsNull;
+
        datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull);
        gistdentryinit(giststate, 0, &(entryvec->vector[i]),
                       datum, r, p, i,
-          ATTSIZE(datum, giststate->tupdesc, 1, IsNull), FALSE, IsNull);
-       if ((!isAttByVal(giststate, 0)) && entryvec->vector[i].key != datum)
-           decompvec[i] = TRUE;
-       else
-           decompvec[i] = FALSE;
+                      ATTSIZE(datum, giststate->tupdesc, 1, IsNull),
+                      FALSE, IsNull);
    }
 
    /*
@@ -1259,6 +1192,8 @@ gistSplit(Relation r,
     */
    if (r->rd_att->natts > 1)
    {
+       int         MaxGrpId;
+
        v.spl_idgrp = (int *) palloc0(sizeof(int) * (*len + 1));
        v.spl_grpflag = (char *) palloc0(sizeof(char) * (*len + 1));
        v.spl_ngrp = (int *) palloc(sizeof(int) * (*len + 1));
@@ -1274,19 +1209,8 @@ gistSplit(Relation r,
         */
        if (MaxGrpId > 1)
            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 */
    lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nleft);
    rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * v.spl_nright);
@@ -1298,15 +1222,12 @@ gistSplit(Relation r,
        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))
    {
        nlen = v.spl_nright;
        newtup = gistSplit(r, rightbuf, rvectup, &nlen, giststate);
        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
    {
@@ -1321,7 +1242,6 @@ gistSplit(Relation r,
        ItemPointerSet(&(newtup[0]->t_tid), rbknum, 1);
    }
 
-
    if (gistnospace(left, lvectup, v.spl_nleft))
    {
        int         llen = v.spl_nleft;
@@ -1330,35 +1250,24 @@ gistSplit(Relation r,
        lntup = gistSplit(r, leftbuf, lvectup, &llen, giststate);
        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);
-       pfree(lntup);
    }
    else
    {
        OffsetNumber l;
 
        l = gistwritebuffer(r, left, lvectup, v.spl_nleft, FirstOffsetNumber);
-       if (BufferGetBlockNumber(buffer) != GISTP_ROOT)
+       if (BufferGetBlockNumber(buffer) != GIST_ROOT_BLKNO)
            PageRestoreTempPage(left, p);
 
        WriteBuffer(leftbuf);
 
        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);
        ItemPointerSet(&(newtup[nlen - 1]->t_tid), lbknum, 1);
    }
 
-   /* !!! pfree */
-   pfree(rvectup);
-   pfree(lvectup);
-   pfree(v.spl_left);
-   pfree(v.spl_right);
-
    *len = nlen;
    return newtup;
 }
@@ -1369,7 +1278,7 @@ gistnewroot(Relation r, IndexTuple *itup, int len)
    Buffer      b;
    Page        p;
 
-   b = ReadBuffer(r, GISTP_ROOT);
+   b = ReadBuffer(r, GIST_ROOT_BLKNO);
    GISTInitBuffer(b, 0);
    p = BufferGetPage(b);
 
@@ -1385,16 +1294,13 @@ GISTInitBuffer(Buffer b, uint32 f)
    Size        pageSize;
 
    pageSize = BufferGetPageSize(b);
-
    page = BufferGetPage(b);
-
    PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
 
    opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
    opaque->flags = f;
 }
 
-
 /*
  * find entry with lowest penalty
  */
@@ -1404,17 +1310,12 @@ gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
 {
    OffsetNumber maxoff;
    OffsetNumber i;
-   Datum       datum;
-   float       usize;
    OffsetNumber which;
    float       sum_grow,
                which_grow[INDEX_MAX_KEYS];
    GISTENTRY   entry,
                identry[INDEX_MAX_KEYS];
-   bool        IsNull,
-               decompvec[INDEX_MAX_KEYS],
-               isnull[INDEX_MAX_KEYS];
-   int         j;
+   bool        isnull[INDEX_MAX_KEYS];
 
    maxoff = PageGetMaxOffsetNumber(p);
    *which_grow = -1.0;
@@ -1422,21 +1323,26 @@ gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
    sum_grow = 1;
    gistDeCompressAtt(giststate, r,
                      it, NULL, (OffsetNumber) 0,
-                     identry, decompvec, isnull);
+                     identry, isnull);
 
    for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i))
    {
+       int         j;
        IndexTuple  itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
 
        sum_grow = 0;
        for (j = 0; j < r->rd_att->natts; j++)
        {
-           datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
-           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);
+           Datum       datum;
+           float       usize;
+           bool        IsNull;
 
-           if ((!isAttByVal(giststate, j)) && entry.key != datum)
-               pfree(DatumGetPointer(entry.key));
+           datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
+           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])
            {
@@ -1456,24 +1362,9 @@ gistchoose(Relation r, Page p, IndexTuple it,    /* it has compressed entry */
        }
    }
 
-   gistFreeAtt(r, identry, decompvec);
    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.
  *
@@ -1593,7 +1484,6 @@ gistbulkdelete(PG_FUNCTION_ARGS)
    PG_RETURN_POINTER(result);
 }
 
-
 void
 initGISTstate(GISTSTATE *giststate, Relation index)
 {
@@ -1608,22 +1498,22 @@ initGISTstate(GISTSTATE *giststate, Relation index)
    for (i = 0; i < index->rd_att->natts; i++)
    {
        fmgr_info_copy(&(giststate->consistentFn[i]),
-                  index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
+                      index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->unionFn[i]),
                       index_getprocinfo(index, i + 1, GIST_UNION_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->compressFn[i]),
-                    index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
+                      index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->decompressFn[i]),
-                  index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
+                      index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->penaltyFn[i]),
                       index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->picksplitFn[i]),
-                   index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
+                      index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(giststate->equalFn[i]),
                       index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
@@ -1703,11 +1593,8 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
                                          PointerGetDatum(e)));
        /* decompressFn may just return the given pointer */
        if (dep != e)
-       {
            gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
                          dep->bytes, dep->leafkey);
-           pfree(dep);
-       }
    }
    else
        gistentryinit(*e, (Datum) 0, r, pg, o, 0, l);
@@ -1732,11 +1619,8 @@ gistcentryinit(GISTSTATE *giststate, int nkey,
                                          PointerGetDatum(e)));
        /* compressFn may just return the given pointer */
        if (cep != e)
-       {
            gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset,
                          cep->bytes, cep->leafkey);
-           pfree(cep);
-       }
    }
    else
        gistentryinit(*e, (Datum) 0, r, pg, o, 0, l);
@@ -1746,79 +1630,42 @@ static IndexTuple
 gistFormTuple(GISTSTATE *giststate, Relation r,
              Datum attdata[], int datumsize[], bool isnull[])
 {
-   IndexTuple  tup;
-   bool        whatfree[INDEX_MAX_KEYS];
    GISTENTRY   centry[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])
-       {
-           compatt[j] = (Datum) 0;
-           whatfree[j] = FALSE;
-       }
+       if (isnull[i])
+           compatt[i] = (Datum) 0;
        else
        {
-           gistcentryinit(giststate, j, &centry[j], attdata[j],
+           gistcentryinit(giststate, i, &centry[i], attdata[i],
                           NULL, NULL, (OffsetNumber) 0,
-                          datumsize[j], FALSE, FALSE);
-           compatt[j] = centry[j].key;
-           if (!isAttByVal(giststate, j))
-           {
-               whatfree[j] = TRUE;
-               if (centry[j].key != attdata[j])
-                   pfree(DatumGetPointer(attdata[j]));
-           }
-           else
-               whatfree[j] = FALSE;
+                          datumsize[i], FALSE, FALSE);
+           compatt[i] = centry[i].key;
        }
    }
 
-   tup = 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;
+   return index_form_tuple(giststate->tupdesc, compatt, isnull);
 }
 
 static void
 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;
-   Datum       datum;
 
    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],
                       datum, r, p, o,
-                      ATTSIZE(datum, giststate->tupdesc, i + 1, isnull[i]), FALSE, isnull[i]);
-       if (isAttByVal(giststate, i))
-           decompvec[i] = FALSE;
-       else
-       {
-           if (attdata[i].key == datum || isnull[i])
-               decompvec[i] = FALSE;
-           else
-               decompvec[i] = TRUE;
-       }
+                      ATTSIZE(datum, giststate->tupdesc, i + 1, isnull[i]),
+                      FALSE, isnull[i]);
    }
 }
 
-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
 gistpenalty(GISTSTATE *giststate, int attno,
            GISTENTRY *key1, bool isNull1,
index 8f7a6c7ed4ff5dc2ec98c7868bab1035840ce658..d2b0f75fc1ba562118cf558716fd6c0ba5a278d2 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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;
 }
index 0746340df45ce0fe33f667f0a05affc67ef4861f..7b449892f11745056ed26374120ce924c77b072f 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.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 $
  *
  *-------------------------------------------------------------------------
  */
 #include "access/genam.h"
 #include "access/gist.h"
 #include "access/gistscan.h"
+#include "utils/memutils.h"
 #include "utils/resowner.h"
 
-
 /* routines defined and used here */
-static void gistregscan(IndexScanDesc s);
-static void gistdropscan(IndexScanDesc s);
-static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
+static void gistregscan(IndexScanDesc scan);
+static void gistdropscan(IndexScanDesc scan);
+static void gistadjone(IndexScanDesc scan, int op, BlockNumber blkno,
           OffsetNumber offnum);
 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);
+static void gistfreestack(GISTSTACK *s);
 
 /*
- * Whenever we start a GiST scan in a backend, we register it in private
- * space.  Then if the GiST index gets updated, we check all registered
- * scans and adjust them if the tuple they point at got moved by the
- * update.  We only need to do this in private space, because when we update
- * an GiST we have a write lock on the tree, so no other process can have
- * any locks at all on it.  A single transaction can have write and read
- * locks on the same object, so that's why we need to handle this case.
+ * Whenever we start a GiST scan in a backend, we register it in
+ * private space. Then if the GiST index gets updated, we check all
+ * registered scans and adjust them if the tuple they point at got
+ * moved by the update.  We only need to do this in private space,
+ * because when we update an GiST we have a write lock on the tree, so
+ * no other process can have any locks at all on it.  A single
+ * transaction can have write and read locks on the same object, so
+ * that's why we need to handle this case.
  */
-
 typedef struct GISTScanListData
 {
    IndexScanDesc gsl_scan;
@@ -57,65 +58,77 @@ gistbeginscan(PG_FUNCTION_ARGS)
    Relation    r = (Relation) PG_GETARG_POINTER(0);
    int         nkeys = PG_GETARG_INT32(1);
    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(s);
+   PG_RETURN_POINTER(scan);
 }
 
 Datum
 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);
-   GISTScanOpaque p;
+   GISTScanOpaque so;
    int         i;
 
    /*
     * Clear all the pointers.
     */
-   ItemPointerSetInvalid(&s->currentItemData);
-   ItemPointerSetInvalid(&s->currentMarkData);
+   ItemPointerSetInvalid(&scan->currentItemData);
+   ItemPointerSetInvalid(&scan->currentMarkData);
 
-   p = (GISTScanOpaque) s->opaque;
-   if (p != NULL)
+   so = (GISTScanOpaque) scan->opaque;
+   if (so != NULL)
    {
        /* rescan an existing indexscan --- reset state */
-       gistfreestack(p->s_stack);
-       gistfreestack(p->s_markstk);
-       p->s_stack = p->s_markstk = NULL;
-       p->s_flags = 0x0;
+       gistfreestack(so->stack);
+       gistfreestack(so->markstk);
+       so->stack = so->markstk = NULL;
+       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
    {
        /* initialize opaque data */
-       p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
-       p->s_stack = p->s_markstk = NULL;
-       p->s_flags = 0x0;
-       s->opaque = p;
-       p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
-       initGISTstate(p->giststate, s->indexRelation);
+       so = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
+       so->stack = so->markstk = NULL;
+       so->flags = 0x0;
+       so->tempCxt = createTempGistContext();
+       so->curbuf = so->markbuf = InvalidBuffer;
+       so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
+       initGISTstate(so->giststate, scan->indexRelation);
+
+       scan->opaque = so;
    }
 
    /* Update scan key, if a new one is given */
-   if (key && s->numberOfKeys > 0)
+   if (key && scan->numberOfKeys > 0)
    {
-       memmove(s->keyData,
-               key,
-               s->numberOfKeys * sizeof(ScanKeyData));
+       memmove(scan->keyData, key,
+               scan->numberOfKeys * sizeof(ScanKeyData));
 
        /*
-        * Modify the scan key so that the Consistent function is called
-        * for all comparisons.  The original operator is passed to the
-        * Consistent function in the form of its strategy number, which
-        * is available from the sk_strategy field, and its subtype from
-        * the sk_subtype field.
+        * Modify the scan key so that all the Consistent method is
+        * called for all comparisons. The original operator is passed
+        * to the Consistent function in the form of its strategy
+        * number, which is available from the sk_strategy field, and
+        * its subtype from the sk_subtype field.
         */
-       for (i = 0; i < s->numberOfKeys; i++)
-           s->keyData[i].sk_func = p->giststate->consistentFn[s->keyData[i].sk_attno - 1];
+       for (i = 0; i < scan->numberOfKeys; i++)
+           scan->keyData[i].sk_func = so->giststate->consistentFn[scan->keyData[i].sk_attno - 1];
    }
 
    PG_RETURN_VOID();
@@ -124,35 +137,47 @@ gistrescan(PG_FUNCTION_ARGS)
 Datum
 gistmarkpos(PG_FUNCTION_ARGS)
 {
-   IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
-   GISTScanOpaque p;
+   IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
+   GISTScanOpaque so;
    GISTSTACK  *o,
               *n,
               *tmp;
 
-   s->currentMarkData = s->currentItemData;
-   p = (GISTScanOpaque) s->opaque;
-   if (p->s_flags & GS_CURBEFORE)
-       p->s_flags |= GS_MRKBEFORE;
+   scan->currentMarkData = scan->currentItemData;
+   so = (GISTScanOpaque) scan->opaque;
+   if (so->flags & GS_CURBEFORE)
+       so->flags |= GS_MRKBEFORE;
    else
-       p->s_flags &= ~GS_MRKBEFORE;
+       so->flags &= ~GS_MRKBEFORE;
 
    o = NULL;
-   n = p->s_stack;
+   n = so->stack;
 
    /* copy the parent stack from the current item data */
    while (n != NULL)
    {
        tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
-       tmp->gs_child = n->gs_child;
-       tmp->gs_blk = n->gs_blk;
-       tmp->gs_parent = o;
+       tmp->offset = n->offset;
+       tmp->block = n->block;
+       tmp->parent = o;
        o = tmp;
-       n = n->gs_parent;
+       n = n->parent;
    }
 
-   gistfreestack(p->s_markstk);
-   p->s_markstk = o;
+   gistfreestack(so->markstk);
+   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();
 }
@@ -160,35 +185,47 @@ gistmarkpos(PG_FUNCTION_ARGS)
 Datum
 gistrestrpos(PG_FUNCTION_ARGS)
 {
-   IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
-   GISTScanOpaque p;
+   IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
+   GISTScanOpaque so;
    GISTSTACK  *o,
               *n,
               *tmp;
 
-   s->currentItemData = s->currentMarkData;
-   p = (GISTScanOpaque) s->opaque;
-   if (p->s_flags & GS_MRKBEFORE)
-       p->s_flags |= GS_CURBEFORE;
+   scan->currentItemData = scan->currentMarkData;
+   so = (GISTScanOpaque) scan->opaque;
+   if (so->flags & GS_MRKBEFORE)
+       so->flags |= GS_CURBEFORE;
    else
-       p->s_flags &= ~GS_CURBEFORE;
+       so->flags &= ~GS_CURBEFORE;
 
    o = NULL;
-   n = p->s_markstk;
+   n = so->markstk;
 
    /* copy the parent stack from the current item data */
    while (n != NULL)
    {
        tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
-       tmp->gs_child = n->gs_child;
-       tmp->gs_blk = n->gs_blk;
-       tmp->gs_parent = o;
+       tmp->offset = n->offset;
+       tmp->block = n->block;
+       tmp->parent = o;
        o = tmp;
-       n = n->gs_parent;
+       n = n->parent;
    }
 
-   gistfreestack(p->s_stack);
-   p->s_stack = o;
+   gistfreestack(so->stack);
+   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();
 }
@@ -196,52 +233,57 @@ gistrestrpos(PG_FUNCTION_ARGS)
 Datum
 gistendscan(PG_FUNCTION_ARGS)
 {
-   IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
-   GISTScanOpaque p;
+   IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
+   GISTScanOpaque so;
 
-   p = (GISTScanOpaque) s->opaque;
+   so = (GISTScanOpaque) scan->opaque;
 
-   if (p != NULL)
+   if (so != NULL)
    {
-       gistfreestack(p->s_stack);
-       gistfreestack(p->s_markstk);
-       if (p->giststate != NULL)
-           freeGISTstate(p->giststate);
-       pfree(s->opaque);
+       gistfreestack(so->stack);
+       gistfreestack(so->markstk);
+       if (so->giststate != NULL)
+           freeGISTstate(so->giststate);
+       /* 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);
-   /* XXX don't unset read lock -- two-phase locking */
+   gistdropscan(scan);
 
    PG_RETURN_VOID();
 }
 
 static void
-gistregscan(IndexScanDesc s)
+gistregscan(IndexScanDesc scan)
 {
    GISTScanList l;
 
    l = (GISTScanList) palloc(sizeof(GISTScanListData));
-   l->gsl_scan = s;
+   l->gsl_scan = scan;
    l->gsl_owner = CurrentResourceOwner;
    l->gsl_next = GISTScans;
    GISTScans = l;
 }
 
 static void
-gistdropscan(IndexScanDesc s)
+gistdropscan(IndexScanDesc scan)
 {
    GISTScanList l;
    GISTScanList prev;
 
    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;
 
    if (l == NULL)
        elog(ERROR, "GiST scan list corrupted -- could not find 0x%p",
-            (void *) s);
+            (void *) scan);
 
    if (prev == NULL)
        GISTScans = l->gsl_next;
@@ -313,22 +355,22 @@ gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum)
  *     update.  If so, we make the change here.
  */
 static void
-gistadjone(IndexScanDesc s,
+gistadjone(IndexScanDesc scan,
           int op,
           BlockNumber blkno,
           OffsetNumber offnum)
 {
    GISTScanOpaque so;
 
-   adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
-   adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
+   adjustiptr(scan, &(scan->currentItemData), op, blkno, offnum);
+   adjustiptr(scan, &(scan->currentMarkData), op, blkno, offnum);
 
-   so = (GISTScanOpaque) s->opaque;
+   so = (GISTScanOpaque) scan->opaque;
 
    if (op == GISTOP_SPLIT)
    {
-       adjuststack(so->s_stack, blkno);
-       adjuststack(so->s_markstk, blkno);
+       adjuststack(so->stack, blkno);
+       adjuststack(so->markstk, blkno);
    }
 }
 
@@ -340,7 +382,7 @@ gistadjone(IndexScanDesc s,
  *     the same page.
  */
 static void
-adjustiptr(IndexScanDesc s,
+adjustiptr(IndexScanDesc scan,
           ItemPointer iptr,
           int op,
           BlockNumber blkno,
@@ -354,7 +396,7 @@ adjustiptr(IndexScanDesc s,
        if (ItemPointerGetBlockNumber(iptr) == blkno)
        {
            curoff = ItemPointerGetOffsetNumber(iptr);
-           so = (GISTScanOpaque) s->opaque;
+           so = (GISTScanOpaque) scan->opaque;
 
            switch (op)
            {
@@ -362,7 +404,6 @@ adjustiptr(IndexScanDesc s,
                    /* back up one if we need to */
                    if (curoff >= offnum)
                    {
-
                        if (curoff > FirstOffsetNumber)
                        {
                            /* just adjust the item pointer */
@@ -375,10 +416,10 @@ adjustiptr(IndexScanDesc s,
                             * tuple
                             */
                            ItemPointerSet(iptr, blkno, FirstOffsetNumber);
-                           if (iptr == &(s->currentItemData))
-                               so->s_flags |= GS_CURBEFORE;
+                           if (iptr == &(scan->currentItemData))
+                               so->flags |= GS_CURBEFORE;
                            else
-                               so->s_flags |= GS_MRKBEFORE;
+                               so->flags |= GS_MRKBEFORE;
                        }
                    }
                    break;
@@ -386,10 +427,10 @@ adjustiptr(IndexScanDesc s,
                case GISTOP_SPLIT:
                    /* back to start of page on split */
                    ItemPointerSet(iptr, blkno, FirstOffsetNumber);
-                   if (iptr == &(s->currentItemData))
-                       so->s_flags &= ~GS_CURBEFORE;
+                   if (iptr == &(scan->currentItemData))
+                       so->flags &= ~GS_CURBEFORE;
                    else
-                       so->s_flags &= ~GS_MRKBEFORE;
+                       so->flags &= ~GS_MRKBEFORE;
                    break;
 
                default:
@@ -417,9 +458,20 @@ adjuststack(GISTSTACK *stk, BlockNumber blkno)
 {
    while (stk != NULL)
    {
-       if (stk->gs_blk == blkno)
-           stk->gs_child = FirstOffsetNumber;
+       if (stk->block == blkno)
+           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;
    }
 }
index ee2df86b402b8a6682f3e67f555ff2f7527a1ef0..3cca22954cb04438f32bec4da5272c149355a9c4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/gist.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;
 #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
 {
-   struct GISTSTACK *gs_parent;
-   OffsetNumber gs_child;
-   BlockNumber gs_blk;
+   struct GISTSTACK *parent;
+   OffsetNumber offset;
+   BlockNumber block;
 } GISTSTACK;
 
 typedef struct GISTSTATE
@@ -84,10 +92,13 @@ typedef struct GISTSTATE
  */
 typedef struct GISTScanOpaqueData
 {
-   struct GISTSTACK *s_stack;
-   struct GISTSTACK *s_markstk;
-   uint16      s_flags;
-   struct GISTSTATE *giststate;
+   GISTSTACK           *stack;
+   GISTSTACK           *markstk;
+   uint16               flags;
+   GISTSTATE           *giststate;
+   MemoryContext        tempCxt;
+   Buffer               curbuf;
+   Buffer               markbuf;
 } GISTScanOpaqueData;
 
 typedef GISTScanOpaqueData *GISTScanOpaque;
@@ -101,8 +112,8 @@ typedef GISTScanOpaqueData *GISTScanOpaque;
 #define GS_CURBEFORE   ((uint16) (1 << 0))
 #define GS_MRKBEFORE   ((uint16) (1 << 1))
 
-/* root page of a gist */
-#define GISTP_ROOT             0
+/* root page of a gist index */
+#define GIST_ROOT_BLKNO                0
 
 /*
  * 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);
 extern Datum gistinsert(PG_FUNCTION_ARGS);
 extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
 extern void _gistdump(Relation r);
-extern void gistfreestack(GISTSTACK *s);
 extern void initGISTstate(GISTSTATE *giststate, Relation index);
 extern void freeGISTstate(GISTSTATE *giststate);
 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_undo(XLogRecPtr lsn, XLogRecord *record);
 extern void gist_desc(char *buf, uint8 xl_info, char *rec);
+extern MemoryContext createTempGistContext(void);
 
 /* gistget.c */
 extern Datum gistgettuple(PG_FUNCTION_ARGS);