diff options
Diffstat (limited to 'src/backend/access/hash/hash.c')
-rw-r--r-- | src/backend/access/hash/hash.c | 317 |
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) { |