summaryrefslogtreecommitdiff
path: root/src/backend/access/hash/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/hash/hash.c')
-rw-r--r--src/backend/access/hash/hash.c317
1 files changed, 148 insertions, 169 deletions
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 9617fcc33a6..9b0e6cf28ee 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.51 2001/05/07 00:43:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.52 2001/07/15 22:48:15 tgl Exp $
*
* NOTES
* This file contains only the public interface routines.
@@ -21,13 +21,27 @@
#include "access/genam.h"
#include "access/hash.h"
#include "access/heapam.h"
+#include "access/xlogutils.h"
#include "catalog/index.h"
#include "executor/executor.h"
#include "miscadmin.h"
+
bool BuildingHash = false;
-#include "access/xlogutils.h"
+
+/* Working state for hashbuild and its callback */
+typedef struct
+{
+ double indtuples;
+} HashBuildState;
+
+static void hashbuildCallback(Relation index,
+ HeapTuple htup,
+ Datum *attdata,
+ char *nulls,
+ bool tupleIsAlive,
+ void *state);
/*
@@ -44,161 +58,32 @@ hashbuild(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;
- HashItem hitem;
- Node *pred = indexInfo->ii_Predicate;
-
-#ifndef OMIT_PARTIAL_INDEX
- TupleTable tupleTable;
- TupleTableSlot *slot;
+ double reltuples;
+ HashBuildState buildstate;
-#endif
- ExprContext *econtext;
- InsertIndexResult res = NULL;
-
- /* note that this is a new hash */
+ /* set flag to disable locking */
BuildingHash = true;
- /* initialize the hash index metadata page (if this is a new index) */
- if (oldPred == NULL)
- _hash_metapinit(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
+ * We expect to be called exactly once for any index relation. If
+ * that's not the case, big trouble's what we have.
*/
-#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 */
-
- /* build the index */
- nhtups = nitups = 0.0;
-
- /* start a heap scan */
- hscan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
+ if (RelationGetNumberOfBlocks(index) != 0)
+ elog(ERROR, "%s already contains data",
+ RelationGetRelationName(index));
- while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
- {
- MemoryContextReset(econtext->ecxt_per_tuple_memory);
+ /* initialize the hash index metadata page */
+ _hash_metapinit(index);
- 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);
-
- /* form an index tuple and point it at the heap tuple */
- itup = index_formtuple(itupdesc, attdata, nulls);
-
- /*
- * If the single index key is null, we don't insert it into the
- * index. Hash tables support scans on '='. Relational algebra
- * says that A = B returns null if either A or B is null. This
- * means that no qualification used in an index scan could ever
- * return true on a null attribute. It also means that indices
- * can't be used by ISNULL or NOTNULL scans, but that's an
- * artifact of the strategy map architecture chosen in 1986, not
- * of the way nulls are handled here.
- */
-
- if (IndexTupleHasNulls(itup))
- {
- pfree(itup);
- continue;
- }
-
- itup->t_tid = htup->t_self;
- hitem = _hash_formitem(itup);
-
- res = _hash_doinsert(index, hitem);
-
- pfree(hitem);
- pfree(itup);
- pfree(res);
- }
+ /* build the index */
+ buildstate.indtuples = 0;
- /* okay, all heap tuples are indexed */
- heap_endscan(hscan);
+ /* do the heap scan */
+ reltuples = IndexBuildHeapScan(heap, index, indexInfo,
+ hashbuildCallback, (void *) &buildstate);
-#ifndef OMIT_PARTIAL_INDEX
- if (pred != NULL || oldPred != NULL)
- ExecDropTupleTable(tupleTable, true);
-#endif /* OMIT_PARTIAL_INDEX */
- FreeExprContext(econtext);
+ /* all done */
+ BuildingHash = false;
/*
* Since we just counted the tuples in the heap, we update its stats
@@ -218,23 +103,54 @@ hashbuild(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);
}
- /* all done */
- BuildingHash = false;
-
PG_RETURN_VOID();
}
/*
+ * Per-tuple callback from IndexBuildHeapScan
+ */
+static void
+hashbuildCallback(Relation index,
+ HeapTuple htup,
+ Datum *attdata,
+ char *nulls,
+ bool tupleIsAlive,
+ void *state)
+{
+ HashBuildState *buildstate = (HashBuildState *) state;
+ IndexTuple itup;
+ HashItem hitem;
+ InsertIndexResult res;
+
+ /* form an index tuple and point it at the heap tuple */
+ itup = index_formtuple(RelationGetDescr(index), attdata, nulls);
+ itup->t_tid = htup->t_self;
+
+ /* Hash indexes don't index nulls, see notes in hashinsert */
+ if (IndexTupleHasNulls(itup))
+ {
+ pfree(itup);
+ return;
+ }
+
+ hitem = _hash_formitem(itup);
+
+ res = _hash_doinsert(index, hitem);
+
+ if (res)
+ pfree(res);
+
+ buildstate->indtuples += 1;
+
+ pfree(hitem);
+ pfree(itup);
+}
+
+/*
* hashinsert() -- insert an index tuple into a hash table.
*
* Hash on the index tuple's key, find the appropriate location
@@ -248,10 +164,8 @@ hashinsert(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;
HashItem hitem;
@@ -261,8 +175,21 @@ hashinsert(PG_FUNCTION_ARGS)
itup = index_formtuple(RelationGetDescr(rel), datum, nulls);
itup->t_tid = *ht_ctid;
+ /*
+ * If the single index key is null, we don't insert it into the
+ * index. Hash tables support scans on '='. Relational algebra
+ * says that A = B returns null if either A or B is null. This
+ * means that no qualification used in an index scan could ever
+ * return true on a null attribute. It also means that indices
+ * can't be used by ISNULL or NOTNULL scans, but that's an
+ * artifact of the strategy map architecture chosen in 1986, not
+ * of the way nulls are handled here.
+ */
if (IndexTupleHasNulls(itup))
+ {
+ pfree(itup);
PG_RETURN_POINTER((InsertIndexResult) NULL);
+ }
hitem = _hash_formitem(itup);
@@ -471,22 +398,74 @@ hashrestrpos(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
-/* stubs */
+/*
+ * 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
-hashdelete(PG_FUNCTION_ARGS)
+hashbulkdelete(PG_FUNCTION_ARGS)
{
Relation rel = (Relation) PG_GETARG_POINTER(0);
- ItemPointer tid = (ItemPointer) PG_GETARG_POINTER(1);
+ 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;
- /* adjust any active scans that will be affected by this deletion */
- _hash_adjscans(rel, tid);
+ tuples_removed = 0;
+ num_index_tuples = 0;
- /* delete the data from the page */
- _hash_pagedel(rel, tid);
+ /*
+ * XXX generic implementation --- should be improved!
+ */
- PG_RETURN_VOID();
+ /* 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;
+
+ /* adjust any active scans that will be affected by deletion */
+ /* (namely, my own scan) */
+ _hash_adjscans(rel, indexptr);
+
+ /* delete the data from the page */
+ _hash_pagedel(rel, indexptr);
+
+ 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
hash_redo(XLogRecPtr lsn, XLogRecord *record)
{