aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/gist/gist.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2001-07-15 22:48:19 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2001-07-15 22:48:19 +0000
commitc8076f09d2eb82a28f27f97230be470fffe7a1e0 (patch)
tree1e357e7e28313386f9d2e789d3905b37ce2d58f6 /src/backend/access/gist/gist.c
parent997439f59e1d487cb2bfa1384f6479fda0c4dd4c (diff)
downloadpostgresql-c8076f09d2eb82a28f27f97230be470fffe7a1e0.tar.gz
postgresql-c8076f09d2eb82a28f27f97230be470fffe7a1e0.zip
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in different AMs' ambuild routines has been moved out to a common routine in index.c; this means that all index types now do the right things about inserting recently-dead tuples, etc. (I also removed support for EXTEND INDEX in the ambuild routines, since that's about to go away anyway, and it cluttered the code a lot.) The retail indextuple deletion routines have been replaced by a "bulk delete" routine in which the indexscan is inside the access method. I haven't pushed this change as far as it should go yet, but it should allow considerable simplification of the internal bookkeeping for deletions. Also, add flag columns to pg_am to eliminate various hardcoded tests on AM OIDs, and remove unused pg_am columns. Fix rtree and gist index types to not attempt to store NULLs; before this, gist usually crashed, while rtree managed not to crash but computed wacko bounding boxes for NULL entries (which might have had something to do with the performance problems we've heard about occasionally). Add AtEOXact routines to hash, rtree, and gist, all of which have static state that needs to be reset after an error. We discovered this need long ago for btree, but missed the other guys. Oh, one more thing: concurrent VACUUM is now the default.
Diffstat (limited to 'src/backend/access/gist/gist.c')
-rw-r--r--src/backend/access/gist/gist.c408
1 files changed, 210 insertions, 198 deletions
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 9d6e2040f6c..c99c4a7e6e3 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.79 2001/06/11 05:00:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.80 2001/07/15 22:48:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,7 +43,23 @@
#define RIGHT_ADDED 0x02
#define BOTH_ADDED ( LEFT_ADDED | RIGHT_ADDED )
+
+/* Working state for gistbuild and its callback */
+typedef struct
+{
+ GISTSTATE giststate;
+ int numindexattrs;
+ double indtuples;
+} GISTBuildState;
+
+
/* non-export function prototypes */
+static void gistbuildCallback(Relation index,
+ HeapTuple htup,
+ Datum *attdata,
+ char *nulls,
+ bool tupleIsAlive,
+ void *state);
static void gistdoinsert(Relation r,
IndexTuple itup,
InsertIndexResult *res,
@@ -89,6 +105,7 @@ static void GISTInitBuffer(Buffer b, uint32 f);
static OffsetNumber gistchoose(Relation r, Page p,
IndexTuple it,
GISTSTATE *giststate);
+static void gistdelete(Relation r, ItemPointer tid);
#ifdef GIST_PAGEADDITEM
static IndexTuple gist_tuple_replacekey(Relation r,
GISTENTRY entry, IndexTuple t);
@@ -116,184 +133,36 @@ gistbuild(PG_FUNCTION_ARGS)
Relation heap = (Relation) PG_GETARG_POINTER(0);
Relation index = (Relation) PG_GETARG_POINTER(1);
IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
- Node *oldPred = (Node *) PG_GETARG_POINTER(3);
-
-#ifdef NOT_USED
- IndexStrategy istrat = (IndexStrategy) PG_GETARG_POINTER(4);
-
-#endif
- HeapScanDesc hscan;
- HeapTuple htup;
- IndexTuple itup;
- TupleDesc htupdesc,
- itupdesc;
- Datum attdata[INDEX_MAX_KEYS];
- char nulls[INDEX_MAX_KEYS];
- double nhtups,
- nitups;
- Node *pred = indexInfo->ii_Predicate;
-
-#ifndef OMIT_PARTIAL_INDEX
- TupleTable tupleTable;
- TupleTableSlot *slot;
-
-#endif
- ExprContext *econtext;
- GISTSTATE giststate;
- GISTENTRY tmpcentry;
- Buffer buffer = InvalidBuffer;
- bool *compvec;
- int i;
+ double reltuples;
+ GISTBuildState buildstate;
+ Buffer buffer;
/* no locking is needed */
- initGISTstate(&giststate, index);
+ 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.
*/
- if (oldPred == NULL && RelationGetNumberOfBlocks(index) != 0)
- elog(ERROR, "%s already contains data", RelationGetRelationName(index));
-
- /* initialize the root page (if this is a new index) */
- if (oldPred == NULL)
- {
- buffer = ReadBuffer(index, P_NEW);
- GISTInitBuffer(buffer, F_LEAF);
- WriteBuffer(buffer);
- }
+ if (RelationGetNumberOfBlocks(index) != 0)
+ elog(ERROR, "%s already contains data",
+ RelationGetRelationName(index));
- /* get tuple descriptors for heap and index relations */
- htupdesc = RelationGetDescr(heap);
- itupdesc = RelationGetDescr(index);
-
- /*
- * If this is a predicate (partial) index, we will need to evaluate
- * the predicate using ExecQual, which requires the current tuple to
- * be in a slot of a TupleTable. In addition, ExecQual must have an
- * ExprContext referring to that slot. Here, we initialize dummy
- * TupleTable and ExprContext objects for this purpose. --Nels, Feb 92
- *
- * We construct the ExprContext anyway since we need a per-tuple
- * temporary memory context for function evaluation -- tgl July 00
- */
-#ifndef OMIT_PARTIAL_INDEX
- if (pred != NULL || oldPred != NULL)
- {
- tupleTable = ExecCreateTupleTable(1);
- slot = ExecAllocTableSlot(tupleTable);
- ExecSetSlotDescriptor(slot, htupdesc, false);
- }
- else
- {
- tupleTable = NULL;
- slot = NULL;
- }
- econtext = MakeExprContext(slot, TransactionCommandContext);
-#else
- econtext = MakeExprContext(NULL, TransactionCommandContext);
-#endif /* OMIT_PARTIAL_INDEX */
+ /* initialize the root page */
+ buffer = ReadBuffer(index, P_NEW);
+ GISTInitBuffer(buffer, F_LEAF);
+ WriteBuffer(buffer);
/* build the index */
- nhtups = nitups = 0.0;
-
- compvec = (bool *) palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
-
- /* start a heap scan */
- hscan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
-
- while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
- {
- MemoryContextReset(econtext->ecxt_per_tuple_memory);
-
- nhtups += 1.0;
-
-#ifndef OMIT_PARTIAL_INDEX
-
- /*
- * If oldPred != NULL, this is an EXTEND INDEX command, so skip
- * this tuple if it was already in the existing partial index
- */
- if (oldPred != NULL)
- {
- slot->val = htup;
- if (ExecQual((List *) oldPred, econtext, false))
- {
- nitups += 1.0;
- continue;
- }
- }
-
- /*
- * Skip this tuple if it doesn't satisfy the partial-index
- * predicate
- */
- if (pred != NULL)
- {
- slot->val = htup;
- if (!ExecQual((List *) pred, econtext, false))
- continue;
- }
-#endif /* OMIT_PARTIAL_INDEX */
-
- nitups += 1.0;
-
- /*
- * For the current heap tuple, extract all the attributes we use
- * in this index, and note which are null.
- */
- FormIndexDatum(indexInfo,
- htup,
- htupdesc,
- econtext->ecxt_per_tuple_memory,
- attdata,
- nulls);
-
- /* immediately compress keys to normalize */
- for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
- {
- gistcentryinit(&giststate, i, &tmpcentry, attdata[i],
- (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
- -1 /* size is currently bogus */ , TRUE);
- if (attdata[i] != tmpcentry.key &&
- !(giststate.keytypbyval))
- compvec[i] = TRUE;
- else
- compvec[i] = FALSE;
- attdata[i] = tmpcentry.key;
- }
-
- /* form an index tuple and point it at the heap tuple */
- itup = index_formtuple(itupdesc, attdata, nulls);
- itup->t_tid = htup->t_self;
-
- /*
- * Since we already have the index relation locked, we call
- * gistdoinsert directly. Normal access method calls dispatch
- * through gistinsert, which locks the relation for write. This
- * is the right thing to do if you're inserting single tups, but
- * not when you're initializing the whole index at once.
- */
- gistdoinsert(index, itup, NULL, &giststate);
+ buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs;
+ buildstate.indtuples = 0;
- for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
- if (compvec[i])
- pfree(DatumGetPointer(attdata[i]));
-
- pfree(itup);
- }
+ /* do the heap scan */
+ reltuples = IndexBuildHeapScan(heap, index, indexInfo,
+ gistbuildCallback, (void *) &buildstate);
/* okay, all heap tuples are indexed */
- heap_endscan(hscan);
-
- pfree(compvec);
-
-#ifndef OMIT_PARTIAL_INDEX
- if (pred != NULL || oldPred != NULL)
- ExecDropTupleTable(tupleTable, true);
-#endif /* OMIT_PARTIAL_INDEX */
- FreeExprContext(econtext);
/*
* Since we just counted the tuples in the heap, we update its stats
@@ -313,14 +182,8 @@ gistbuild(PG_FUNCTION_ARGS)
heap_close(heap, NoLock);
index_close(index);
- UpdateStats(hrelid, nhtups);
- UpdateStats(irelid, nitups);
- if (oldPred != NULL)
- {
- if (nitups == nhtups)
- pred = NULL;
- UpdateIndexPredicate(irelid, oldPred, pred);
- }
+ UpdateStats(hrelid, reltuples);
+ UpdateStats(irelid, buildstate.indtuples);
}
#ifdef GISTDEBUG
@@ -331,6 +194,63 @@ gistbuild(PG_FUNCTION_ARGS)
}
/*
+ * Per-tuple callback from IndexBuildHeapScan
+ */
+static void
+gistbuildCallback(Relation index,
+ HeapTuple htup,
+ Datum *attdata,
+ char *nulls,
+ bool tupleIsAlive,
+ void *state)
+{
+ GISTBuildState *buildstate = (GISTBuildState *) state;
+ IndexTuple itup;
+ bool compvec[INDEX_MAX_KEYS];
+ GISTENTRY tmpcentry;
+ int i;
+
+ /* immediately compress keys to normalize */
+ for (i = 0; i < buildstate->numindexattrs; i++)
+ {
+ gistcentryinit(&buildstate->giststate, i, &tmpcentry, attdata[i],
+ (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
+ -1 /* size is currently bogus */ , TRUE);
+ if (attdata[i] != tmpcentry.key &&
+ !(buildstate->giststate.keytypbyval))
+ compvec[i] = TRUE;
+ else
+ compvec[i] = FALSE;
+ attdata[i] = tmpcentry.key;
+ }
+
+ /* form an index tuple and point it at the heap tuple */
+ itup = index_formtuple(RelationGetDescr(index), attdata, nulls);
+ itup->t_tid = htup->t_self;
+
+ /* GIST indexes don't index nulls, see notes in gistinsert */
+ if (! IndexTupleHasNulls(itup))
+ {
+ /*
+ * Since we already have the index relation locked, we call
+ * gistdoinsert directly. Normal access method calls dispatch
+ * through gistinsert, which locks the relation for write. This
+ * is the right thing to do if you're inserting single tups, but
+ * not when you're initializing the whole index at once.
+ */
+ gistdoinsert(index, itup, NULL, &buildstate->giststate);
+
+ buildstate->indtuples += 1;
+ }
+
+ for (i = 0; i < buildstate->numindexattrs; i++)
+ if (compvec[i])
+ pfree(DatumGetPointer(attdata[i]));
+
+ pfree(itup);
+}
+
+/*
* gistinsert -- wrapper for GiST tuple insertion.
*
* This is the public interface routine for tuple insertion in GiSTs.
@@ -343,25 +263,28 @@ gistinsert(PG_FUNCTION_ARGS)
Datum *datum = (Datum *) PG_GETARG_POINTER(1);
char *nulls = (char *) PG_GETARG_POINTER(2);
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
#ifdef NOT_USED
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
-
#endif
InsertIndexResult res;
IndexTuple itup;
GISTSTATE giststate;
GISTENTRY tmpentry;
int i;
- bool *compvec;
+ bool compvec[INDEX_MAX_KEYS];
+
+ /*
+ * Since GIST is not marked "amconcurrent" in pg_am, caller should
+ * have acquired exclusive lock on index relation. We need no locking
+ * here.
+ */
initGISTstate(&giststate, r);
/* immediately compress keys to normalize */
- compvec = (bool *) palloc(sizeof(bool) * r->rd_att->natts);
for (i = 0; i < r->rd_att->natts; i++)
{
- gistcentryinit(&giststate, i,&tmpentry, datum[i],
+ gistcentryinit(&giststate, i, &tmpentry, datum[i],
(Relation) NULL, (Page) NULL, (OffsetNumber) 0,
-1 /* size is currently bogus */ , TRUE);
if (datum[i] != tmpentry.key && !(giststate.keytypbyval))
@@ -374,18 +297,24 @@ gistinsert(PG_FUNCTION_ARGS)
itup->t_tid = *ht_ctid;
/*
- * Notes in ExecUtils:ExecOpenIndices()
- *
- * RelationSetLockForWrite(r);
+ * Currently, GIST indexes do not support indexing NULLs; considerable
+ * infrastructure work would have to be done to do anything reasonable
+ * with a NULL.
*/
+ if (IndexTupleHasNulls(itup))
+ {
+ res = NULL;
+ }
+ else
+ {
+ res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
+ gistdoinsert(r, itup, &res, &giststate);
+ }
- res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
- gistdoinsert(r, itup, &res, &giststate);
for (i = 0; i < r->rd_att->natts; i++)
if (compvec[i] == TRUE)
pfree(DatumGetPointer(datum[i]));
pfree(itup);
- pfree(compvec);
PG_RETURN_POINTER(res);
}
@@ -527,9 +456,7 @@ gistlayerinsert(Relation r, BlockNumber blkno,
/* key is modified, so old version must be deleted */
ItemPointerSet(&oldtid, blkno, child);
- DirectFunctionCall2(gistdelete,
- PointerGetDatum(r),
- PointerGetDatum(&oldtid));
+ gistdelete(r, &oldtid);
}
ret = INSERTED;
@@ -1416,29 +1343,31 @@ gistfreestack(GISTSTACK *s)
/*
-** remove an entry from a page
-*/
-Datum
-gistdelete(PG_FUNCTION_ARGS)
+ * Retail deletion of a single tuple.
+ *
+ * NB: this is no longer called externally, but is still needed by
+ * gistlayerinsert(). That dependency will have to be fixed if GIST
+ * is ever going to allow concurrent insertions.
+ */
+static void
+gistdelete(Relation r, ItemPointer tid)
{
- Relation r = (Relation) PG_GETARG_POINTER(0);
- ItemPointer tid = (ItemPointer) PG_GETARG_POINTER(1);
BlockNumber blkno;
OffsetNumber offnum;
Buffer buf;
Page page;
/*
- * Notes in ExecUtils:ExecOpenIndices() Also note that only vacuum
- * deletes index tuples now...
- *
- * RelationSetLockForWrite(r);
+ * Since GIST is not marked "amconcurrent" in pg_am, caller should
+ * have acquired exclusive lock on index relation. We need no locking
+ * here.
*/
blkno = ItemPointerGetBlockNumber(tid);
offnum = ItemPointerGetOffsetNumber(tid);
/* adjust any scans that will be affected by this deletion */
+ /* NB: this works only for scans in *this* backend! */
gistadjscans(r, GISTOP_DEL, blkno, offnum);
/* delete the index tuple */
@@ -1448,10 +1377,93 @@ gistdelete(PG_FUNCTION_ARGS)
PageIndexTupleDelete(page, offnum);
WriteBuffer(buf);
+}
- PG_RETURN_VOID();
+/*
+ * Bulk deletion of all index entries pointing to a set of heap tuples.
+ * The set of target tuples is specified via a callback routine that tells
+ * whether any given heap tuple (identified by ItemPointer) is being deleted.
+ *
+ * Result: a palloc'd struct containing statistical info for VACUUM displays.
+ */
+Datum
+gistbulkdelete(PG_FUNCTION_ARGS)
+{
+ Relation rel = (Relation) PG_GETARG_POINTER(0);
+ IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(1);
+ void *callback_state = (void *) PG_GETARG_POINTER(2);
+ IndexBulkDeleteResult *result;
+ BlockNumber num_pages;
+ double tuples_removed;
+ double num_index_tuples;
+ RetrieveIndexResult res;
+ IndexScanDesc iscan;
+
+ tuples_removed = 0;
+ num_index_tuples = 0;
+
+ /*
+ * Since GIST is not marked "amconcurrent" in pg_am, caller should
+ * have acquired exclusive lock on index relation. We need no locking
+ * here.
+ */
+
+ /*
+ * XXX generic implementation --- should be improved!
+ */
+
+ /* walk through the entire index */
+ iscan = index_beginscan(rel, false, 0, (ScanKey) NULL);
+
+ while ((res = index_getnext(iscan, ForwardScanDirection))
+ != (RetrieveIndexResult) NULL)
+ {
+ ItemPointer heapptr = &res->heap_iptr;
+
+ if (callback(heapptr, callback_state))
+ {
+ ItemPointer indexptr = &res->index_iptr;
+ BlockNumber blkno;
+ OffsetNumber offnum;
+ Buffer buf;
+ Page page;
+
+ blkno = ItemPointerGetBlockNumber(indexptr);
+ offnum = ItemPointerGetOffsetNumber(indexptr);
+
+ /* adjust any scans that will be affected by this deletion */
+ gistadjscans(rel, GISTOP_DEL, blkno, offnum);
+
+ /* delete the index tuple */
+ buf = ReadBuffer(rel, blkno);
+ page = BufferGetPage(buf);
+
+ PageIndexTupleDelete(page, offnum);
+
+ WriteBuffer(buf);
+
+ tuples_removed += 1;
+ }
+ else
+ num_index_tuples += 1;
+
+ pfree(res);
+ }
+
+ index_endscan(iscan);
+
+ /* return statistics */
+ num_pages = RelationGetNumberOfBlocks(rel);
+
+ result = (IndexBulkDeleteResult *) palloc(sizeof(IndexBulkDeleteResult));
+ result->num_pages = num_pages;
+ result->tuples_removed = tuples_removed;
+ result->num_index_tuples = num_index_tuples;
+
+ PG_RETURN_POINTER(result);
}
+
void
initGISTstate(GISTSTATE *giststate, Relation index)
{