Commit 1b8a219e authored by Tom Lane's avatar Tom Lane

Clean up non-reentrant interface for hash_seq/HashTableWalk, so that

starting a new hashtable search no longer clobbers any other search
active anywhere in the system.  Fix RelationCacheInvalidate() so that
it will not crash or go into an infinite loop if invoked recursively,
as for example by a second SI Reset message arriving while we are still
processing a prior one.
parent 25d88e4c
...@@ -262,7 +262,7 @@ _xl_init_rel_cache(void) ...@@ -262,7 +262,7 @@ _xl_init_rel_cache(void)
} }
static void static void
_xl_remove_hash_entry(XLogRelDesc **edata, int dummy) _xl_remove_hash_entry(XLogRelDesc **edata, Datum dummy)
{ {
XLogRelCacheEntry *hentry; XLogRelCacheEntry *hentry;
bool found; bool found;
...@@ -328,7 +328,7 @@ XLogCloseRelationCache(void) ...@@ -328,7 +328,7 @@ XLogCloseRelationCache(void)
if (!_xlrelarr) if (!_xlrelarr)
return; return;
HashTableWalk(_xlrelcache, (HashtFunc)_xl_remove_hash_entry, 0); HashTableWalk(_xlrelcache, (HashtFunc) _xl_remove_hash_entry, 0);
hash_destroy(_xlrelcache); hash_destroy(_xlrelcache);
free(_xlrelarr); free(_xlrelarr);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.13 2000/01/31 04:35:51 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.14 2001/01/02 04:33:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,23 +21,32 @@ ...@@ -21,23 +21,32 @@
/* ----------------------------------- /* -----------------------------------
* HashTableWalk * HashTableWalk
* *
* call function on every element in hashtable * call given function on every element in hashtable
* one extra argument (arg) may be supplied *
* one extra argument (arg) may be supplied
*
* NOTE: it is allowed for the given function to delete the hashtable entry
* it is passed. However, deleting any other element while the scan is
* in progress is UNDEFINED (see hash_seq functions). Also, if elements are
* added to the table while the scan is in progress, it is unspecified
* whether they will be visited by the scan or not.
* ----------------------------------- * -----------------------------------
*/ */
void void
HashTableWalk(HTAB *hashtable, HashtFunc function, int arg) HashTableWalk(HTAB *hashtable, HashtFunc function, Datum arg)
{ {
HASH_SEQ_STATUS status;
long *hashent; long *hashent;
void *data; void *data;
int keysize; int keysize;
hash_seq_init(&status, hashtable);
keysize = hashtable->hctl->keysize; keysize = hashtable->hctl->keysize;
hash_seq((HTAB *) NULL);
while ((hashent = hash_seq(hashtable)) != (long *) TRUE) while ((hashent = hash_seq_search(&status)) != (long *) TRUE)
{ {
if (hashent == NULL) if (hashent == NULL)
elog(FATAL, "error in HashTableWalk."); elog(FATAL, "error in HashTableWalk");
/* /*
* XXX the corresponding hash table insertion does NOT LONGALIGN * XXX the corresponding hash table insertion does NOT LONGALIGN
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.74 2000/12/22 00:51:54 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.75 2001/01/02 04:33:16 tgl Exp $
* *
* NOTES * NOTES
* Outside modules can create a lock table and acquire/release * Outside modules can create a lock table and acquire/release
...@@ -1772,6 +1772,7 @@ DumpAllLocks(void) ...@@ -1772,6 +1772,7 @@ DumpAllLocks(void)
int lockmethod = DEFAULT_LOCKMETHOD; int lockmethod = DEFAULT_LOCKMETHOD;
LOCKMETHODTABLE *lockMethodTable; LOCKMETHODTABLE *lockMethodTable;
HTAB *holderTable; HTAB *holderTable;
HASH_SEQ_STATUS status;
pid = getpid(); pid = getpid();
ShmemPIDLookup(pid, &location); ShmemPIDLookup(pid, &location);
...@@ -1791,8 +1792,8 @@ DumpAllLocks(void) ...@@ -1791,8 +1792,8 @@ DumpAllLocks(void)
if (proc->waitLock) if (proc->waitLock)
LOCK_PRINT("DumpAllLocks: waiting on", proc->waitLock, 0); LOCK_PRINT("DumpAllLocks: waiting on", proc->waitLock, 0);
hash_seq(NULL); hash_seq_init(&status, holderTable);
while ((holder = (HOLDER *) hash_seq(holderTable)) && while ((holder = (HOLDER *) hash_seq_search(&status)) &&
(holder != (HOLDER *) TRUE)) (holder != (HOLDER *) TRUE))
{ {
HOLDER_PRINT("DumpAllLocks", holder); HOLDER_PRINT("DumpAllLocks", holder);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.121 2000/12/22 23:12:06 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.122 2001/01/02 04:33:19 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -253,10 +253,10 @@ static void RelationClearRelation(Relation relation, bool rebuildIt); ...@@ -253,10 +253,10 @@ static void RelationClearRelation(Relation relation, bool rebuildIt);
#ifdef ENABLE_REINDEX_NAILED_RELATIONS #ifdef ENABLE_REINDEX_NAILED_RELATIONS
static void RelationReloadClassinfo(Relation relation); static void RelationReloadClassinfo(Relation relation);
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */ #endif /* ENABLE_REINDEX_NAILED_RELATIONS */
static void RelationFlushRelation(Relation *relationPtr, static void RelationFlushRelation(Relation relation);
int skipLocalRelations);
static Relation RelationNameCacheGetRelation(const char *relationName); static Relation RelationNameCacheGetRelation(const char *relationName);
static void RelationCacheAbortWalker(Relation *relationPtr, int dummy); static void RelationCacheInvalidateWalker(Relation *relationPtr, Datum listp);
static void RelationCacheAbortWalker(Relation *relationPtr, Datum dummy);
static void init_irels(void); static void init_irels(void);
static void write_irels(void); static void write_irels(void);
...@@ -1639,14 +1639,12 @@ RelationClearRelation(Relation relation, bool rebuildIt) ...@@ -1639,14 +1639,12 @@ RelationClearRelation(Relation relation, bool rebuildIt)
* we'd be unable to recover. * we'd be unable to recover.
*/ */
if (relation->rd_isnailed) if (relation->rd_isnailed)
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
{ {
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
RelationReloadClassinfo(relation); RelationReloadClassinfo(relation);
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */ #endif /* ENABLE_REINDEX_NAILED_RELATIONS */
return; return;
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
} }
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
/* /*
* Remove relation from hash tables * Remove relation from hash tables
...@@ -1774,26 +1772,15 @@ RelationClearRelation(Relation relation, bool rebuildIt) ...@@ -1774,26 +1772,15 @@ RelationClearRelation(Relation relation, bool rebuildIt)
* RelationFlushRelation * RelationFlushRelation
* *
* Rebuild the relation if it is open (refcount > 0), else blow it away. * Rebuild the relation if it is open (refcount > 0), else blow it away.
* If skipLocalRelations is TRUE, xact-local relations are ignored
* (which is useful when processing SI cache reset, since xact-local
* relations could not be targets of notifications from other backends).
*
* The peculiar calling convention (pointer to pointer to relation)
* is needed so that we can use this routine as a hash table walker.
* -------------------------------- * --------------------------------
*/ */
static void static void
RelationFlushRelation(Relation *relationPtr, RelationFlushRelation(Relation relation)
int skipLocalRelations)
{ {
Relation relation = *relationPtr;
bool rebuildIt; bool rebuildIt;
if (relation->rd_myxactonly) if (relation->rd_myxactonly)
{ {
if (skipLocalRelations)
return; /* don't touch local rels if so commanded */
/* /*
* Local rels should always be rebuilt, not flushed; the relcache * Local rels should always be rebuilt, not flushed; the relcache
* entry must live until RelationPurgeLocalRelation(). * entry must live until RelationPurgeLocalRelation().
...@@ -1878,7 +1865,7 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId) ...@@ -1878,7 +1865,7 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
RelationIdCacheLookup(relationId, relation); RelationIdCacheLookup(relationId, relation);
if (PointerIsValid(relation)) if (PointerIsValid(relation))
RelationFlushRelation(&relation, false); RelationFlushRelation(relation);
} }
#if NOT_USED #if NOT_USED
...@@ -1900,35 +1887,12 @@ RelationFlushIndexes(Relation *r, ...@@ -1900,35 +1887,12 @@ RelationFlushIndexes(Relation *r,
if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */ if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */
(!OidIsValid(accessMethodId) || (!OidIsValid(accessMethodId) ||
relation->rd_rel->relam == accessMethodId)) relation->rd_rel->relam == accessMethodId))
RelationFlushRelation(&relation, false); RelationFlushRelation(relation);
} }
#endif #endif
#if NOT_USED
void
RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
{
/*
* 25 aug 1992: mao commented out the ht walk below. it should be
* doing the right thing, in theory, but flushing reldescs for index
* relations apparently doesn't work. we want to cut 4.0.1, and i
* don't want to introduce new bugs. this code never executed before,
* so i'm turning it off for now. after the release is cut, i'll fix
* this up.
*
* 20 nov 1999: this code has still never done anything, so I'm cutting
* the routine out of the system entirely. tgl
*/
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes,
accessMethodId);
}
#endif
/* /*
* RelationCacheInvalidate * RelationCacheInvalidate
* Blow away cached relation descriptors that have zero reference counts, * Blow away cached relation descriptors that have zero reference counts,
...@@ -1938,12 +1902,59 @@ RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId) ...@@ -1938,12 +1902,59 @@ RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
* so we do not touch transaction-local relations; they cannot be targets * so we do not touch transaction-local relations; they cannot be targets
* of cross-backend SI updates (and our own updates now go through a * of cross-backend SI updates (and our own updates now go through a
* separate linked list that isn't limited by the SI message buffer size). * separate linked list that isn't limited by the SI message buffer size).
*
* We do this in two phases: the first pass deletes deletable items, and
* the second one rebuilds the rebuildable items. This is essential for
* safety, because HashTableWalk only copes with concurrent deletion of
* the element it is currently visiting. If a second SI overflow were to
* occur while we are walking the table, resulting in recursive entry to
* this routine, we could crash because the inner invocation blows away
* the entry next to be visited by the outer scan. But this way is OK,
* because (a) during the first pass we won't process any more SI messages,
* so HashTableWalk will complete safely; (b) during the second pass we
* only hold onto pointers to nondeletable entries.
*/ */
void void
RelationCacheInvalidate(void) RelationCacheInvalidate(void)
{ {
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation, List *rebuildList = NIL;
(int) true); List *l;
/* Phase 1 */
HashTableWalk(RelationNameCache,
(HashtFunc) RelationCacheInvalidateWalker,
PointerGetDatum(&rebuildList));
/* Phase 2: rebuild the items found to need rebuild in phase 1 */
foreach (l, rebuildList)
{
Relation relation = (Relation) lfirst(l);
RelationClearRelation(relation, true);
}
freeList(rebuildList);
}
static void
RelationCacheInvalidateWalker(Relation *relationPtr, Datum listp)
{
Relation relation = *relationPtr;
List **rebuildList = (List **) DatumGetPointer(listp);
/* We can ignore xact-local relations, since they are never SI targets */
if (relation->rd_myxactonly)
return;
if (RelationHasReferenceCountZero(relation))
{
/* Delete this entry immediately */
RelationClearRelation(relation, false);
}
else
{
/* Add entry to list of stuff to rebuild in second pass */
*rebuildList = lcons(relation, *rebuildList);
}
} }
/* /*
...@@ -1962,12 +1973,13 @@ RelationCacheInvalidate(void) ...@@ -1962,12 +1973,13 @@ RelationCacheInvalidate(void)
void void
RelationCacheAbort(void) RelationCacheAbort(void)
{ {
HashTableWalk(RelationNameCache, (HashtFunc) RelationCacheAbortWalker, HashTableWalk(RelationNameCache,
(HashtFunc) RelationCacheAbortWalker,
0); 0);
} }
static void static void
RelationCacheAbortWalker(Relation *relationPtr, int dummy) RelationCacheAbortWalker(Relation *relationPtr, Datum dummy)
{ {
Relation relation = *relationPtr; Relation relation = *relationPtr;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.32 2000/06/28 03:32:34 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.33 2001/01/02 04:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -681,50 +681,54 @@ hash_search(HTAB *hashp, ...@@ -681,50 +681,54 @@ hash_search(HTAB *hashp,
} }
/* /*
* hash_seq -- sequentially search through hash table and return * hash_seq_init/_search
* all the elements one by one, return NULL on error and * Sequentially search through hash table and return
* return TRUE in the end. * all the elements one by one, return NULL on error and
* return (long *) TRUE in the end.
* *
* NOTE: caller may delete the returned element before continuing the scan.
* However, deleting any other element while the scan is in progress is
* UNDEFINED (it might be the one that curIndex is pointing at!). Also,
* if elements are added to the table while the scan is in progress, it is
* unspecified whether they will be visited by the scan or not.
*/ */
void
hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
{
status->hashp = hashp;
status->curBucket = 0;
status->curIndex = INVALID_INDEX;
}
long * long *
hash_seq(HTAB *hashp) hash_seq_search(HASH_SEQ_STATUS *status)
{ {
static long curBucket = 0; HTAB *hashp = status->hashp;
static BUCKET_INDEX curIndex; HHDR *hctl = hashp->hctl;
ELEMENT *curElem;
long segment_num;
long segment_ndx;
SEGMENT segp;
HHDR *hctl;
if (hashp == NULL) while (status->curBucket <= hctl->max_bucket)
{ {
long segment_num;
long segment_ndx;
SEGMENT segp;
/* if (status->curIndex != INVALID_INDEX)
* reset static state
*/
curBucket = 0;
curIndex = INVALID_INDEX;
return (long *) NULL;
}
hctl = hashp->hctl;
while (curBucket <= hctl->max_bucket)
{
if (curIndex != INVALID_INDEX)
{ {
curElem = GET_BUCKET(hashp, curIndex); /* Continuing scan of curBucket... */
curIndex = curElem->next; ELEMENT *curElem;
if (curIndex == INVALID_INDEX) /* end of this bucket */
++curBucket; curElem = GET_BUCKET(hashp, status->curIndex);
status->curIndex = curElem->next;
if (status->curIndex == INVALID_INDEX) /* end of this bucket */
++status->curBucket;
return &(curElem->key); return &(curElem->key);
} }
/* /*
* initialize the search within this bucket. * initialize the search within this bucket.
*/ */
segment_num = curBucket >> hctl->sshift; segment_num = status->curBucket >> hctl->sshift;
segment_ndx = MOD(curBucket, hctl->ssize); segment_ndx = MOD(status->curBucket, hctl->ssize);
/* /*
* first find the right segment in the table directory. * first find the right segment in the table directory.
...@@ -742,10 +746,10 @@ hash_seq(HTAB *hashp) ...@@ -742,10 +746,10 @@ hash_seq(HTAB *hashp)
* directory of valid stuff. if there are elements in the bucket * directory of valid stuff. if there are elements in the bucket
* chains that point to the freelist we're in big trouble. * chains that point to the freelist we're in big trouble.
*/ */
curIndex = segp[segment_ndx]; status->curIndex = segp[segment_ndx];
if (curIndex == INVALID_INDEX) /* empty bucket */ if (status->curIndex == INVALID_INDEX) /* empty bucket */
++curBucket; ++status->curBucket;
} }
return (long *) TRUE; /* out of buckets */ return (long *) TRUE; /* out of buckets */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.37 2000/06/28 03:32:50 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.38 2001/01/02 04:33:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -55,8 +55,6 @@ ...@@ -55,8 +55,6 @@
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/portal.h" #include "utils/portal.h"
static void CollectNamedPortals(Portal *portalP, int destroy);
/* ---------------- /* ----------------
* Global state * Global state
* ---------------- * ----------------
...@@ -120,65 +118,11 @@ do { \ ...@@ -120,65 +118,11 @@ do { \
static MemoryContext PortalMemory = NULL; static MemoryContext PortalMemory = NULL;
/* ----------------------------------------------------------------
* private internal support routines
* ----------------------------------------------------------------
*/
/*
* This routine is used to collect all portals created in this xaction
* and then destroy them. There is a little trickiness required as a
* result of the dynamic hashing interface to getting every hash entry
* sequentially. Its use of static variables requires that we get every
* entry *before* we destroy anything (destroying updates the hashtable
* and screws up the sequential walk of the table). -mer 17 Aug 1992
*/
static void
CollectNamedPortals(Portal *portalP, int destroy)
{
static Portal *portalList = (Portal *) NULL;
static int listIndex = 0;
static int maxIndex = 9;
if (portalList == (Portal *) NULL)
portalList = (Portal *) malloc(10 * sizeof(Portal));
if (destroy != 0)
{
int i;
for (i = 0; i < listIndex; i++)
PortalDrop(&portalList[i]);
listIndex = 0;
}
else
{
Assert(portalP);
Assert(*portalP);
portalList[listIndex] = *portalP;
listIndex++;
if (listIndex == maxIndex)
{
portalList = (Portal *)
realloc(portalList, (maxIndex + 11) * sizeof(Portal));
maxIndex += 10;
}
}
return;
}
void
AtEOXact_portals()
{
HashTableWalk(PortalHashTable, (HashtFunc) CollectNamedPortals, 0);
CollectNamedPortals(NULL, 1);
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* public portal interface functions * public portal interface functions
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* /*
* EnablePortalManager * EnablePortalManager
* Enables the portal management module at backend startup. * Enables the portal management module at backend startup.
...@@ -337,6 +281,9 @@ CreatePortal(char *name) ...@@ -337,6 +281,9 @@ CreatePortal(char *name)
* Exceptions: * Exceptions:
* BadState if called when disabled. * BadState if called when disabled.
* BadArg if portal is invalid. * BadArg if portal is invalid.
*
* Note peculiar calling convention: pass a pointer to a portal pointer.
* This is mainly so that this routine can be used as a hashtable walker.
*/ */
void void
PortalDrop(Portal *portalP) PortalDrop(Portal *portalP)
...@@ -360,6 +307,15 @@ PortalDrop(Portal *portalP) ...@@ -360,6 +307,15 @@ PortalDrop(Portal *portalP)
pfree(portal); pfree(portal);
} }
/*
* Destroy all portals created in the current transaction (ie, all of them).
*/
void
AtEOXact_portals(void)
{
HashTableWalk(PortalHashTable, (HashtFunc) PortalDrop, 0);
}
/* /*
* PortalGetHeapMemory * PortalGetHeapMemory
* Returns heap memory context for a given portal. * Returns heap memory context for a given portal.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: hasht.h,v 1.10 2000/01/31 04:35:55 tgl Exp $ * $Id: hasht.h,v 1.11 2001/01/02 04:33:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
#include "utils/hsearch.h" #include "utils/hsearch.h"
typedef void (*HashtFunc) (void *hashitem, int arg); typedef void (*HashtFunc) (void *hashitem, Datum arg);
extern void HashTableWalk(HTAB *hashtable, HashtFunc function, int arg); extern void HashTableWalk(HTAB *hashtable, HashtFunc function, Datum arg);
#endif /* HASHT_H */ #endif /* HASHT_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: hsearch.h,v 1.16 2000/06/28 03:33:33 tgl Exp $ * $Id: hsearch.h,v 1.17 2001/01/02 04:33:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -131,6 +131,14 @@ typedef enum ...@@ -131,6 +131,14 @@ typedef enum
HASH_REMOVE_SAVED HASH_REMOVE_SAVED
} HASHACTION; } HASHACTION;
/* hash_seq status (should be considered an opaque type by callers) */
typedef struct
{
HTAB *hashp;
long curBucket;
BUCKET_INDEX curIndex;
} HASH_SEQ_STATUS;
/* /*
* prototypes from functions in dynahash.c * prototypes from functions in dynahash.c
*/ */
...@@ -139,7 +147,8 @@ extern void hash_destroy(HTAB *hashp); ...@@ -139,7 +147,8 @@ extern void hash_destroy(HTAB *hashp);
extern void hash_stats(char *where, HTAB *hashp); extern void hash_stats(char *where, HTAB *hashp);
extern long *hash_search(HTAB *hashp, char *keyPtr, HASHACTION action, extern long *hash_search(HTAB *hashp, char *keyPtr, HASHACTION action,
bool *foundPtr); bool *foundPtr);
extern long *hash_seq(HTAB *hashp); extern void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp);
extern long *hash_seq_search(HASH_SEQ_STATUS *status);
extern long hash_estimate_size(long num_entries, long keysize, long datasize); extern long hash_estimate_size(long num_entries, long keysize, long datasize);
extern long hash_select_dirsize(long num_entries); extern long hash_select_dirsize(long num_entries);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment