about summary refs log tree commit diff
path: root/db2/hash
diff options
context:
space:
mode:
Diffstat (limited to 'db2/hash')
-rw-r--r--db2/hash/hash.c1151
-rw-r--r--db2/hash/hash_auto.c161
-rw-r--r--db2/hash/hash_debug.c92
-rw-r--r--db2/hash/hash_dup.c295
-rw-r--r--db2/hash/hash_page.c1088
-rw-r--r--db2/hash/hash_rec.c281
-rw-r--r--db2/hash/hash_stat.c37
7 files changed, 1524 insertions, 1581 deletions
diff --git a/db2/hash/hash.c b/db2/hash/hash.c
index 0265f19659..0d202fce20 100644
--- a/db2/hash/hash.c
+++ b/db2/hash/hash.c
@@ -47,7 +47,7 @@
 #include "config.h"
 
 #ifndef lint
-static const char sccsid[] = "@(#)hash.c	10.45 (Sleepycat) 5/11/98";
+static const char sccsid[] = "@(#)hash.c	10.63 (Sleepycat) 12/11/98";
 #endif /* not lint */
 
 #ifndef NO_SYSTEM_INCLUDES
@@ -64,23 +64,23 @@ static const char sccsid[] = "@(#)hash.c	10.45 (Sleepycat) 5/11/98";
 #include "db_am.h"
 #include "db_ext.h"
 #include "hash.h"
+#include "btree.h"
 #include "log.h"
+#include "db_shash.h"
+#include "lock.h"
+#include "lock_ext.h"
 
 static int  __ham_c_close __P((DBC *));
 static int  __ham_c_del __P((DBC *, u_int32_t));
+static int  __ham_c_destroy __P((DBC *));
 static int  __ham_c_get __P((DBC *, DBT *, DBT *, u_int32_t));
 static int  __ham_c_put __P((DBC *, DBT *, DBT *, u_int32_t));
-static int  __ham_c_init __P((DB *, DB_TXN *, DBC **));
-static int  __ham_cursor __P((DB *, DB_TXN *, DBC **));
 static int  __ham_delete __P((DB *, DB_TXN *, DBT *, u_int32_t));
-static int  __ham_dup_return __P((HTAB *, HASH_CURSOR *, DBT *, u_int32_t));
-static int  __ham_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
-static void __ham_init_htab __P((HTAB *, u_int32_t, u_int32_t));
-static int  __ham_lookup __P((HTAB *,
-		HASH_CURSOR *, const DBT *, u_int32_t, db_lockmode_t));
-static int  __ham_overwrite __P((HTAB *, HASH_CURSOR *, DBT *));
-static int  __ham_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
-static int  __ham_sync __P((DB *, u_int32_t));
+static int  __ham_dup_return __P((DBC *, DBT *, u_int32_t));
+static int  __ham_expand_table __P((DBC *));
+static void __ham_init_htab __P((DBC *, u_int32_t, u_int32_t));
+static int  __ham_lookup __P((DBC *, const DBT *, u_int32_t, db_lockmode_t));
+static int  __ham_overwrite __P((DBC *, DBT *));
 
 /************************** INTERFACE ROUTINES ***************************/
 /* OPEN/CLOSE */
@@ -96,65 +96,53 @@ __ham_open(dbp, dbinfo)
 	DB_INFO *dbinfo;
 {
 	DB_ENV *dbenv;
-	DBC *curs;
-	HTAB *hashp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	int file_existed, ret;
 
+	dbc = NULL;
 	dbenv = dbp->dbenv;
 
-	if ((hashp = (HTAB *)__db_calloc(1, sizeof(HTAB))) == NULL)
-		return (ENOMEM);
-	hashp->dbp = dbp;
-
 	/* Set the hash function if specified by the user. */
 	if (dbinfo != NULL && dbinfo->h_hash != NULL)
-		hashp->hash = dbinfo->h_hash;
+		dbp->h_hash = dbinfo->h_hash;
 
 	/*
-	 * Initialize the remaining fields of the dbp.  The type, close and
-	 * fd functions are all set in db_open.
+	 * Initialize the remaining fields of the dbp.  The only function
+	 * that differs from the default set is __ham_stat().
 	 */
-	dbp->internal = hashp;
-	dbp->cursor = __ham_cursor;
+	dbp->internal = NULL;
+	dbp->am_close = __ham_close;
 	dbp->del = __ham_delete;
-	dbp->get = __ham_get;
-	dbp->put = __ham_put;
-	dbp->sync = __ham_sync;
-
-	/* If locking is turned on, lock the meta data page. */
-	if (F_ISSET(dbp, DB_AM_LOCKING)) {
-		dbp->lock.pgno = BUCKET_INVALID;
-		if ((ret = lock_get(dbenv->lk_info, dbp->locker,
-		    0, &dbp->lock_dbt, DB_LOCK_READ, &hashp->hlock)) != 0) {
-			if (ret < 0)
-				ret = EAGAIN;
-			goto out;
-		}
-	}
+	dbp->stat = __ham_stat;
+
+	/* Get a cursor we can use for the rest of this function. */
+	if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
+		goto out;
+
+	hcp = (HASH_CURSOR *)dbc->internal;
+	GET_META(dbp, hcp, ret);
+	if (ret != 0)
+		goto out;
 
 	/*
-	 * Now, we can try to read the meta-data page and figure out
-	 * if we set up locking and get the meta-data page properly.
 	 * If this is a new file, initialize it, and put it back dirty.
 	 */
-	if ((ret = __ham_get_page(hashp->dbp, 0, (PAGE **)&hashp->hdr)) != 0)
-		goto out;
 
-	/* Initialize the hashp structure */
-	if (hashp->hdr->magic == DB_HASHMAGIC) {
+	/* Initialize the hdr structure */
+	if (hcp->hdr->magic == DB_HASHMAGIC) {
 		file_existed = 1;
 		/* File exists, verify the data in the header. */
-		if (hashp->hash == NULL)
-			hashp->hash =
-			    hashp->hdr->version < 5 ? __ham_func4 : __ham_func5;
-		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) !=
-		    hashp->hdr->h_charkey) {
-			__db_err(hashp->dbp->dbenv,
-			    "hash: incompatible hash function");
+		if (dbp->h_hash == NULL)
+			dbp->h_hash =
+			    hcp->hdr->version < 5 ? __ham_func4 : __ham_func5;
+		if (dbp->h_hash(CHARKEY, sizeof(CHARKEY)) !=
+		    hcp->hdr->h_charkey) {
+			__db_err(dbp->dbenv, "hash: incompatible hash function");
 			ret = EINVAL;
 			goto out;
 		}
-		if (F_ISSET(hashp->hdr, DB_HASH_DUP))
+		if (F_ISSET(hcp->hdr, DB_HASH_DUP))
 			F_SET(dbp, DB_AM_DUP);
 	} else {
 		/*
@@ -163,59 +151,27 @@ __ham_open(dbp, dbinfo)
 		 */
 		file_existed = 0;
 		if (F_ISSET(dbp, DB_AM_LOCKING) &&
-		    ((ret = lock_put(dbenv->lk_info, hashp->hlock)) != 0 ||
-		    (ret = lock_get(dbenv->lk_info, dbp->locker, 0,
-		        &dbp->lock_dbt, DB_LOCK_WRITE, &hashp->hlock)) != 0)) {
+		    ((ret = lock_put(dbenv->lk_info, hcp->hlock)) != 0 ||
+		    (ret = lock_get(dbenv->lk_info, dbc->locker, 0,
+		        &dbc->lock_dbt, DB_LOCK_WRITE, &hcp->hlock)) != 0)) {
 			if (ret < 0)
 				ret = EAGAIN;
 			goto out;
 		}
 
-		__ham_init_htab(hashp,
-		    dbinfo != NULL ? dbinfo->h_nelem : 0,
+		__ham_init_htab(dbc, dbinfo != NULL ? dbinfo->h_nelem : 0,
 		    dbinfo != NULL ? dbinfo->h_ffactor : 0);
 		if (F_ISSET(dbp, DB_AM_DUP))
-			F_SET(hashp->hdr, DB_HASH_DUP);
-		if ((ret = __ham_dirty_page(hashp, (PAGE *)hashp->hdr)) != 0)
+			F_SET(hcp->hdr, DB_HASH_DUP);
+		if ((ret = __ham_dirty_page(dbp, (PAGE *)hcp->hdr)) != 0)
 			goto out;
 	}
 
-	/* Initialize the default cursor. */
-	__ham_c_init(dbp, NULL, &curs);
-	TAILQ_INSERT_TAIL(&dbp->curs_queue, curs, links);
-
-	/* Allocate memory for our split buffer. */
-	if ((hashp->split_buf = (PAGE *)__db_malloc(dbp->pgsize)) == NULL) {
-		ret = ENOMEM;
-		goto out;
-	}
-
-#ifdef NO_STATISTICS_FOR_DB_ERR
-	__db_err(dbp->dbenv,
-	    "%s%lx\n%s%ld\n%s%ld\n%s%ld\n%s%ld\n%s0x%lx\n%s0x%lx\n%s%ld\n%s%ld\n%s0x%lx",
-	    "TABLE POINTER   ", (long)hashp,
-	    "BUCKET SIZE     ", (long)hashp->hdr->pagesize,
-	    "FILL FACTOR     ", (long)hashp->hdr->ffactor,
-	    "MAX BUCKET      ", (long)hashp->hdr->max_bucket,
-	    "OVFL POINT      ", (long)hashp->hdr->ovfl_point,
-	    "LAST FREED      ", (long)hashp->hdr->last_freed,
-	    "HIGH MASK       ", (long)hashp->hdr->high_mask,
-	    "LOW  MASK       ", (long)hashp->hdr->low_mask,
-	    "NELEM           ", (long)hashp->hdr->nelem,
-	    "FLAGS           ", (long)hashp->hdr->flags);
-#endif
-
 	/* Release the meta data page */
-	(void)__ham_put_page(hashp->dbp, (PAGE *)hashp->hdr, 0);
-	if (F_ISSET(dbp, DB_AM_LOCKING) &&
-	    (ret = lock_put(dbenv->lk_info, hashp->hlock)) != 0) {
-		if (ret < 0)
-			ret = EAGAIN;
+	RELEASE_META(dbp, hcp);
+	if ((ret  = dbc->c_close(dbc)) != 0)
 		goto out;
-	}
 
-	hashp->hlock = 0;
-	hashp->hdr = NULL;
 	/* Sync the file so that we know that the meta data goes to disk. */
 	if (!file_existed && (ret = dbp->sync(dbp, 0)) != 0)
 		goto out;
@@ -232,27 +188,8 @@ int
 __ham_close(dbp)
 	DB *dbp;
 {
-	HTAB *hashp;
-	int ret, t_ret;
-
-	DEBUG_LWRITE(dbp, NULL, "ham_close", NULL, NULL, 0);
-	hashp = (HTAB *)dbp->internal;
-	ret = 0;
-
-	/* Free the split page. */
-	if (hashp->split_buf)
-		FREE(hashp->split_buf, dbp->pgsize);
-
-	if (hashp->hdr && (t_ret = __ham_put_page(hashp->dbp,
-	    (PAGE *)hashp->hdr, 0)) != 0 && ret == 0)
-		ret = t_ret;
-	if (hashp->hlock && (t_ret = lock_put(hashp->dbp->dbenv->lk_info,
-	    hashp->hlock)) != 0 && ret == 0)
-		ret = t_ret;
-
-	FREE(hashp, sizeof(HTAB));
-	dbp->internal = NULL;
-	return (ret);
+	COMPQUIET(dbp, NULL);
+	return (0);
 }
 
 /************************** LOCAL CREATION ROUTINES **********************/
@@ -260,408 +197,204 @@ __ham_close(dbp)
  * Returns 0 on No Error
  */
 static void
-__ham_init_htab(hashp, nelem, ffactor)
-	HTAB *hashp;
+__ham_init_htab(dbc, nelem, ffactor)
+	DBC *dbc;
 	u_int32_t nelem, ffactor;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	int32_t l2, nbuckets;
 
-	memset(hashp->hdr, 0, sizeof(HASHHDR));
-	hashp->hdr->ffactor = ffactor;
-	hashp->hdr->pagesize = hashp->dbp->pgsize;
-	ZERO_LSN(hashp->hdr->lsn);
-	hashp->hdr->magic = DB_HASHMAGIC;
-	hashp->hdr->version = DB_HASHVERSION;
-	if (hashp->hash == NULL)
-		hashp->hash =
-		    hashp->hdr->version < 5 ? __ham_func4 : __ham_func5;
-	hashp->hdr->h_charkey = hashp->hash(CHARKEY, sizeof(CHARKEY));
-	if (nelem != 0 && hashp->hdr->ffactor != 0) {
-		nelem = (nelem - 1) / hashp->hdr->ffactor + 1;
+	hcp = (HASH_CURSOR *)dbc->internal;
+	dbp = dbc->dbp;
+	memset(hcp->hdr, 0, sizeof(HASHHDR));
+	hcp->hdr->ffactor = ffactor;
+	hcp->hdr->pagesize = dbp->pgsize;
+	ZERO_LSN(hcp->hdr->lsn);
+	hcp->hdr->magic = DB_HASHMAGIC;
+	hcp->hdr->version = DB_HASHVERSION;
+
+	if (dbp->h_hash == NULL)
+		dbp->h_hash = hcp->hdr->version < 5 ? __ham_func4 : __ham_func5;
+	hcp->hdr->h_charkey = dbp->h_hash(CHARKEY, sizeof(CHARKEY));
+	if (nelem != 0 && hcp->hdr->ffactor != 0) {
+		nelem = (nelem - 1) / hcp->hdr->ffactor + 1;
 		l2 = __db_log2(nelem > 2 ? nelem : 2);
 	} else
 		l2 = 2;
 
 	nbuckets = 1 << l2;
 
-	hashp->hdr->ovfl_point = l2;
-	hashp->hdr->last_freed = PGNO_INVALID;
+	hcp->hdr->ovfl_point = l2;
+	hcp->hdr->last_freed = PGNO_INVALID;
 
-	hashp->hdr->max_bucket = hashp->hdr->high_mask = nbuckets - 1;
-	hashp->hdr->low_mask = (nbuckets >> 1) - 1;
-	memcpy(hashp->hdr->uid, hashp->dbp->lock.fileid, DB_FILE_ID_LEN);
+	hcp->hdr->max_bucket = hcp->hdr->high_mask = nbuckets - 1;
+	hcp->hdr->low_mask = (nbuckets >> 1) - 1;
+	memcpy(hcp->hdr->uid, dbp->fileid, DB_FILE_ID_LEN);
 }
 
-/********************** DESTROY/CLOSE ROUTINES ************************/
-
-
-/*
- * Write modified pages to disk
- *
- * Returns:
- *	 0 == OK
- *	-1 ERROR
- */
 static int
-__ham_sync(dbp, flags)
-	DB *dbp;
-	u_int32_t flags;
-{
-	int ret;
-
-	DEBUG_LWRITE(dbp, NULL, "ham_sync", NULL, NULL, flags);
-	if ((ret = __db_syncchk(dbp, flags)) != 0)
-		return (ret);
-	if (F_ISSET(dbp, DB_AM_RDONLY))
-		return (0);
-
-	if ((ret = memp_fsync(dbp->mpf)) == DB_INCOMPLETE)
-		ret = 0;
-
-	return (ret);
-}
-
-/*******************************SEARCH ROUTINES *****************************/
-/*
- * All the access routines return
- *
- * Returns:
- *	 0 on SUCCESS
- *	 1 to indicate an external ERROR (i.e. key not found, etc)
- *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
- */
-
-static int
-__ham_get(dbp, txn, key, data, flags)
+__ham_delete(dbp, txn, key, flags)
 	DB *dbp;
 	DB_TXN *txn;
 	DBT *key;
-	DBT *data;
 	u_int32_t flags;
 {
-	DB *ldbp;
-	HTAB *hashp;
+	DBC *dbc;
 	HASH_CURSOR *hcp;
-	int ret, t_ret;
+	int ret, tret;
 
-	DEBUG_LREAD(dbp, txn, "ham_get", key, NULL, flags);
-	if ((ret = __db_getchk(dbp, key, data, flags)) != 0)
-		return (ret);
+	DB_PANIC_CHECK(dbp);
 
-	ldbp = dbp;
-	if (F_ISSET(dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(dbp, __ham_hdup, &ldbp)) != 0)
+	if ((ret =
+	    __db_delchk(dbp, key, flags, F_ISSET(dbp, DB_AM_RDONLY))) != 0)
 		return (ret);
 
-	hashp = (HTAB *)ldbp->internal;
-	SET_LOCKER(ldbp, txn);
-	GET_META(ldbp, hashp);
-
-	hashp->hash_accesses++;
-	hcp = (HASH_CURSOR *)TAILQ_FIRST(&ldbp->curs_queue)->internal;
-	if ((ret = __ham_lookup(hashp, hcp, key, 0, DB_LOCK_READ)) == 0) {
-		if (F_ISSET(hcp, H_OK))
-			ret = __ham_dup_return(hashp, hcp, data, DB_FIRST);
-		else /* Key was not found */
-			ret = DB_NOTFOUND;
-	}
-
-	if ((t_ret = __ham_item_done(hashp, hcp, 0)) != 0 && ret == 0)
-		ret = t_ret;
-	RELEASE_META(ldbp, hashp);
-	if (F_ISSET(dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
-	return (ret);
-}
-
-static int
-__ham_put(dbp, txn, key, data, flags)
-	DB *dbp;
-	DB_TXN *txn;
-	DBT *key;
-	DBT *data;
-	u_int32_t flags;
-{
-	DB *ldbp;
-	DBT tmp_val, *myval;
-	HASH_CURSOR *hcp;
-	HTAB *hashp;
-	u_int32_t nbytes;
-	int ret, t_ret;
-
-	DEBUG_LWRITE(dbp, txn, "ham_put", key, data, flags);
-	if ((ret = __db_putchk(dbp, key, data,
-	    flags, F_ISSET(dbp, DB_AM_RDONLY), F_ISSET(dbp, DB_AM_DUP))) != 0)
+	if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
 		return (ret);
 
-	ldbp = dbp;
-	if (F_ISSET(dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(dbp, __ham_hdup, &ldbp)) != 0)
-		return (ret);
+	DEBUG_LWRITE(dbc, txn, "ham_delete", key, NULL, flags);
 
-	hashp = (HTAB *)ldbp->internal;
-	SET_LOCKER(ldbp, txn);
-	GET_META(ldbp, hashp);
-	hcp = TAILQ_FIRST(&ldbp->curs_queue)->internal;
-
-	nbytes = (ISBIG(hashp, key->size) ? HOFFPAGE_PSIZE :
-	    HKEYDATA_PSIZE(key->size)) +
-	    (ISBIG(hashp, data->size) ? HOFFPAGE_PSIZE :
-	    HKEYDATA_PSIZE(data->size));
-
-	hashp->hash_accesses++;
-	ret = __ham_lookup(hashp, hcp, key, nbytes, DB_LOCK_WRITE);
-
-	if (ret == DB_NOTFOUND) {
-		ret = 0;
-		if (hcp->seek_found_page != PGNO_INVALID &&
-		    hcp->seek_found_page != hcp->pgno) {
-			if ((ret = __ham_item_done(hashp, hcp, 0)) != 0)
-				goto out;
-			hcp->pgno = hcp->seek_found_page;
-			hcp->bndx = NDX_INVALID;
-		}
+	hcp = (HASH_CURSOR *)dbc->internal;
+	GET_META(dbp, hcp, ret);
+	if (ret != 0)
+		goto out;
 
-		if (F_ISSET(data, DB_DBT_PARTIAL) && data->doff != 0) {
-			/*
-			 * Doing a partial put, but the key does not exist
-			 * and we are not beginning the write at 0.  We
-			 * must create a data item padded up to doff and
-			 * then write the new bytes represented by val.
-			 */
-			ret = __ham_init_dbt(&tmp_val, data->size + data->doff,
-			    &hcp->big_data, &hcp->big_datalen);
-			if (ret == 0) {
-				memset(tmp_val.data, 0, data->doff);
-				memcpy((u_int8_t *)tmp_val.data + data->doff,
-				    data->data, data->size);
-				myval = &tmp_val;
-			}
-		} else
-			myval = (DBT *)data;
-
-		if (ret == 0)
-			ret = __ham_add_el(hashp, hcp, key, myval, H_KEYDATA);
-	} else if (ret == 0 && F_ISSET(hcp, H_OK)) {
-		if (flags == DB_NOOVERWRITE)
-			ret = DB_KEYEXIST;
-		else if (F_ISSET(ldbp, DB_AM_DUP))
-			ret = __ham_add_dup(hashp, hcp, data, DB_KEYLAST);
+	hcp->stats.hash_deleted++;
+	if ((ret = __ham_lookup(dbc, key, 0, DB_LOCK_WRITE)) == 0) {
+		if (F_ISSET(hcp, H_OK))
+			ret = __ham_del_pair(dbc, 1);
 		else
-			ret = __ham_overwrite(hashp, hcp, data);
-	}
-
-	/* Free up all the cursor pages. */
-	if ((t_ret = __ham_item_done(hashp, hcp, ret == 0)) != 0 && ret == 0)
-		ret = t_ret;
-	/* Now check if we have to grow. */
-out:	if (ret == 0 && F_ISSET(hcp, H_EXPAND)) {
-		ret = __ham_expand_table(hashp);
-		F_CLR(hcp, H_EXPAND);
+			ret = DB_NOTFOUND;
 	}
 
-	if ((t_ret = __ham_item_done(hashp, hcp, ret == 0)) != 0 && ret == 0)
-		ret = t_ret;
-	RELEASE_META(ldbp, hashp);
-	if (F_ISSET(dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
+	RELEASE_META(dbp, hcp);
+out:	if ((tret = dbc->c_close(dbc)) != 0 && ret == 0)
+		ret = tret;
 	return (ret);
 }
 
-static int
-__ham_cursor(dbp, txnid, dbcp)
-	DB *dbp;
-	DB_TXN *txnid;
-	DBC **dbcp;
-{
+/* ****************** CURSORS ********************************** */
+/*
+ * __ham_c_init --
+ *	Initialize the hash-specific portion of a cursor.
+ *
+ * PUBLIC: int __ham_c_init __P((DBC *));
+ */
+int
+__ham_c_init(dbc)
+	DBC *dbc;
+  {
+	HASH_CURSOR *new_curs;
 	int ret;
 
-	DEBUG_LWRITE(dbp, txnid, "ham_cursor", NULL, NULL, 0);
-	if ((ret = __ham_c_init(dbp, txnid, dbcp)) != 0)
+	if ((ret = __os_calloc(1, sizeof(struct cursor_t), &new_curs)) != 0)
+		return (ret);
+	if ((ret =
+	    __os_malloc(dbc->dbp->pgsize, NULL, &new_curs->split_buf)) != 0) {
+		__os_free(new_curs, sizeof(*new_curs));
 		return (ret);
-
-	DB_THREAD_LOCK(dbp);
-	TAILQ_INSERT_TAIL(&dbp->curs_queue, *dbcp, links);
-	DB_THREAD_UNLOCK(dbp);
-	return (ret);
-}
-
-static int
-__ham_c_init(dbp, txnid, dbcp)
-	DB *dbp;
-	DB_TXN *txnid;
-	DBC **dbcp;
-{
-	DBC *db_curs;
-	HASH_CURSOR *new_curs;
-
-	if ((db_curs = (DBC *)__db_calloc(sizeof(DBC), 1)) == NULL)
-		return (ENOMEM);
-
-	if ((new_curs =
-	    (HASH_CURSOR *)__db_calloc(sizeof(struct cursor_t), 1)) == NULL) {
-		FREE(db_curs, sizeof(DBC));
-		return (ENOMEM);
 	}
 
-	db_curs->internal = new_curs;
-	db_curs->c_close = __ham_c_close;
-	db_curs->c_del = __ham_c_del;
-	db_curs->c_get = __ham_c_get;
-	db_curs->c_put = __ham_c_put;
-	db_curs->txn = txnid;
-	db_curs->dbp = dbp;
+	new_curs->dbc = dbc;
+
+	dbc->internal = new_curs;
+	dbc->c_am_close = __ham_c_close;
+	dbc->c_am_destroy = __ham_c_destroy;
+	dbc->c_del = __ham_c_del;
+	dbc->c_get = __ham_c_get;
+	dbc->c_put = __ham_c_put;
 
-	new_curs->db_cursor = db_curs;
 	__ham_item_init(new_curs);
 
-	if (dbcp != NULL)
-		*dbcp = db_curs;
 	return (0);
 }
 
+/*
+ * __ham_c_close --
+ *	Close down the cursor from a single use.
+ */
 static int
-__ham_delete(dbp, txn, key, flags)
-	DB *dbp;
-	DB_TXN *txn;
-	DBT *key;
-	u_int32_t flags;
-{
-	DB *ldbp;
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
-	int ret, t_ret;
-
-	DEBUG_LWRITE(dbp, txn, "ham_delete", key, NULL, flags);
-	if ((ret =
-	    __db_delchk(dbp, key, flags, F_ISSET(dbp, DB_AM_RDONLY))) != 0)
-		return (ret);
-
-	ldbp = dbp;
-	if (F_ISSET(dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(dbp, __ham_hdup, &ldbp)) != 0)
-		return (ret);
-	hashp = (HTAB *)ldbp->internal;
-	SET_LOCKER(ldbp, txn);
-	GET_META(ldbp, hashp);
-	hcp = TAILQ_FIRST(&ldbp->curs_queue)->internal;
-
-	hashp->hash_accesses++;
-	if ((ret = __ham_lookup(hashp, hcp, key, 0, DB_LOCK_WRITE)) == 0) {
-		if (F_ISSET(hcp, H_OK))
-			ret = __ham_del_pair(hashp, hcp, 1);
-		else
-			ret = DB_NOTFOUND;
-	}
-
-	if ((t_ret = __ham_item_done(hashp, hcp, ret == 0)) != 0 && ret == 0)
-		ret = t_ret;
-	RELEASE_META(ldbp, hashp);
-	if (F_ISSET(dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
-	return (ret);
-}
-
-/* ****************** CURSORS ********************************** */
-static int
-__ham_c_close(cursor)
-	DBC *cursor;
+__ham_c_close(dbc)
+	DBC *dbc;
 {
-	DB  *ldbp;
 	int ret;
 
-	DEBUG_LWRITE(cursor->dbp, cursor->txn, "ham_c_close", NULL, NULL, 0);
-	/*
-	 * If the pagep, dpagep, and lock fields of the cursor are all NULL,
-	 * then there really isn't a need to get a handle here.  However,
-	 * the normal case is that at least one of those fields is non-NULL,
-	 * and putting those checks in here would couple the ham_item_done
-	 * functionality with cursor close which would be pretty disgusting.
-	 * Instead, we pay the overhead here of always getting the handle.
-	 */
-	ldbp = cursor->dbp;
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(cursor->dbp, __ham_hdup, &ldbp)) != 0)
+	if ((ret = __ham_item_done(dbc, 0)) != 0)
 		return (ret);
 
-	ret = __ham_c_iclose(ldbp, cursor);
-
-	if (F_ISSET(ldbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
-	return (ret);
+	__ham_item_init((HASH_CURSOR *)dbc->internal);
+	return (0);
 }
+
 /*
- * __ham_c_iclose --
- *
- * Internal cursor close routine; assumes it is being passed the correct
- * handle, rather than getting and putting a handle.
- *
- * PUBLIC: int __ham_c_iclose __P((DB *, DBC *));
+ * __ham_c_destroy --
+ *	Cleanup the access method private part of a cursor.
  */
-int
-__ham_c_iclose(dbp, dbc)
-	DB *dbp;
+static int
+__ham_c_destroy(dbc)
 	DBC *dbc;
 {
 	HASH_CURSOR *hcp;
-	HTAB *hashp;
-	int ret;
 
-	hashp = (HTAB *)dbp->internal;
 	hcp = (HASH_CURSOR *)dbc->internal;
-	ret = __ham_item_done(hashp, hcp, 0);
-
-	if (hcp->big_key)
-		FREE(hcp->big_key, hcp->big_keylen);
-	if (hcp->big_data)
-		FREE(hcp->big_data, hcp->big_datalen);
+	if (hcp->split_buf != NULL)
+		__os_free(hcp->split_buf, dbc->dbp->pgsize);
+	__os_free(hcp, sizeof(HASH_CURSOR));
 
-	/*
-	 * All cursors (except the default ones) are linked off the master.
-	 * Therefore, when we close the cursor, we have to remove it from
-	 * the master, not the local one.
-	 * XXX I am always removing from the master; what about local cursors?
-	 */
-	DB_THREAD_LOCK(dbc->dbp);
-	TAILQ_REMOVE(&dbc->dbp->curs_queue, dbc, links);
-	DB_THREAD_UNLOCK(dbc->dbp);
-
-	FREE(hcp, sizeof(HASH_CURSOR));
-	FREE(dbc, sizeof(DBC));
-
-	return (ret);
+	return (0);
 }
 
 static int
-__ham_c_del(cursor, flags)
-	DBC *cursor;
+__ham_c_del(dbc, flags)
+	DBC *dbc;
 	u_int32_t flags;
 {
-	DB *ldbp;
+	DB *dbp;
+	DBT repldbt;
 	HASH_CURSOR *hcp;
 	HASH_CURSOR save_curs;
-	HTAB *hashp;
 	db_pgno_t ppgno, chg_pgno;
 	int ret, t_ret;
 
-	DEBUG_LWRITE(cursor->dbp, cursor->txn, "ham_c_del", NULL, NULL, flags);
-	ldbp = cursor->dbp;
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(cursor->dbp, __ham_hdup, &ldbp)) != 0)
-		return (ret);
-	hashp = (HTAB *)ldbp->internal;
-	hcp = (HASH_CURSOR *)cursor->internal;
-	save_curs = *hcp;
-	if ((ret = __db_cdelchk(ldbp, flags,
-	    F_ISSET(ldbp, DB_AM_RDONLY), IS_VALID(hcp))) != 0)
+	DEBUG_LWRITE(dbc, dbc->txn, "ham_c_del", NULL, NULL, flags);
+	dbp = dbc->dbp;
+	DB_PANIC_CHECK(dbp);
+	hcp = (HASH_CURSOR *)dbc->internal;
+
+	if ((ret = __db_cdelchk(dbc->dbp, flags,
+	    F_ISSET(dbc->dbp, DB_AM_RDONLY), IS_VALID(hcp))) != 0)
 		return (ret);
+
 	if (F_ISSET(hcp, H_DELETED))
 		return (DB_NOTFOUND);
 
-	SET_LOCKER(hashp->dbp, cursor->txn);
-	GET_META(hashp->dbp, hashp);
-	hashp->hash_accesses++;
-	if ((ret = __ham_get_cpage(hashp, hcp, DB_LOCK_WRITE)) != 0)
+	/*
+	 * If we are in the concurrent DB product and this cursor
+	 * is not a write cursor, then this request is invalid.
+	 * If it is a simple write cursor, then we need to upgrade its
+	 * lock.
+	 */
+	if (F_ISSET(dbp, DB_AM_CDB)) {
+		/* Make sure it's a valid update cursor. */
+		if (!F_ISSET(dbc, DBC_RMW | DBC_WRITER))
+			return (EINVAL);
+
+		if (F_ISSET(dbc, DBC_RMW) &&
+		    (ret = lock_get(dbp->dbenv->lk_info, dbc->locker,
+		    DB_LOCK_UPGRADE, &dbc->lock_dbt, DB_LOCK_WRITE,
+		    &dbc->mylock)) != 0)
+			return (EAGAIN);
+	}
+
+	GET_META(dbp, hcp, ret);
+	if (ret != 0)
+		return (ret);
+
+	SAVE_CURSOR(hcp, &save_curs);
+	hcp->stats.hash_deleted++;
+
+	if ((ret = __ham_get_cpage(dbc, DB_LOCK_WRITE)) != 0)
 		goto out;
 	if (F_ISSET(hcp, H_ISDUP) && hcp->dpgno != PGNO_INVALID) {
 		/*
@@ -695,20 +428,20 @@ __ham_c_del(cursor, flags)
 
 		/* Remove item from duplicate page. */
 		chg_pgno = hcp->dpgno;
-		if ((ret = __db_drem(hashp->dbp,
+		if ((ret = __db_drem(dbc,
 		    &hcp->dpagep, hcp->dndx, __ham_del_page)) != 0)
 			goto out;
 
 		if (hcp->dpagep == NULL) {
 			if (ppgno != PGNO_INVALID) {		/* Case 3 */
 				hcp->dpgno = ppgno;
-				if ((ret = __ham_get_cpage(hashp, hcp,
+				if ((ret = __ham_get_cpage(dbc,
 				    DB_LOCK_READ)) != 0)
 					goto out;
 				hcp->dndx = NUM_ENT(hcp->dpagep);
 				F_SET(hcp, H_DELETED);
 			} else {				/* Case 4 */
-				ret = __ham_del_pair(hashp, hcp, 1);
+				ret = __ham_del_pair(dbc, 1);
 				hcp->dpgno = PGNO_INVALID;
 				/*
 				 * Delpair updated the cursor queue, so we
@@ -723,6 +456,15 @@ __ham_c_del(cursor, flags)
 				memcpy(HOFFDUP_PGNO(P_ENTRY(hcp->pagep,
 				    H_DATAINDEX(hcp->bndx))),
 				    &hcp->dpgno, sizeof(db_pgno_t));
+			/*
+			 * We need to put the master page here, because
+			 * although we have a duplicate page, the master
+			 * page is dirty, and ham_item_done assumes that
+			 * if you have a duplicate page, it's the only one
+			 * that can be dirty.
+			 */
+			ret = __ham_put_page(dbp, hcp->pagep, 1);
+			hcp->pagep = NULL;
 			F_SET(hcp, H_DELETED);
 		} else						/* Case 1 */
 			F_SET(hcp, H_DELETED);
@@ -730,17 +472,17 @@ __ham_c_del(cursor, flags)
 			__ham_c_update(hcp, chg_pgno, 0, 0, 1);
 	} else if (F_ISSET(hcp, H_ISDUP)) {			/* on page */
 		if (hcp->dup_off == 0 && DUP_SIZE(hcp->dup_len) ==
-		    LEN_HDATA(hcp->pagep, hashp->hdr->pagesize, hcp->bndx))
-			ret = __ham_del_pair(hashp, hcp, 1);
+		    LEN_HDATA(hcp->pagep, hcp->hdr->pagesize, hcp->bndx))
+			ret = __ham_del_pair(dbc, 1);
 		else {
-			DBT repldbt;
-
 			repldbt.flags = 0;
 			F_SET(&repldbt, DB_DBT_PARTIAL);
 			repldbt.doff = hcp->dup_off;
 			repldbt.dlen = DUP_SIZE(hcp->dup_len);
 			repldbt.size = 0;
-			ret = __ham_replpair(hashp, hcp, &repldbt, 0);
+			repldbt.data =
+			    HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx));
+			ret = __ham_replpair(dbc, &repldbt, 0);
 			hcp->dup_tlen -= DUP_SIZE(hcp->dup_len);
 			F_SET(hcp, H_DELETED);
 			__ham_c_update(hcp, hcp->pgno,
@@ -749,48 +491,53 @@ __ham_c_del(cursor, flags)
 
 	} else
 		/* Not a duplicate */
-normal:		ret = __ham_del_pair(hashp, hcp, 1);
+normal:		ret = __ham_del_pair(dbc, 1);
 
-out:	if ((t_ret = __ham_item_done(hashp, hcp, ret == 0)) != 0 && ret == 0)
+out:	if ((t_ret = __ham_item_done(dbc, ret == 0)) != 0 && ret == 0)
 		ret = t_ret;
-	if (ret != 0)
-		*hcp = save_curs;
-	RELEASE_META(hashp->dbp, hashp);
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
+	RELEASE_META(dbp, hcp);
+	RESTORE_CURSOR(dbp, hcp, &save_curs, ret);
+	if (F_ISSET(dbp, DB_AM_CDB) && F_ISSET(dbc, DBC_RMW))
+		(void)__lock_downgrade(dbp->dbenv->lk_info, dbc->mylock,
+		    DB_LOCK_IWRITE, 0);
 	return (ret);
 }
 
 static int
-__ham_c_get(cursor, key, data, flags)
-	DBC *cursor;
+__ham_c_get(dbc, key, data, flags)
+	DBC *dbc;
 	DBT *key;
 	DBT *data;
 	u_int32_t flags;
 {
-	DB *ldbp;
-	HTAB *hashp;
+	DB *dbp;
 	HASH_CURSOR *hcp, save_curs;
+	db_lockmode_t lock_type;
 	int get_key, ret, t_ret;
 
-	DEBUG_LREAD(cursor->dbp, cursor->txn, "ham_c_get",
+	DEBUG_LREAD(dbc, dbc->txn, "ham_c_get",
 	    flags == DB_SET || flags == DB_SET_RANGE ? key : NULL,
 	    NULL, flags);
-	ldbp = cursor->dbp;
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(cursor->dbp, __ham_hdup, &ldbp)) != 0)
-		return (ret);
-	hashp = (HTAB *)(ldbp->internal);
-	hcp = (HASH_CURSOR *)cursor->internal;
-	save_curs = *hcp;
+
+	hcp = (HASH_CURSOR *)dbc->internal;
+	dbp = dbc->dbp;
+	DB_PANIC_CHECK(dbp);
+	SAVE_CURSOR(hcp, &save_curs);
 	if ((ret =
-	    __db_cgetchk(hashp->dbp, key, data, flags, IS_VALID(hcp))) != 0)
+	    __db_cgetchk(dbp, key, data, flags, IS_VALID(hcp))) != 0)
 		return (ret);
 
-	SET_LOCKER(hashp->dbp, cursor->txn);
-	GET_META(hashp->dbp, hashp);
-	hashp->hash_accesses++;
+	/* Clear OR'd in additional bits so we can check for flag equality. */
+	if (LF_ISSET(DB_RMW)) {
+		lock_type = DB_LOCK_WRITE;
+		LF_CLR(DB_RMW);
+	} else
+		lock_type = DB_LOCK_READ;
 
+	GET_META(dbp, hcp, ret);
+	if (ret != 0)
+		return (ret);
+	hcp->stats.hash_get++;
 	hcp->seek_size = 0;
 
 	ret = 0;
@@ -798,24 +545,39 @@ __ham_c_get(cursor, key, data, flags)
 	switch (flags) {
 	case DB_PREV:
 		if (hcp->bucket != BUCKET_INVALID) {
-			ret = __ham_item_prev(hashp, hcp, DB_LOCK_READ);
+			ret = __ham_item_prev(dbc, lock_type);
 			break;
 		}
 		/* FALLTHROUGH */
 	case DB_LAST:
-		ret = __ham_item_last(hashp, hcp, DB_LOCK_READ);
+		ret = __ham_item_last(dbc, lock_type);
 		break;
 	case DB_FIRST:
-		ret = __ham_item_first(hashp, hcp, DB_LOCK_READ);
+		ret = __ham_item_first(dbc, lock_type);
+		break;
+	case DB_NEXT_DUP:
+		if (hcp->bucket == BUCKET_INVALID)
+			ret = EINVAL;
+		else {
+			F_SET(hcp, H_DUPONLY);
+			ret = __ham_item_next(dbc, lock_type);
+		}
 		break;
 	case DB_NEXT:
 		if (hcp->bucket == BUCKET_INVALID)
 			hcp->bucket = 0;
-		ret = __ham_item_next(hashp, hcp, DB_LOCK_READ);
+		ret = __ham_item_next(dbc, lock_type);
 		break;
 	case DB_SET:
 	case DB_SET_RANGE:
-		ret = __ham_lookup(hashp, hcp, key, 0, DB_LOCK_READ);
+	case DB_GET_BOTH:
+		if (F_ISSET(dbc, DBC_CONTINUE)) {
+			F_SET(hcp, H_DUPONLY);
+			ret = __ham_item_next(dbc, lock_type);
+		} else if (F_ISSET(dbc, DBC_KEYSET))
+			ret = __ham_item(dbc, lock_type);
+		else
+			ret = __ham_lookup(dbc, key, 0, lock_type);
 		get_key = 0;
 		break;
 	case DB_CURRENT:
@@ -824,7 +586,7 @@ __ham_c_get(cursor, key, data, flags)
 			goto out;
 		}
 
-		ret = __ham_item(hashp, hcp, DB_LOCK_READ);
+		ret = __ham_item(dbc, lock_type);
 		break;
 	}
 
@@ -837,12 +599,12 @@ __ham_c_get(cursor, key, data, flags)
 			goto out1;
 		else if (F_ISSET(hcp, H_OK)) {
 			/* Get the key. */
-			if (get_key && (ret = __db_ret(hashp->dbp, hcp->pagep,
-			    H_KEYINDEX(hcp->bndx), key, &hcp->big_key,
-			    &hcp->big_keylen)) != 0)
+			if (get_key && (ret = __db_ret(dbp, hcp->pagep,
+			    H_KEYINDEX(hcp->bndx), key, &dbc->rkey.data,
+			    &dbc->rkey.size)) != 0)
 				goto out1;
 
-			ret = __ham_dup_return(hashp, hcp, data, flags);
+			ret = __ham_dup_return(dbc, data, flags);
 			break;
 		} else if (!F_ISSET(hcp, H_NOMORE)) {
 			abort();
@@ -855,7 +617,7 @@ __ham_c_get(cursor, key, data, flags)
 		switch (flags) {
 			case DB_LAST:
 			case DB_PREV:
-				ret = __ham_item_done(hashp, hcp, 0);
+				ret = __ham_item_done(dbc, 0);
 				if (hcp->bucket == 0) {
 					ret = DB_NOTFOUND;
 					goto out1;
@@ -863,24 +625,24 @@ __ham_c_get(cursor, key, data, flags)
 				hcp->bucket--;
 				hcp->bndx = NDX_INVALID;
 				if (ret == 0)
-					ret = __ham_item_prev(hashp,
-					    hcp, DB_LOCK_READ);
+					ret = __ham_item_prev(dbc, lock_type);
 				break;
 			case DB_FIRST:
 			case DB_NEXT:
-				ret = __ham_item_done(hashp, hcp, 0);
+				ret = __ham_item_done(dbc, 0);
 				hcp->bndx = NDX_INVALID;
 				hcp->bucket++;
 				hcp->pgno = PGNO_INVALID;
 				hcp->pagep = NULL;
-				if (hcp->bucket > hashp->hdr->max_bucket) {
+				if (hcp->bucket > hcp->hdr->max_bucket) {
 					ret = DB_NOTFOUND;
 					goto out1;
 				}
 				if (ret == 0)
-					ret = __ham_item_next(hashp,
-					    hcp, DB_LOCK_READ);
+					ret = __ham_item_next(dbc, lock_type);
 				break;
+			case DB_GET_BOTH:
+			case DB_NEXT_DUP:
 			case DB_SET:
 			case DB_SET_RANGE:
 				/* Key not found. */
@@ -888,85 +650,137 @@ __ham_c_get(cursor, key, data, flags)
 				goto out1;
 		}
 	}
-out1:	if ((t_ret = __ham_item_done(hashp, hcp, 0)) != 0 && ret == 0)
+out1:	if ((t_ret = __ham_item_done(dbc, 0)) != 0 && ret == 0)
 		ret = t_ret;
-out:	if (ret)
-		*hcp = save_curs;
-	RELEASE_META(hashp->dbp, hashp);
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
+out:	RELEASE_META(dbp, hcp);
+	RESTORE_CURSOR(dbp, hcp, &save_curs, ret);
 	return (ret);
 }
 
 static int
-__ham_c_put(cursor, key, data, flags)
-	DBC *cursor;
+__ham_c_put(dbc, key, data, flags)
+	DBC *dbc;
 	DBT *key;
 	DBT *data;
 	u_int32_t flags;
 {
-	DB *ldbp;
+	DB *dbp;
+	DBT tmp_val, *myval;
 	HASH_CURSOR *hcp, save_curs;
-	HTAB *hashp;
 	u_int32_t nbytes;
 	int ret, t_ret;
 
-	DEBUG_LWRITE(cursor->dbp, cursor->txn, "ham_c_put",
+	dbp = dbc->dbp;
+	DB_PANIC_CHECK(dbp);
+	DEBUG_LWRITE(dbc, dbc->txn, "ham_c_put",
 	    flags == DB_KEYFIRST || flags == DB_KEYLAST ? key : NULL,
 	    data, flags);
-	ldbp = cursor->dbp;
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD) &&
-	    (ret = __db_gethandle(cursor->dbp, __ham_hdup, &ldbp)) != 0)
-		return (ret);
-	hashp = (HTAB *)(ldbp->internal);
-	hcp = (HASH_CURSOR *)cursor->internal;
-	save_curs = *hcp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	if ((ret = __db_cputchk(hashp->dbp, key, data, flags,
-	    F_ISSET(ldbp, DB_AM_RDONLY), IS_VALID(hcp))) != 0)
+	if ((ret = __db_cputchk(dbp, key, data, flags,
+	    F_ISSET(dbp, DB_AM_RDONLY), IS_VALID(hcp))) != 0)
 		return (ret);
-	if (F_ISSET(hcp, H_DELETED))
+
+	if (F_ISSET(hcp, H_DELETED) &&
+	    flags != DB_KEYFIRST && flags != DB_KEYLAST)
 		return (DB_NOTFOUND);
 
-	SET_LOCKER(hashp->dbp, cursor->txn);
-	GET_META(hashp->dbp, hashp);
-	ret = 0;
+	/*
+	 * If we are in the concurrent DB product and this cursor
+	 * is not a write cursor, then this request is invalid.
+	 * If it is a simple write cursor, then we need to upgrade its
+	 * lock.
+	 */
+	if (F_ISSET(dbp, DB_AM_CDB)) {
+		/* Make sure it's a valid update cursor. */
+		if (!F_ISSET(dbc, DBC_RMW | DBC_WRITER))
+			return (EINVAL);
+
+		if (F_ISSET(dbc, DBC_RMW) &&
+		    (ret = lock_get(dbp->dbenv->lk_info, dbc->locker,
+		    DB_LOCK_UPGRADE, &dbc->lock_dbt, DB_LOCK_WRITE,
+		    &dbc->mylock)) != 0)
+			return (EAGAIN);
+	}
+
+	GET_META(dbp, hcp, ret);
+	if (ret != 0)
+		return (ret);
+
+	SAVE_CURSOR(hcp, &save_curs);
+	hcp->stats.hash_put++;
 
 	switch (flags) {
 	case DB_KEYLAST:
 	case DB_KEYFIRST:
-		nbytes = (ISBIG(hashp, key->size) ? HOFFPAGE_PSIZE :
+		nbytes = (ISBIG(hcp, key->size) ? HOFFPAGE_PSIZE :
 		    HKEYDATA_PSIZE(key->size)) +
-		    (ISBIG(hashp, data->size) ? HOFFPAGE_PSIZE :
+		    (ISBIG(hcp, data->size) ? HOFFPAGE_PSIZE :
 		    HKEYDATA_PSIZE(data->size));
-		ret = __ham_lookup(hashp, hcp, key, nbytes, DB_LOCK_WRITE);
+		if ((ret = __ham_lookup(dbc,
+		    key, nbytes, DB_LOCK_WRITE)) == DB_NOTFOUND) {
+			ret = 0;
+			if (hcp->seek_found_page != PGNO_INVALID &&
+			    hcp->seek_found_page != hcp->pgno) {
+				if ((ret = __ham_item_done(dbc, 0)) != 0)
+					goto out;
+				hcp->pgno = hcp->seek_found_page;
+				hcp->bndx = NDX_INVALID;
+			}
+
+			if (F_ISSET(data, DB_DBT_PARTIAL) && data->doff != 0) {
+				/*
+				 * A partial put, but the key does not exist
+				 * and we are not beginning the write at 0.
+				 * We must create a data item padded up to doff
+				 * and then write the new bytes represented by
+				 * val.
+				 */
+				if ((ret = __ham_init_dbt(&tmp_val,
+				    data->size + data->doff,
+				    &dbc->rdata.data, &dbc->rdata.size)) == 0) {
+					memset(tmp_val.data, 0, data->doff);
+					memcpy((u_int8_t *)tmp_val.data +
+					    data->doff, data->data, data->size);
+					myval = &tmp_val;
+				}
+			} else
+				myval = (DBT *)data;
+
+			if (ret == 0)
+				ret = __ham_add_el(dbc, key, myval, H_KEYDATA);
+			goto done;
+		}
 		break;
 	case DB_BEFORE:
 	case DB_AFTER:
 	case DB_CURRENT:
-		ret = __ham_item(hashp, hcp, DB_LOCK_WRITE);
+		ret = __ham_item(dbc, DB_LOCK_WRITE);
 		break;
 	}
 
 	if (ret == 0) {
-		if (flags == DB_CURRENT && !F_ISSET(ldbp, DB_AM_DUP))
-			ret = __ham_overwrite(hashp, hcp, data);
+		if ((flags == DB_CURRENT && !F_ISSET(hcp, H_ISDUP)) ||
+		    ((flags == DB_KEYFIRST || flags == DB_KEYLAST) &&
+		    !F_ISSET(dbp, DB_AM_DUP)))
+			ret = __ham_overwrite(dbc, data);
 		else
-			ret = __ham_add_dup(hashp, hcp, data, flags);
+			ret = __ham_add_dup(dbc, data, flags);
 	}
 
-	if (ret == 0 && F_ISSET(hcp, H_EXPAND)) {
-		ret = __ham_expand_table(hashp);
+done:	if (ret == 0 && F_ISSET(hcp, H_EXPAND)) {
+		ret = __ham_expand_table(dbc);
 		F_CLR(hcp, H_EXPAND);
 	}
 
-	if ((t_ret = __ham_item_done(hashp, hcp, ret == 0)) != 0 && ret == 0)
+	if ((t_ret = __ham_item_done(dbc, ret == 0)) != 0 && ret == 0)
 		ret = t_ret;
-	if (ret != 0)
-		*hcp = save_curs;
-	RELEASE_META(hashp->dbp, hashp);
-	if (F_ISSET(cursor->dbp, DB_AM_THREAD))
-		__db_puthandle(ldbp);
+
+out:	RELEASE_META(dbp, hcp);
+	RESTORE_CURSOR(dbp, hcp, &save_curs, ret);
+	if (F_ISSET(dbp, DB_AM_CDB) && F_ISSET(dbc, DBC_RMW))
+		(void)__lock_downgrade(dbp->dbenv->lk_info, dbc->mylock,
+		    DB_LOCK_IWRITE, 0);
 	return (ret);
 }
 
@@ -974,19 +788,21 @@ __ham_c_put(cursor, key, data, flags)
 
 /*
  * __ham_expand_table --
- *
- * PUBLIC: int __ham_expand_table __P((HTAB *));
  */
-int
-__ham_expand_table(hashp)
-	HTAB *hashp;
+static int
+__ham_expand_table(dbc)
+	DBC *dbc;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DB_LSN new_lsn;
 	u_int32_t old_bucket, new_bucket, spare_ndx;
 	int ret;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	ret = 0;
-	DIRTY_META(hashp, ret);
+	DIRTY_META(dbp, hcp, ret);
 	if (ret)
 		return (ret);
 
@@ -999,78 +815,78 @@ __ham_expand_table(hashp)
 	 * see what the log of one greater than that is; here we have to
 	 * look at the log of max + 2.  VERY NASTY STUFF.
 	 */
-	if (__db_log2(hashp->hdr->max_bucket + 2) > hashp->hdr->ovfl_point) {
+	if (__db_log2(hcp->hdr->max_bucket + 2) > hcp->hdr->ovfl_point) {
 		/*
 		 * We are about to shift the split point.  Make sure that
 		 * if the next doubling is going to be big (more than 8
 		 * pages), we have some extra pages around.
 		 */
-		if (hashp->hdr->max_bucket + 1 >= 8 &&
-		    hashp->hdr->spares[hashp->hdr->ovfl_point] <
-		    hashp->hdr->spares[hashp->hdr->ovfl_point - 1] +
-		    hashp->hdr->ovfl_point + 1)
-			__ham_init_ovflpages(hashp);
+		if (hcp->hdr->max_bucket + 1 >= 8 &&
+		    hcp->hdr->spares[hcp->hdr->ovfl_point] <
+		    hcp->hdr->spares[hcp->hdr->ovfl_point - 1] +
+		    hcp->hdr->ovfl_point + 1)
+			__ham_init_ovflpages(dbc);
 	}
 
 	/* Now we can log the meta-data split. */
-	if (DB_LOGGING(hashp->dbp)) {
-		if ((ret = __ham_splitmeta_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-		    hashp->dbp->log_fileid,
-		    hashp->hdr->max_bucket, hashp->hdr->ovfl_point,
-		    hashp->hdr->spares[hashp->hdr->ovfl_point],
-		    &hashp->hdr->lsn)) != 0)
+	if (DB_LOGGING(dbc)) {
+		if ((ret = __ham_splitmeta_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, dbp->log_fileid,
+		    hcp->hdr->max_bucket, hcp->hdr->ovfl_point,
+		    hcp->hdr->spares[hcp->hdr->ovfl_point],
+		    &hcp->hdr->lsn)) != 0)
 			return (ret);
 
-		hashp->hdr->lsn = new_lsn;
+		hcp->hdr->lsn = new_lsn;
 	}
 
-	hashp->hash_expansions++;
-	new_bucket = ++hashp->hdr->max_bucket;
-	old_bucket = (hashp->hdr->max_bucket & hashp->hdr->low_mask);
+	hcp->stats.hash_expansions++;
+	new_bucket = ++hcp->hdr->max_bucket;
+	old_bucket = (hcp->hdr->max_bucket & hcp->hdr->low_mask);
 
 	/*
 	 * If the split point is increasing, copy the current contents
 	 * of the spare split bucket to the next bucket.
 	 */
-	spare_ndx = __db_log2(hashp->hdr->max_bucket + 1);
-	if (spare_ndx > hashp->hdr->ovfl_point) {
-		hashp->hdr->spares[spare_ndx] =
-		    hashp->hdr->spares[hashp->hdr->ovfl_point];
-		hashp->hdr->ovfl_point = spare_ndx;
+	spare_ndx = __db_log2(hcp->hdr->max_bucket + 1);
+	if (spare_ndx > hcp->hdr->ovfl_point) {
+		hcp->hdr->spares[spare_ndx] =
+		    hcp->hdr->spares[hcp->hdr->ovfl_point];
+		hcp->hdr->ovfl_point = spare_ndx;
 	}
 
-	if (new_bucket > hashp->hdr->high_mask) {
+	if (new_bucket > hcp->hdr->high_mask) {
 		/* Starting a new doubling */
-		hashp->hdr->low_mask = hashp->hdr->high_mask;
-		hashp->hdr->high_mask = new_bucket | hashp->hdr->low_mask;
+		hcp->hdr->low_mask = hcp->hdr->high_mask;
+		hcp->hdr->high_mask = new_bucket | hcp->hdr->low_mask;
 	}
 
-	if (BUCKET_TO_PAGE(hashp, new_bucket) > MAX_PAGES(hashp)) {
-		__db_err(hashp->dbp->dbenv,
+	if (BUCKET_TO_PAGE(hcp, new_bucket) > MAX_PAGES(hcp)) {
+		__db_err(dbp->dbenv,
 		    "hash: Cannot allocate new bucket.  Pages exhausted.");
 		return (ENOSPC);
 	}
 
 	/* Relocate records to the new bucket */
-	return (__ham_split_page(hashp, old_bucket, new_bucket));
+	return (__ham_split_page(dbc, old_bucket, new_bucket));
 }
 
 /*
- * PUBLIC: u_int32_t __ham_call_hash __P((HTAB *, u_int8_t *, int32_t));
+ * PUBLIC: u_int32_t __ham_call_hash __P((HASH_CURSOR *, u_int8_t *, int32_t));
  */
 u_int32_t
-__ham_call_hash(hashp, k, len)
-	HTAB *hashp;
+__ham_call_hash(hcp, k, len)
+	HASH_CURSOR *hcp;
 	u_int8_t *k;
 	int32_t len;
 {
 	u_int32_t n, bucket;
 
-	n = (u_int32_t)hashp->hash(k, len);
-	bucket = n & hashp->hdr->high_mask;
-	if (bucket > hashp->hdr->max_bucket)
-		bucket = bucket & hashp->hdr->low_mask;
+	n = (u_int32_t)(hcp->dbc->dbp->h_hash(k, len));
+
+	bucket = n & hcp->hdr->high_mask;
+	if (bucket > hcp->hdr->max_bucket)
+		bucket = bucket & hcp->hdr->low_mask;
 	return (bucket);
 }
 
@@ -1079,31 +895,36 @@ __ham_call_hash(hashp, k, len)
  * everything held by the cursor.
  */
 static int
-__ham_dup_return(hashp, hcp, val, flags)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_dup_return(dbc, val, flags)
+	DBC *dbc;
 	DBT *val;
 	u_int32_t flags;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	PAGE *pp;
 	DBT *myval, tmp_val;
 	db_indx_t ndx;
 	db_pgno_t pgno;
+	u_int32_t off, tlen;
 	u_int8_t *hk, type;
-	int ret;
+	int cmp, ret;
 	db_indx_t len;
 
 	/* Check for duplicate and return the first one. */
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	ndx = H_DATAINDEX(hcp->bndx);
 	type = HPAGE_TYPE(hcp->pagep, ndx);
 	pp = hcp->pagep;
 	myval = val;
 
 	/*
-	 * There are 3 cases:
+	 * There are 4 cases:
 	 * 1. We are not in duplicate, simply call db_ret.
 	 * 2. We are looking at keys and stumbled onto a duplicate.
 	 * 3. We are in the middle of a duplicate set. (ISDUP set)
+	 * 4. This is a duplicate and we need to return a specific item.
 	 */
 
 	/*
@@ -1115,7 +936,7 @@ __ham_dup_return(hashp, hcp, val, flags)
 		if (type == H_DUPLICATE) {
 			F_SET(hcp, H_ISDUP);
 			hcp->dup_tlen = LEN_HDATA(hcp->pagep,
-			    hashp->hdr->pagesize, hcp->bndx);
+			    hcp->hdr->pagesize, hcp->bndx);
 			hk = H_PAIRDATA(hcp->pagep, hcp->bndx);
 			if (flags == DB_LAST || flags == DB_PREV) {
 				hcp->dndx = 0;
@@ -1141,18 +962,63 @@ __ham_dup_return(hashp, hcp, val, flags)
 			memcpy(&pgno, HOFFDUP_PGNO(P_ENTRY(hcp->pagep, ndx)),
 			    sizeof(db_pgno_t));
 			if (flags == DB_LAST || flags == DB_PREV) {
-				if ((ret = __db_dend(hashp->dbp,
+				if ((ret = __db_dend(dbc,
 				    pgno, &hcp->dpagep)) != 0)
 					return (ret);
 				hcp->dpgno = PGNO(hcp->dpagep);
 				hcp->dndx = NUM_ENT(hcp->dpagep) - 1;
-			} else if ((ret = __ham_next_cpage(hashp,
-			    hcp, pgno, 0, H_ISDUP)) != 0)
+			} else if ((ret = __ham_next_cpage(dbc,
+			    pgno, 0, H_ISDUP)) != 0)
 				return (ret);
 		}
 	}
 
 	/*
+	 * If we are retrieving a specific key/data pair, then we
+	 * may need to adjust the cursor before returning data.
+	 */
+	if (flags == DB_GET_BOTH) {
+		if (F_ISSET(hcp, H_ISDUP)) {
+			if (hcp->dpgno != PGNO_INVALID) {
+				if ((ret = __db_dsearch(dbc, 0, val,
+				    hcp->dpgno, &hcp->dndx, &hcp->dpagep, &cmp))
+				    != 0)
+					return (ret);
+				if (cmp == 0)
+					hcp->dpgno = PGNO(hcp->dpagep);
+			} else {
+				__ham_dsearch(dbc, val, &off, &cmp);
+				hcp->dup_off = off;
+			}
+		} else {
+			hk = H_PAIRDATA(hcp->pagep, hcp->bndx);
+			if (((HKEYDATA *)hk)->type == H_OFFPAGE) {
+				memcpy(&tlen,
+				    HOFFPAGE_TLEN(hk), sizeof(u_int32_t));
+				memcpy(&pgno,
+				    HOFFPAGE_PGNO(hk), sizeof(db_pgno_t));
+				if ((ret = __db_moff(dbp, val,
+				    pgno, tlen, dbp->dup_compare, &cmp)) != 0)
+					return (ret);
+			} else {
+				/*
+				 * We do not zero tmp_val since the comparison
+				 * routines may only look at data and size.
+				 */
+				tmp_val.data = HKEYDATA_DATA(hk);
+				tmp_val.size = LEN_HDATA(hcp->pagep,
+				    dbp->pgsize, hcp->bndx);
+				cmp = dbp->dup_compare == NULL ?
+				    __bam_defcmp(&tmp_val, val) :
+				    dbp->dup_compare(&tmp_val, val);
+			}
+		}
+
+		if (cmp != 0)
+			return (DB_NOTFOUND);
+	}
+
+	/*
 	 * Now, everything is initialized, grab a duplicate if
 	 * necessary.
 	 */
@@ -1162,14 +1028,34 @@ __ham_dup_return(hashp, hcp, val, flags)
 			ndx = hcp->dndx;
 		} else {
 			/*
-			 * Copy the DBT in case we are retrieving into
-			 * user memory and we need the parameters for
-			 * it.
+			 * Copy the DBT in case we are retrieving into user
+			 * memory and we need the parameters for it.  If the
+			 * user requested a partial, then we need to adjust
+			 * the user's parameters to get the partial of the
+			 * duplicate which is itself a partial.
 			 */
 			memcpy(&tmp_val, val, sizeof(*val));
-			F_SET(&tmp_val, DB_DBT_PARTIAL);
-			tmp_val.dlen = hcp->dup_len;
-			tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
+			if (F_ISSET(&tmp_val, DB_DBT_PARTIAL)) {
+				/*
+				 * Take the user's length unless it would go
+				 * beyond the end of the duplicate.
+				 */
+				if (tmp_val.doff + hcp->dup_off > hcp->dup_len)
+					tmp_val.dlen = 0;
+				else if (tmp_val.dlen + tmp_val.doff >
+				    hcp->dup_len)
+					tmp_val.dlen =
+					    hcp->dup_len - tmp_val.doff;
+
+				/*
+				 * Calculate the new offset.
+				 */
+				tmp_val.doff += hcp->dup_off;
+			} else {
+				F_SET(&tmp_val, DB_DBT_PARTIAL);
+				tmp_val.dlen = hcp->dup_len;
+				tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
+			}
 			myval = &tmp_val;
 		}
 	}
@@ -1178,8 +1064,8 @@ __ham_dup_return(hashp, hcp, val, flags)
 	 * Finally, if we had a duplicate, pp, ndx, and myval should be
 	 * set appropriately.
 	 */
-	if ((ret = __db_ret(hashp->dbp, pp, ndx, myval, &hcp->big_data,
-	    &hcp->big_datalen)) != 0)
+	if ((ret = __db_ret(dbp, pp, ndx, myval, &dbc->rdata.data,
+	    &dbc->rdata.size)) != 0)
 		return (ret);
 
 	/*
@@ -1193,16 +1079,17 @@ __ham_dup_return(hashp, hcp, val, flags)
 }
 
 static int
-__ham_overwrite(hashp, hcp, nval)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_overwrite(dbc, nval)
+	DBC *dbc;
 	DBT *nval;
 {
+	HASH_CURSOR *hcp;
 	DBT *myval, tmp_val;
 	u_int8_t *hk;
 
-	if (F_ISSET(hashp->dbp, DB_AM_DUP))
-		return (__ham_add_dup(hashp, hcp, nval, DB_KEYLAST));
+	hcp = (HASH_CURSOR *)dbc->internal;
+	if (F_ISSET(dbc->dbp, DB_AM_DUP))
+		return (__ham_add_dup(dbc, nval, DB_KEYLAST));
 	else if (!F_ISSET(nval, DB_DBT_PARTIAL)) {
 		/* Put/overwrite */
 		memcpy(&tmp_val, nval, sizeof(*nval));
@@ -1214,12 +1101,12 @@ __ham_overwrite(hashp, hcp, nval)
 			    HOFFPAGE_TLEN(hk), sizeof(u_int32_t));
 		else
 			tmp_val.dlen = LEN_HDATA(hcp->pagep,
-			    hashp->hdr->pagesize,hcp->bndx);
+			    hcp->hdr->pagesize,hcp->bndx);
 		myval = &tmp_val;
 	} else /* Regular partial put */
 		myval = nval;
 
-	return (__ham_replpair(hashp, hcp, myval, 0));
+	return (__ham_replpair(dbc, myval, 0));
 }
 
 /*
@@ -1232,29 +1119,32 @@ __ham_overwrite(hashp, hcp, nval)
  * non of the cursor pointer field are valid.
  */
 static int
-__ham_lookup(hashp, hcp, key, sought, mode)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_lookup(dbc, key, sought, mode)
+	DBC *dbc;
 	const DBT *key;
 	u_int32_t sought;
 	db_lockmode_t mode;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	db_pgno_t pgno;
 	u_int32_t tlen;
 	int match, ret, t_ret;
 	u_int8_t *hk;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	/*
 	 * Set up cursor so that we're looking for space to add an item
 	 * as we cycle through the pages looking for the key.
 	 */
-	if ((ret = __ham_item_reset(hashp, hcp)) != 0)
+	if ((ret = __ham_item_reset(dbc)) != 0)
 		return (ret);
 	hcp->seek_size = sought;
 
-	hcp->bucket = __ham_call_hash(hashp, (u_int8_t *)key->data, key->size);
+	hcp->bucket = __ham_call_hash(hcp, (u_int8_t *)key->data, key->size);
 	while (1) {
-		if ((ret = __ham_item_next(hashp, hcp, mode)) != 0)
+		if ((ret = __ham_item_next(dbc, mode)) != 0)
 			return (ret);
 
 		if (F_ISSET(hcp, H_NOMORE))
@@ -1267,7 +1157,9 @@ __ham_lookup(hashp, hcp, key, sought, mode)
 			if (tlen == key->size) {
 				memcpy(&pgno,
 				    HOFFPAGE_PGNO(hk), sizeof(db_pgno_t));
-				match = __db_moff(hashp->dbp, key, pgno);
+				if ((ret = __db_moff(dbp,
+				    key, pgno, tlen, NULL, &match)) != 0)
+					return (ret);
 				if (match == 0) {
 					F_SET(hcp, H_OK);
 					return (0);
@@ -1276,7 +1168,7 @@ __ham_lookup(hashp, hcp, key, sought, mode)
 			break;
 		case H_KEYDATA:
 			if (key->size == LEN_HKEY(hcp->pagep,
-			    hashp->hdr->pagesize, hcp->bndx) &&
+			    hcp->hdr->pagesize, hcp->bndx) &&
 			    memcmp(key->data,
 			    HKEYDATA_DATA(hk), key->size) == 0) {
 				F_SET(hcp, H_OK);
@@ -1289,9 +1181,9 @@ __ham_lookup(hashp, hcp, key, sought, mode)
 			 * These are errors because keys are never
 			 * duplicated, only data items are.
 			 */
-			return (__db_pgfmt(hashp->dbp, PGNO(hcp->pagep)));
+			return (__db_pgfmt(dbp, PGNO(hcp->pagep)));
 		}
-		hashp->hash_collisions++;
+		hcp->stats.hash_collisions++;
 	}
 
 	/*
@@ -1301,7 +1193,7 @@ __ham_lookup(hashp, hcp, key, sought, mode)
 	if (sought != 0)
 		return (ret);
 
-	if ((t_ret = __ham_item_done(hashp, hcp, 0)) != 0 && ret == 0)
+	if ((t_ret = __ham_item_done(dbc, 0)) != 0 && ret == 0)
 		ret = t_ret;
 	return (ret);
 }
@@ -1318,12 +1210,13 @@ __ham_init_dbt(dbt, size, bufp, sizep)
 	void **bufp;
 	u_int32_t *sizep;
 {
+	int ret;
+
 	memset(dbt, 0, sizeof(*dbt));
 	if (*sizep < size) {
-		if ((*bufp = (void *)(*bufp == NULL ?
-		    __db_malloc(size) : __db_realloc(*bufp, size))) == NULL) {
+		if ((ret = __os_realloc(bufp, size)) != 0) {
 			*sizep = 0;
-			return (ENOMEM);
+			return (ret);
 		}
 		*sizep = size;
 	}
@@ -1352,8 +1245,8 @@ __ham_c_update(hcp, chg_pgno, len, add, is_dup)
 	u_int32_t len;
 	int add, is_dup;
 {
+	DB *dbp;
 	DBC *cp;
-	HTAB *hp;
 	HASH_CURSOR *lcp;
 	int page_deleted;
 
@@ -1379,10 +1272,10 @@ __ham_c_update(hcp, chg_pgno, len, add, is_dup)
 		page_deleted =
 		    chg_pgno != PGNO_INVALID && chg_pgno != hcp->dpgno;
 
-	hp = hcp->db_cursor->dbp->master->internal;
-	DB_THREAD_LOCK(hp->dbp);
+	dbp = hcp->dbc->dbp;
+	DB_THREAD_LOCK(dbp);
 
-	for (cp = TAILQ_FIRST(&hp->dbp->curs_queue); cp != NULL;
+	for (cp = TAILQ_FIRST(&dbp->active_queue); cp != NULL;
 	    cp = TAILQ_NEXT(cp, links)) {
 		if (cp->internal == hcp)
 			continue;
@@ -1440,43 +1333,5 @@ __ham_c_update(hcp, chg_pgno, len, add, is_dup)
 			}
 		}
 	}
-	DB_THREAD_UNLOCK(hp->dbp);
-}
-
-/*
- * __ham_hdup --
- *	This function gets called when we create a duplicate handle for a
- *	threaded DB.  It should create the private part of the DB structure.
- *
- * PUBLIC: int  __ham_hdup __P((DB *, DB *));
- */
-int
-__ham_hdup(orig, new)
-	DB *orig, *new;
-{
-	DBC *curs;
-	HTAB *hashp;
-	int ret;
-
-	if ((hashp = (HTAB *)__db_malloc(sizeof(HTAB))) == NULL)
-		return (ENOMEM);
-
-	new->internal = hashp;
-
-	hashp->dbp = new;
-	hashp->hlock = 0;
-	hashp->hdr = NULL;
-	hashp->hash = ((HTAB *)orig->internal)->hash;
-	if ((hashp->split_buf = (PAGE *)__db_malloc(orig->pgsize)) == NULL)
-		return (ENOMEM);
-	hashp->local_errno = 0;
-	hashp->hash_accesses = 0;
-	hashp->hash_collisions = 0;
-	hashp->hash_expansions = 0;
-	hashp->hash_overflows = 0;
-	hashp->hash_bigpages = 0;
-	/* Initialize the cursor queue. */
-	ret = __ham_c_init(new, NULL, &curs);
-	TAILQ_INSERT_TAIL(&new->curs_queue, curs, links);
-	return (ret);
+	DB_THREAD_UNLOCK(dbp);
 }
diff --git a/db2/hash/hash_auto.c b/db2/hash/hash_auto.c
index 41b1ebed01..94a1dff6ed 100644
--- a/db2/hash/hash_auto.c
+++ b/db2/hash/hash_auto.c
@@ -10,7 +10,6 @@
 #endif
 
 #include "db_int.h"
-#include "shqueue.h"
 #include "db_page.h"
 #include "db_dispatch.h"
 #include "hash.h"
@@ -46,8 +45,7 @@ int __ham_insdel_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_insdel;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -59,8 +57,8 @@ int __ham_insdel_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(*pagelsn)
 	    + sizeof(u_int32_t) + (key == NULL ? 0 : key->size)
 	    + sizeof(u_int32_t) + (data == NULL ? 0 : data->size);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -109,7 +107,7 @@ int __ham_insdel_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -170,7 +168,7 @@ __ham_insdel_print(notused1, dbtp, lsnp, notused2, notused3)
 	}
 	printf("\n");
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -184,11 +182,12 @@ __ham_insdel_read(recbuf, argpp)
 {
 	__ham_insdel_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_insdel_args *)__db_malloc(sizeof(__ham_insdel_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_insdel_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -250,8 +249,7 @@ int __ham_newpage_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_newpage;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -264,8 +262,8 @@ int __ham_newpage_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(*pagelsn)
 	    + sizeof(next_pgno)
 	    + sizeof(*nextlsn);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -306,7 +304,7 @@ int __ham_newpage_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -354,7 +352,7 @@ __ham_newpage_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\tnextlsn: [%lu][%lu]\n",
 	    (u_long)argp->nextlsn.file, (u_long)argp->nextlsn.offset);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -368,11 +366,12 @@ __ham_newpage_read(recbuf, argpp)
 {
 	__ham_newpage_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_newpage_args *)__db_malloc(sizeof(__ham_newpage_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_newpage_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -428,8 +427,7 @@ int __ham_splitmeta_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_splitmeta;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -439,8 +437,8 @@ int __ham_splitmeta_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(ovflpoint)
 	    + sizeof(spares)
 	    + sizeof(*metalsn);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -469,7 +467,7 @@ int __ham_splitmeta_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -512,7 +510,7 @@ __ham_splitmeta_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\tmetalsn: [%lu][%lu]\n",
 	    (u_long)argp->metalsn.file, (u_long)argp->metalsn.offset);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -526,11 +524,12 @@ __ham_splitmeta_read(recbuf, argpp)
 {
 	__ham_splitmeta_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_splitmeta_args *)__db_malloc(sizeof(__ham_splitmeta_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_splitmeta_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -581,8 +580,7 @@ int __ham_splitdata_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_splitdata;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -592,8 +590,8 @@ int __ham_splitdata_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(pgno)
 	    + sizeof(u_int32_t) + (pageimage == NULL ? 0 : pageimage->size)
 	    + sizeof(*pagelsn);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -630,7 +628,7 @@ int __ham_splitdata_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -681,7 +679,7 @@ __ham_splitdata_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\tpagelsn: [%lu][%lu]\n",
 	    (u_long)argp->pagelsn.file, (u_long)argp->pagelsn.offset);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -695,11 +693,12 @@ __ham_splitdata_read(recbuf, argpp)
 {
 	__ham_splitdata_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_splitdata_args *)__db_malloc(sizeof(__ham_splitdata_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_splitdata_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -756,8 +755,7 @@ int __ham_replace_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_replace;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -770,8 +768,8 @@ int __ham_replace_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(u_int32_t) + (olditem == NULL ? 0 : olditem->size)
 	    + sizeof(u_int32_t) + (newitem == NULL ? 0 : newitem->size)
 	    + sizeof(makedup);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -822,7 +820,7 @@ int __ham_replace_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -884,7 +882,7 @@ __ham_replace_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\n");
 	printf("\tmakedup: %lu\n", (u_long)argp->makedup);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -898,11 +896,12 @@ __ham_replace_read(recbuf, argpp)
 {
 	__ham_replace_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_replace_args *)__db_malloc(sizeof(__ham_replace_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_replace_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -968,8 +967,7 @@ int __ham_newpgno_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_newpgno;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -983,8 +981,8 @@ int __ham_newpgno_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(new_type)
 	    + sizeof(*pagelsn)
 	    + sizeof(*metalsn);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -1024,7 +1022,7 @@ int __ham_newpgno_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -1072,7 +1070,7 @@ __ham_newpgno_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\tmetalsn: [%lu][%lu]\n",
 	    (u_long)argp->metalsn.file, (u_long)argp->metalsn.offset);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -1086,11 +1084,12 @@ __ham_newpgno_read(recbuf, argpp)
 {
 	__ham_newpgno_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_newpgno_args *)__db_malloc(sizeof(__ham_newpgno_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_newpgno_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -1149,8 +1148,7 @@ int __ham_ovfl_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_ovfl;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -1161,8 +1159,8 @@ int __ham_ovfl_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(free_pgno)
 	    + sizeof(ovflpoint)
 	    + sizeof(*metalsn);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -1193,7 +1191,7 @@ int __ham_ovfl_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -1237,7 +1235,7 @@ __ham_ovfl_print(notused1, dbtp, lsnp, notused2, notused3)
 	printf("\tmetalsn: [%lu][%lu]\n",
 	    (u_long)argp->metalsn.file, (u_long)argp->metalsn.offset);
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -1251,11 +1249,12 @@ __ham_ovfl_read(recbuf, argpp)
 {
 	__ham_ovfl_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_ovfl_args *)__db_malloc(sizeof(__ham_ovfl_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_ovfl_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
@@ -1312,8 +1311,7 @@ int __ham_copypage_log(logp, txnid, ret_lsnp, flags,
 	rectype = DB_ham_copypage;
 	txn_num = txnid == NULL ? 0 : txnid->txnid;
 	if (txnid == NULL) {
-		null_lsn.file = 0;
-		null_lsn.offset = 0;
+		ZERO_LSN(null_lsn);
 		lsnp = &null_lsn;
 	} else
 		lsnp = &txnid->last_lsn;
@@ -1326,8 +1324,8 @@ int __ham_copypage_log(logp, txnid, ret_lsnp, flags,
 	    + sizeof(nnext_pgno)
 	    + sizeof(*nnextlsn)
 	    + sizeof(u_int32_t) + (page == NULL ? 0 : page->size);
-	if ((logrec.data = (void *)__db_malloc(logrec.size)) == NULL)
-		return (ENOMEM);
+	if ((ret = __os_malloc(logrec.size, NULL, &logrec.data)) != 0)
+		return (ret);
 
 	bp = logrec.data;
 	memcpy(bp, &rectype, sizeof(rectype));
@@ -1376,7 +1374,7 @@ int __ham_copypage_log(logp, txnid, ret_lsnp, flags,
 	ret = log_put(logp, ret_lsnp, (DBT *)&logrec, flags);
 	if (txnid != NULL)
 		txnid->last_lsn = *ret_lsnp;
-	__db_free(logrec.data);
+	__os_free(logrec.data, 0);
 	return (ret);
 }
 
@@ -1432,7 +1430,7 @@ __ham_copypage_print(notused1, dbtp, lsnp, notused2, notused3)
 	}
 	printf("\n");
 	printf("\n");
-	__db_free(argp);
+	__os_free(argp, 0);
 	return (0);
 }
 
@@ -1446,11 +1444,12 @@ __ham_copypage_read(recbuf, argpp)
 {
 	__ham_copypage_args *argp;
 	u_int8_t *bp;
+	int ret;
 
-	argp = (__ham_copypage_args *)__db_malloc(sizeof(__ham_copypage_args) +
-	    sizeof(DB_TXN));
-	if (argp == NULL)
-		return (ENOMEM);
+	ret = __os_malloc(sizeof(__ham_copypage_args) +
+	    sizeof(DB_TXN), NULL, &argp);
+	if (ret != 0)
+		return (ret);
 	argp->txnid = (DB_TXN *)&argp[1];
 	bp = recbuf;
 	memcpy(&argp->type, bp, sizeof(argp->type));
diff --git a/db2/hash/hash_debug.c b/db2/hash/hash_debug.c
deleted file mode 100644
index 232906ae34..0000000000
--- a/db2/hash/hash_debug.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*-
- * See the file LICENSE for redistribution information.
- *
- * Copyright (c) 1996, 1997, 1998
- *	Sleepycat Software.  All rights reserved.
- */
-/*
- * Copyright (c) 1995
- *	The President and Fellows of Harvard University.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Jeremy Rassen.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#ifndef lint
-static const char sccsid[] = "@(#)hash_debug.c	10.6 (Sleepycat) 5/7/98";
-#endif /* not lint */
-
-#ifdef DEBUG
-/*
- * PACKAGE:  hashing
- *
- * DESCRIPTION:
- *	Debug routines.
- *
- * ROUTINES:
- *
- * External
- *	__dump_bucket
- */
-#ifndef NO_SYSTEM_INCLUDES
-#include <sys/types.h>
-#endif
-
-#include "db_int.h"
-#include "db_page.h"
-#include "hash.h"
-
-/*
- * __ham_dump_bucket --
- *
- * PUBLIC: #ifdef DEBUG
- * PUBLIC: void __ham_dump_bucket __P((HTAB *, u_int32_t));
- * PUBLIC: #endif
- */
-void
-__ham_dump_bucket(hashp, bucket)
-	HTAB *hashp;
-	u_int32_t bucket;
-{
-	PAGE *p;
-	db_pgno_t pgno;
-
-	for (pgno = BUCKET_TO_PAGE(hashp, bucket); pgno != PGNO_INVALID;) {
-		if (memp_fget(hashp->dbp->mpf, &pgno, 0, &p) != 0)
-			break;
-		(void)__db_prpage(p, 1);
-		pgno = p->next_pgno;
-		(void)memp_fput(hashp->dbp->mpf, p, 0);
-	}
-}
-#endif /* DEBUG */
diff --git a/db2/hash/hash_dup.c b/db2/hash/hash_dup.c
index ba248ddb17..bb3466428d 100644
--- a/db2/hash/hash_dup.c
+++ b/db2/hash/hash_dup.c
@@ -42,7 +42,7 @@
 #include "config.h"
 
 #ifndef lint
-static const char sccsid[] = "@(#)hash_dup.c	10.14 (Sleepycat) 5/7/98";
+static const char sccsid[] = "@(#)hash_dup.c	10.27 (Sleepycat) 12/6/98";
 #endif /* not lint */
 
 /*
@@ -61,15 +61,17 @@ static const char sccsid[] = "@(#)hash_dup.c	10.14 (Sleepycat) 5/7/98";
 #ifndef NO_SYSTEM_INCLUDES
 #include <sys/types.h>
 
+#include <errno.h>
 #include <string.h>
 #endif
 
 #include "db_int.h"
 #include "db_page.h"
 #include "hash.h"
+#include "btree.h"
 
-static int __ham_check_move __P((HTAB *, HASH_CURSOR *, int32_t));
-static int __ham_dup_convert __P((HTAB *, HASH_CURSOR *));
+static int __ham_check_move __P((DBC *, int32_t));
+static int __ham_dup_convert __P((DBC *));
 static int __ham_make_dup __P((const DBT *, DBT *d, void **, u_int32_t *));
 
 /*
@@ -85,26 +87,29 @@ static int __ham_make_dup __P((const DBT *, DBT *d, void **, u_int32_t *));
  * Case 4: The element is large enough to push the duplicate set onto a
  *	   separate page.
  *
- * PUBLIC: int __ham_add_dup __P((HTAB *, HASH_CURSOR *, DBT *, u_int32_t));
+ * PUBLIC: int __ham_add_dup __P((DBC *, DBT *, u_int32_t));
  */
 int
-__ham_add_dup(hashp, hcp, nval, flags)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_add_dup(dbc, nval, flags)
+	DBC *dbc;
 	DBT *nval;
 	u_int32_t flags;
 {
-	DBT pval, tmp_val;
+	DB *dbp;
+	HASH_CURSOR *hcp;
+	DBT dbt, pval, tmp_val;
 	u_int32_t del_len, new_size;
-	int ret;
+	int cmp, ret;
 	u_int8_t *hk;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	if (flags == DB_CURRENT && hcp->dpgno == PGNO_INVALID)
 		del_len = hcp->dup_len;
 	else
 		del_len = 0;
 
-	if ((ret = __ham_check_move(hashp, hcp,
+	if ((ret = __ham_check_move(dbc,
 	    (int32_t)DUP_SIZE(nval->size) - (int32_t)del_len)) != 0)
 		return (ret);
 
@@ -117,7 +122,7 @@ __ham_add_dup(hashp, hcp, nval, flags)
 	 */
 	hk = H_PAIRDATA(hcp->pagep, hcp->bndx);
 	new_size = DUP_SIZE(nval->size) - del_len + LEN_HKEYDATA(hcp->pagep,
-	    hashp->hdr->pagesize, H_DATAINDEX(hcp->bndx));
+	    hcp->hdr->pagesize, H_DATAINDEX(hcp->bndx));
 
 	/*
 	 * We convert to off-page duplicates if the item is a big item,
@@ -125,10 +130,10 @@ __ham_add_dup(hashp, hcp, nval, flags)
 	 * if there isn't enough room on this page to add the next item.
 	 */
 	if (HPAGE_PTYPE(hk) != H_OFFDUP &&
-	    (HPAGE_PTYPE(hk) == H_OFFPAGE || ISBIG(hashp, new_size) ||
+	    (HPAGE_PTYPE(hk) == H_OFFPAGE || ISBIG(hcp, new_size) ||
 	    DUP_SIZE(nval->size) - del_len > P_FREESPACE(hcp->pagep))) {
 
-		if ((ret = __ham_dup_convert(hashp, hcp)) != 0)
+		if ((ret = __ham_dup_convert(dbc)) != 0)
 			return (ret);
 		else
 			hk = H_PAIRDATA(hcp->pagep, hcp->bndx);
@@ -140,30 +145,44 @@ __ham_add_dup(hashp, hcp, nval, flags)
 			HPAGE_PTYPE(hk) = H_DUPLICATE;
 			pval.flags = 0;
 			pval.data = HKEYDATA_DATA(hk);
-			pval.size = LEN_HDATA(hcp->pagep, hashp->hdr->pagesize,
+			pval.size = LEN_HDATA(hcp->pagep, dbp->pgsize,
 			    hcp->bndx);
 			if ((ret =
-			    __ham_make_dup(&pval, &tmp_val, &hcp->big_data,
-			    &hcp->big_datalen)) != 0 || (ret =
-			    __ham_replpair(hashp, hcp, &tmp_val, 1)) != 0)
+			    __ham_make_dup(&pval, &tmp_val, &dbc->rdata.data,
+			    &dbc->rdata.size)) != 0 || (ret =
+			    __ham_replpair(dbc, &tmp_val, 1)) != 0)
 				return (ret);
 		}
 
 		/* Now make the new entry a duplicate. */
 		if ((ret = __ham_make_dup(nval,
-		    &tmp_val, &hcp->big_data, &hcp->big_datalen)) != 0)
+		    &tmp_val, &dbc->rdata.data, &dbc->rdata.size)) != 0)
 			return (ret);
 
 		tmp_val.dlen = 0;
 		switch (flags) {			/* On page. */
 		case DB_KEYFIRST:
-			tmp_val.doff = 0;
-			break;
 		case DB_KEYLAST:
-			tmp_val.doff = LEN_HDATA(hcp->pagep,
-			    hashp->hdr->pagesize, hcp->bndx);
+			if (dbp->dup_compare != NULL)
+				__ham_dsearch(dbc, nval, &tmp_val.doff, &cmp);
+			else if (flags == DB_KEYFIRST)
+				tmp_val.doff = 0;
+			else
+				tmp_val.doff = LEN_HDATA(hcp->pagep,
+				    hcp->hdr->pagesize, hcp->bndx);
 			break;
 		case DB_CURRENT:
+			/*
+			 * If we have a sort function, we need to verify that
+			 * the new item sorts identically to the old item.
+			 */
+			if (dbp->dup_compare != NULL) {
+				dbt.data = HKEYDATA_DATA(H_PAIRDATA(hcp->pagep,
+				    hcp->bndx)) + hcp->dup_off;
+				dbt.size = DUP_SIZE(hcp->dup_len);
+				if (dbp->dup_compare(nval, &dbt) != 0)
+					return (EINVAL);
+			}
 			tmp_val.doff = hcp->dup_off;
 			tmp_val.dlen = DUP_SIZE(hcp->dup_len);
 			break;
@@ -175,9 +194,9 @@ __ham_add_dup(hashp, hcp, nval, flags)
 			break;
 		}
 		/* Add the duplicate. */
-		ret = __ham_replpair(hashp, hcp, &tmp_val, 0);
+		ret = __ham_replpair(dbc, &tmp_val, 0);
 		if (ret == 0)
-			ret = __ham_dirty_page(hashp, hcp->pagep);
+			ret = __ham_dirty_page(dbp, hcp->pagep);
 		__ham_c_update(hcp, hcp->pgno, tmp_val.size, 1, 1);
 		return (ret);
 	}
@@ -190,27 +209,48 @@ __ham_add_dup(hashp, hcp, nval, flags)
 
 	switch (flags) {
 	case DB_KEYFIRST:
+		if (dbp->dup_compare != NULL)
+			goto sorted_dups;
 		/*
 		 * The only way that we are already on a dup page is
 		 * if we just converted the on-page representation.
 		 * In that case, we've only got one page of duplicates.
 		 */
 		if (hcp->dpagep == NULL && (ret =
-		    __db_dend(hashp->dbp, hcp->dpgno, &hcp->dpagep)) != 0)
+		    __db_dend(dbc, hcp->dpgno, &hcp->dpagep)) != 0)
 			return (ret);
 		hcp->dndx = 0;
 		break;
 	case DB_KEYLAST:
-		if (hcp->dpagep == NULL && (ret =
-		    __db_dend(hashp->dbp, hcp->dpgno, &hcp->dpagep)) != 0)
-			return (ret);
-		hcp->dpgno = PGNO(hcp->dpagep);
-		hcp->dndx = NUM_ENT(hcp->dpagep);
+		if (dbp->dup_compare != NULL) {
+sorted_dups:		if ((ret = __db_dsearch(dbc, 1, nval,
+			    hcp->dpgno, &hcp->dndx, &hcp->dpagep, &cmp)) != 0)
+				return (ret);
+			if (cmp == 0)
+				hcp->dpgno = PGNO(hcp->dpagep);
+		} else {
+			if (hcp->dpagep == NULL && (ret =
+			    __db_dend(dbc, hcp->dpgno, &hcp->dpagep)) != 0)
+				return (ret);
+			hcp->dpgno = PGNO(hcp->dpagep);
+			hcp->dndx = NUM_ENT(hcp->dpagep);
+		}
 		break;
 	case DB_CURRENT:
-		if ((ret = __db_ditem(hashp->dbp, hcp->dpagep, hcp->dndx,
-		    BKEYDATA_SIZE(GET_BKEYDATA(hcp->dpagep, hcp->dndx)->len)))
-		    != 0)
+		if (dbp->dup_compare != NULL && __bam_cmp(dbp,
+		    nval, hcp->dpagep, hcp->dndx, dbp->dup_compare) != 0)
+			return (EINVAL);
+		switch (GET_BKEYDATA(hcp->dpagep, hcp->dndx)->type) {
+		case B_KEYDATA:
+			del_len = BKEYDATA_SIZE(GET_BKEYDATA(hcp->dpagep,
+			    hcp->dndx)->len);
+			break;
+		case B_OVERFLOW:
+			del_len = BOVERFLOW_SIZE;
+			break;
+		}
+		if ((ret =
+		    __db_ditem(dbc, hcp->dpagep, hcp->dndx, del_len)) != 0)
 			return (ret);
 		break;
 	case DB_BEFORE:	/* The default behavior is correct. */
@@ -220,7 +260,7 @@ __ham_add_dup(hashp, hcp, nval, flags)
 		break;
 	}
 
-	ret = __db_dput(hashp->dbp,
+	ret = __db_dput(dbc,
 	    nval, &hcp->dpagep, &hcp->dndx, __ham_overflow_page);
 	hcp->pgno = PGNO(hcp->pagep);
 	__ham_c_update(hcp, hcp->pgno, nval->size, 1, 1);
@@ -231,22 +271,25 @@ __ham_add_dup(hashp, hcp, nval, flags)
  * Convert an on-page set of duplicates to an offpage set of duplicates.
  */
 static int
-__ham_dup_convert(hashp, hcp)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_dup_convert(dbc)
+	DBC *dbc;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	BOVERFLOW bo;
 	DBT dbt;
 	HOFFPAGE ho;
-	db_indx_t dndx, len;
+	db_indx_t dndx, i, len, off;
 	int ret;
 	u_int8_t *p, *pend;
 
 	/*
 	 * Create a new page for the duplicates.
 	 */
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	if ((ret =
-	    __ham_overflow_page(hashp->dbp, P_DUPLICATE, &hcp->dpagep)) != 0)
+	    __ham_overflow_page(dbc, P_DUPLICATE, &hcp->dpagep)) != 0)
 		return (ret);
 	hcp->dpagep->type = P_DUPLICATE;
 	hcp->dpgno = PGNO(hcp->dpagep);
@@ -254,67 +297,80 @@ __ham_dup_convert(hashp, hcp)
 	/*
 	 * Now put the duplicates onto the new page.
 	 */
+	dndx = 0;
 	dbt.flags = 0;
 	switch (HPAGE_PTYPE(H_PAIRDATA(hcp->pagep, hcp->bndx))) {
 	case H_KEYDATA:
 		/* Simple case, one key on page; move it to dup page. */
-		dndx = 0;
 		dbt.size =
-		    LEN_HDATA(hcp->pagep, hashp->hdr->pagesize, hcp->bndx);
+		    LEN_HDATA(hcp->pagep, hcp->hdr->pagesize, hcp->bndx);
 		dbt.data = HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx));
-		ret = __db_pitem(hashp->dbp, hcp->dpagep,
+		ret = __db_pitem(dbc, hcp->dpagep,
 		    (u_int32_t)dndx, BKEYDATA_SIZE(dbt.size), NULL, &dbt);
 		if (ret == 0)
-			__ham_dirty_page(hashp, hcp->dpagep);
+			__ham_dirty_page(dbp, hcp->dpagep);
 		break;
 	case H_OFFPAGE:
 		/* Simple case, one key on page; move it to dup page. */
-		dndx = 0;
 		memcpy(&ho,
 		    P_ENTRY(hcp->pagep, H_DATAINDEX(hcp->bndx)), HOFFPAGE_SIZE);
+		UMRW(bo.unused1);
 		B_TSET(bo.type, ho.type, 0);
+		UMRW(bo.unused2);
 		bo.pgno = ho.pgno;
 		bo.tlen = ho.tlen;
 		dbt.size = BOVERFLOW_SIZE;
 		dbt.data = &bo;
 
-		ret = __db_pitem(hashp->dbp, hcp->dpagep,
+		ret = __db_pitem(dbc, hcp->dpagep,
 		   (u_int32_t)dndx, dbt.size, &dbt, NULL);
 		if (ret == 0)
-			__ham_dirty_page(hashp, hcp->dpagep);
+			__ham_dirty_page(dbp, hcp->dpagep);
 		break;
 	case H_DUPLICATE:
 		p = HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx));
 		pend = p +
-		    LEN_HDATA(hcp->pagep, hashp->hdr->pagesize, hcp->bndx);
+		    LEN_HDATA(hcp->pagep, hcp->hdr->pagesize, hcp->bndx);
 
-		for (dndx = 0; p < pend; dndx++) {
+		/*
+		 * We need to maintain the duplicate cursor position.
+		 * Keep track of where we are in the duplicate set via
+		 * the offset, and when it matches the one in the cursor,
+		 * set the off-page duplicate cursor index to the current
+		 * index.
+		 */
+		for (off = 0, i = 0; p < pend; i++) {
+			if (off == hcp->dup_off)
+				dndx = i;
 			memcpy(&len, p, sizeof(db_indx_t));
 			dbt.size = len;
 			p += sizeof(db_indx_t);
 			dbt.data = p;
 			p += len + sizeof(db_indx_t);
-			ret = __db_dput(hashp->dbp, &dbt,
-			    &hcp->dpagep, &dndx, __ham_overflow_page);
+			off += len + 2 * sizeof(db_indx_t);
+			ret = __db_dput(dbc, &dbt,
+			    &hcp->dpagep, &i, __ham_overflow_page);
 			if (ret != 0)
 				break;
 		}
 		break;
 	default:
-		ret = __db_pgfmt(hashp->dbp, (u_long)hcp->pgno);
+		ret = __db_pgfmt(dbp, (u_long)hcp->pgno);
+		break;
 	}
 	if (ret == 0) {
 		/*
 		 * Now attach this to the source page in place of
 		 * the old duplicate item.
 		 */
-		__ham_move_offpage(hashp, hcp->pagep,
+		__ham_move_offpage(dbc, hcp->pagep,
 		    (u_int32_t)H_DATAINDEX(hcp->bndx), hcp->dpgno);
 
 		/* Can probably just do a "put" here. */
-		ret = __ham_dirty_page(hashp, hcp->pagep);
+		ret = __ham_dirty_page(dbp, hcp->pagep);
+		hcp->dndx = dndx;
 	} else {
-		(void)__ham_del_page(hashp->dbp, hcp->dpagep);
+		(void)__ham_del_page(dbc, hcp->dpagep);
 		hcp->dpagep = NULL;
 	}
 	return (ret);
@@ -354,11 +410,12 @@ __ham_make_dup(notdup, duplicate, bufp, sizep)
 }
 
 static int
-__ham_check_move(hashp, hcp, add_len)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_check_move(dbc, add_len)
+	DBC *dbc;
 	int32_t add_len;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DBT k, d;
 	DB_LSN new_lsn;
 	PAGE *next_pagep;
@@ -367,6 +424,8 @@ __ham_check_move(hashp, hcp, add_len)
 	u_int8_t *hk;
 	int ret;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	/*
 	 * Check if we can do whatever we need to on this page.  If not,
 	 * then we'll have to move the current element to a new page.
@@ -381,7 +440,7 @@ __ham_check_move(hashp, hcp, add_len)
 		return (0);
 
 	old_len =
-	    LEN_HITEM(hcp->pagep, hashp->hdr->pagesize, H_DATAINDEX(hcp->bndx));
+	    LEN_HITEM(hcp->pagep, hcp->hdr->pagesize, H_DATAINDEX(hcp->bndx));
 	new_datalen = old_len - HKEYDATA_SIZE(0) + add_len;
 
 	/*
@@ -392,11 +451,11 @@ __ham_check_move(hashp, hcp, add_len)
 	 *    threshold, but the new data won't fit on the page.
 	 * If neither of these is true, then we can return.
 	 */
-	if (ISBIG(hashp, new_datalen) && (old_len > HOFFDUP_SIZE ||
+	if (ISBIG(hcp, new_datalen) && (old_len > HOFFDUP_SIZE ||
 	    HOFFDUP_SIZE - old_len <= P_FREESPACE(hcp->pagep)))
 		return (0);
 
-	if (!ISBIG(hashp, new_datalen) &&
+	if (!ISBIG(hcp, new_datalen) &&
 	    add_len <= (int32_t)P_FREESPACE(hcp->pagep))
 		return (0);
 
@@ -405,18 +464,18 @@ __ham_check_move(hashp, hcp, add_len)
 	 * Check if there are more pages in the chain.
 	 */
 
-	new_datalen = ISBIG(hashp, new_datalen) ?
+	new_datalen = ISBIG(hcp, new_datalen) ?
 	    HOFFDUP_SIZE : HKEYDATA_SIZE(new_datalen);
 
 	next_pagep = NULL;
 	for (next_pgno = NEXT_PGNO(hcp->pagep); next_pgno != PGNO_INVALID;
 	    next_pgno = NEXT_PGNO(next_pagep)) {
 		if (next_pagep != NULL &&
-		    (ret = __ham_put_page(hashp->dbp, next_pagep, 0)) != 0)
+		    (ret = __ham_put_page(dbp, next_pagep, 0)) != 0)
 			return (ret);
 
 		if ((ret =
-		    __ham_get_page(hashp->dbp, next_pgno, &next_pagep)) != 0)
+		    __ham_get_page(dbp, next_pgno, &next_pagep)) != 0)
 			return (ret);
 
 		if (P_FREESPACE(next_pagep) >= new_datalen)
@@ -424,17 +483,17 @@ __ham_check_move(hashp, hcp, add_len)
 	}
 
 	/* No more pages, add one. */
-	if (next_pagep == NULL &&
-	    (ret = __ham_add_ovflpage(hashp, hcp->pagep, 0, &next_pagep)) != 0)
+	if (next_pagep == NULL && (ret = __ham_add_ovflpage(dbc,
+	    hcp->pagep, 0, &next_pagep)) != 0)
 		return (ret);
 
 	/* Add new page at the end of the chain. */
-	if (P_FREESPACE(next_pagep) < new_datalen &&
-	    (ret = __ham_add_ovflpage(hashp, next_pagep, 1, &next_pagep)) != 0)
+	if (P_FREESPACE(next_pagep) < new_datalen && (ret =
+	    __ham_add_ovflpage(dbc, next_pagep, 1, &next_pagep)) != 0)
 		return (ret);
 
 	/* Copy the item to the new page. */
-	if (DB_LOGGING(hashp->dbp)) {
+	if (DB_LOGGING(hcp->dbc)) {
 		rectype = PUTPAIR;
 		k.flags = 0;
 		d.flags = 0;
@@ -447,7 +506,7 @@ __ham_check_move(hashp, hcp, add_len)
 			k.data =
 			    HKEYDATA_DATA(H_PAIRKEY(hcp->pagep, hcp->bndx));
 			k.size = LEN_HKEY(hcp->pagep,
-			    hashp->hdr->pagesize, hcp->bndx);
+			    hcp->hdr->pagesize, hcp->bndx);
 		}
 
 		if (HPAGE_PTYPE(hk) == H_OFFPAGE) {
@@ -458,13 +517,13 @@ __ham_check_move(hashp, hcp, add_len)
 			d.data =
 			    HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx));
 			d.size = LEN_HDATA(hcp->pagep,
-			    hashp->hdr->pagesize, hcp->bndx);
+			    hcp->hdr->pagesize, hcp->bndx);
 		}
 
 
-		if ((ret = __ham_insdel_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, rectype,
-		    hashp->dbp->log_fileid, PGNO(next_pagep),
+		if ((ret = __ham_insdel_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, rectype,
+		    dbp->log_fileid, PGNO(next_pagep),
 		    (u_int32_t)H_NUMPAIRS(next_pagep), &LSN(next_pagep),
 		    &k, &d)) != 0)
 			return (ret);
@@ -473,13 +532,15 @@ __ham_check_move(hashp, hcp, add_len)
 		LSN(next_pagep) = new_lsn;	/* Structure assignment. */
 	}
 
-	__ham_copy_item(hashp, hcp->pagep, H_KEYINDEX(hcp->bndx), next_pagep);
-	__ham_copy_item(hashp, hcp->pagep, H_DATAINDEX(hcp->bndx), next_pagep);
+	__ham_copy_item(dbp->pgsize,
+	    hcp->pagep, H_KEYINDEX(hcp->bndx), next_pagep);
+	__ham_copy_item(dbp->pgsize,
+	    hcp->pagep, H_DATAINDEX(hcp->bndx), next_pagep);
 
 	/* Now delete the pair from the current page. */
-	ret = __ham_del_pair(hashp, hcp, 0);
+	ret = __ham_del_pair(dbc, 0);
 
-	(void)__ham_put_page(hashp->dbp, hcp->pagep, 1);
+	(void)__ham_put_page(dbp, hcp->pagep, 1);
 	hcp->pagep = next_pagep;
 	hcp->pgno = PGNO(hcp->pagep);
 	hcp->bndx = H_NUMPAIRS(hcp->pagep) - 1;
@@ -488,19 +549,25 @@ __ham_check_move(hashp, hcp, add_len)
 }
 
 /*
- * Replace an onpage set of duplicates with the OFFDUP structure that
- * references the duplicate page.
- * XXX This is really just a special case of __onpage_replace; we should
+ * __ham_move_offpage --
+ *	Replace an onpage set of duplicates with the OFFDUP structure
+ *	that references the duplicate page.
+ *
+ * XXX
+ * This is really just a special case of __onpage_replace; we should
  * probably combine them.
- * PUBLIC: void __ham_move_offpage __P((HTAB *, PAGE *, u_int32_t, db_pgno_t));
+ *
+ * PUBLIC: void __ham_move_offpage __P((DBC *, PAGE *, u_int32_t, db_pgno_t));
  */
 void
-__ham_move_offpage(hashp, pagep, ndx, pgno)
-	HTAB *hashp;
+__ham_move_offpage(dbc, pagep, ndx, pgno)
+	DBC *dbc;
 	PAGE *pagep;
 	u_int32_t ndx;
 	db_pgno_t pgno;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DBT new_dbt;
 	DBT old_dbt;
 	HOFFDUP od;
@@ -508,22 +575,27 @@ __ham_move_offpage(hashp, pagep, ndx, pgno)
 	int32_t shrink;
 	u_int8_t *src;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	od.type = H_OFFDUP;
+	UMRW(od.unused[0]);
+	UMRW(od.unused[1]);
+	UMRW(od.unused[2]);
 	od.pgno = pgno;
 
-	if (DB_LOGGING(hashp->dbp)) {
+	if (DB_LOGGING(dbc)) {
 		new_dbt.data = &od;
 		new_dbt.size = HOFFDUP_SIZE;
 		old_dbt.data = P_ENTRY(pagep, ndx);
-		old_dbt.size = LEN_HITEM(pagep, hashp->hdr->pagesize, ndx);
-		(void)__ham_replace_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &LSN(pagep), 0,
-		    hashp->dbp->log_fileid, PGNO(pagep), (u_int32_t)ndx,
-		    &LSN(pagep), -1, &old_dbt, &new_dbt, 0);
+		old_dbt.size = LEN_HITEM(pagep, hcp->hdr->pagesize, ndx);
+		(void)__ham_replace_log(dbp->dbenv->lg_info,
+		    dbc->txn, &LSN(pagep), 0, dbp->log_fileid,
+		    PGNO(pagep), (u_int32_t)ndx, &LSN(pagep), -1,
+		    &old_dbt, &new_dbt, 0);
 	}
 
 	shrink =
-	    LEN_HITEM(pagep, hashp->hdr->pagesize, ndx) - HOFFDUP_SIZE;
+	    LEN_HITEM(pagep, hcp->hdr->pagesize, ndx) - HOFFDUP_SIZE;
 
 	if (shrink != 0) {
 		/* Copy data. */
@@ -539,3 +611,46 @@ __ham_move_offpage(hashp, pagep, ndx, pgno)
 	/* Now copy the offdup entry onto the page. */
 	memcpy(P_ENTRY(pagep, ndx), &od, HOFFDUP_SIZE);
 }
+
+/*
+ * __ham_dsearch:
+ *	Locate a particular duplicate in a duplicate set.
+ *
+ * PUBLIC: void __ham_dsearch __P((DBC *, DBT *, u_int32_t *, int *));
+ */
+void
+__ham_dsearch(dbc, dbt, offp, cmpp)
+	DBC *dbc;
+	DBT *dbt;
+	u_int32_t *offp;
+	int *cmpp;
+{
+	DB *dbp;
+	HASH_CURSOR *hcp;
+	DBT cur;
+	db_indx_t i, len;
+	int (*func) __P((const DBT *, const DBT *));
+	u_int8_t *data;
+
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+	if (dbp->dup_compare == NULL)
+		func = __bam_defcmp;
+	else
+		func = dbp->dup_compare;
+
+	i = F_ISSET(dbc, DBC_CONTINUE) ? hcp->dup_off: 0;
+	data = HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx)) + i;
+	while (i < LEN_HDATA(hcp->pagep, hcp->hdr->pagesize, hcp->bndx)) {
+		memcpy(&len, data, sizeof(db_indx_t));
+		data += sizeof(db_indx_t);
+		cur.data = data;
+		cur.size = (u_int32_t)len;
+		*cmpp = func(dbt, &cur);
+		if (*cmpp == 0 || (*cmpp < 0 && dbp->dup_compare != NULL))
+			break;
+		i += len + 2 * sizeof(db_indx_t);
+		data += len + sizeof(db_indx_t);
+	}
+	*offp = i;
+}
diff --git a/db2/hash/hash_page.c b/db2/hash/hash_page.c
index 5b3463947b..3419c1215c 100644
--- a/db2/hash/hash_page.c
+++ b/db2/hash/hash_page.c
@@ -47,7 +47,7 @@
 #include "config.h"
 
 #ifndef lint
-static const char sccsid[] = "@(#)hash_page.c	10.40 (Sleepycat) 6/2/98";
+static const char sccsid[] = "@(#)hash_page.c	10.55 (Sleepycat) 1/3/99";
 #endif /* not lint */
 
 /*
@@ -77,107 +77,118 @@ static const char sccsid[] = "@(#)hash_page.c	10.40 (Sleepycat) 6/2/98";
 #include "db_page.h"
 #include "hash.h"
 
-static int __ham_lock_bucket __P((DB *, HASH_CURSOR *, db_lockmode_t));
+static int __ham_lock_bucket __P((DBC *, db_lockmode_t));
 
 #ifdef DEBUG_SLOW
-static void  __account_page(HTAB *, db_pgno_t, int);
+static void  __account_page(DB *, db_pgno_t, int);
 #endif
 
 /*
- * PUBLIC: int __ham_item __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_item __P((DBC *, db_lockmode_t));
  */
 int
-__ham_item(hashp, cursorp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	db_pgno_t next_pgno;
 	int ret;
 
-	if (F_ISSET(cursorp, H_DELETED))
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+
+	if (F_ISSET(hcp, H_DELETED))
 		return (EINVAL);
-	F_CLR(cursorp, H_OK | H_NOMORE);
+	F_CLR(hcp, H_OK | H_NOMORE);
 
 	/* Check if we need to get a page for this cursor. */
-	if ((ret = __ham_get_cpage(hashp, cursorp, mode)) != 0)
+	if ((ret = __ham_get_cpage(dbc, mode)) != 0)
 		return (ret);
 
 	/* Check if we are looking for space in which to insert an item. */
-	if (cursorp->seek_size && cursorp->seek_found_page == PGNO_INVALID
-	    && cursorp->seek_size < P_FREESPACE(cursorp->pagep))
-		cursorp->seek_found_page = cursorp->pgno;
+	if (hcp->seek_size && hcp->seek_found_page == PGNO_INVALID
+	    && hcp->seek_size < P_FREESPACE(hcp->pagep))
+		hcp->seek_found_page = hcp->pgno;
 
 	/* Check if we need to go on to the next page. */
-	if (F_ISSET(cursorp, H_ISDUP) && cursorp->dpgno == PGNO_INVALID)
+	if (F_ISSET(hcp, H_ISDUP) && hcp->dpgno == PGNO_INVALID)
 		/*
 		 * ISDUP is set, and offset is at the beginning of the datum.
 		 * We need to grab the length of the datum, then set the datum
 		 * pointer to be the beginning of the datum.
 		 */
-		memcpy(&cursorp->dup_len,
-		    HKEYDATA_DATA(H_PAIRDATA(cursorp->pagep, cursorp->bndx)) +
-		    cursorp->dup_off, sizeof(db_indx_t));
-	else if (F_ISSET(cursorp, H_ISDUP)) {
+		memcpy(&hcp->dup_len,
+		    HKEYDATA_DATA(H_PAIRDATA(hcp->pagep, hcp->bndx)) +
+		    hcp->dup_off, sizeof(db_indx_t));
+	else if (F_ISSET(hcp, H_ISDUP)) {
 		/* Make sure we're not about to run off the page. */
-		if (cursorp->dpagep == NULL && (ret = __ham_get_page(hashp->dbp,
-		    cursorp->dpgno, &cursorp->dpagep)) != 0)
+		if (hcp->dpagep == NULL && (ret = __ham_get_page(dbp,
+		    hcp->dpgno, &hcp->dpagep)) != 0)
 			return (ret);
 
-		if (cursorp->dndx >= NUM_ENT(cursorp->dpagep)) {
-			if (NEXT_PGNO(cursorp->dpagep) == PGNO_INVALID) {
-				if ((ret = __ham_put_page(hashp->dbp,
-				    cursorp->dpagep, 0)) != 0)
+		if (hcp->dndx >= NUM_ENT(hcp->dpagep)) {
+			if (NEXT_PGNO(hcp->dpagep) == PGNO_INVALID) {
+				if (F_ISSET(hcp, H_DUPONLY)) {
+					F_CLR(hcp, H_OK);
+					F_SET(hcp, H_NOMORE);
+					return (0);
+				}
+				if ((ret = __ham_put_page(dbp,
+				    hcp->dpagep, 0)) != 0)
 					return (ret);
-				F_CLR(cursorp, H_ISDUP);
-				cursorp->dpagep = NULL;
-				cursorp->dpgno = PGNO_INVALID;
-				cursorp->dndx = NDX_INVALID;
-				cursorp->bndx++;
-			} else if ((ret = __ham_next_cpage(hashp, cursorp,
-			    NEXT_PGNO(cursorp->dpagep), 0, H_ISDUP)) != 0)
+				F_CLR(hcp, H_ISDUP);
+				hcp->dpagep = NULL;
+				hcp->dpgno = PGNO_INVALID;
+				hcp->dndx = NDX_INVALID;
+				hcp->bndx++;
+			} else if ((ret = __ham_next_cpage(dbc,
+			    NEXT_PGNO(hcp->dpagep), 0, H_ISDUP)) != 0)
 				return (ret);
 		}
 	}
 
-	if (cursorp->bndx >= (db_indx_t)H_NUMPAIRS(cursorp->pagep)) {
+	if (hcp->bndx >= (db_indx_t)H_NUMPAIRS(hcp->pagep)) {
 		/* Fetch next page. */
-		if (NEXT_PGNO(cursorp->pagep) == PGNO_INVALID) {
-			F_SET(cursorp, H_NOMORE);
-			if (cursorp->dpagep != NULL &&
-			    (ret = __ham_put_page(hashp->dbp,
-			    cursorp->dpagep, 0)) != 0)
+		if (NEXT_PGNO(hcp->pagep) == PGNO_INVALID) {
+			F_SET(hcp, H_NOMORE);
+			if (hcp->dpagep != NULL &&
+			    (ret = __ham_put_page(dbp, hcp->dpagep, 0)) != 0)
 				return (ret);
-			cursorp->dpgno = PGNO_INVALID;
+			hcp->dpgno = PGNO_INVALID;
 			return (DB_NOTFOUND);
 		}
-		next_pgno = NEXT_PGNO(cursorp->pagep);
-		cursorp->bndx = 0;
-		if ((ret = __ham_next_cpage(hashp,
-		    cursorp, next_pgno, 0, 0)) != 0)
+		next_pgno = NEXT_PGNO(hcp->pagep);
+		hcp->bndx = 0;
+		if ((ret = __ham_next_cpage(dbc, next_pgno, 0, 0)) != 0)
 			return (ret);
 	}
 
-	F_SET(cursorp, H_OK);
+	F_SET(hcp, H_OK);
 	return (0);
 }
 
 /*
- * PUBLIC: int __ham_item_reset __P((HTAB *, HASH_CURSOR *));
+ * PUBLIC: int __ham_item_reset __P((DBC *));
  */
 int
-__ham_item_reset(hashp, cursorp)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_reset(dbc)
+	DBC *dbc;
 {
+	HASH_CURSOR *hcp;
+	DB *dbp;
 	int ret;
 
-	if (cursorp->pagep)
-		ret = __ham_put_page(hashp->dbp, cursorp->pagep, 0);
-	else
-		ret = 0;
-
-	__ham_item_init(cursorp);
+	ret = 0;
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+	if (hcp->pagep != NULL)
+		ret = __ham_put_page(dbp, hcp->pagep, 0);
+	if (ret == 0 && hcp->dpagep != NULL)
+		ret = __ham_put_page(dbp, hcp->dpagep, 0);
+
+	__ham_item_init(hcp);
 	return (ret);
 }
 
@@ -185,57 +196,67 @@ __ham_item_reset(hashp, cursorp)
  * PUBLIC: void __ham_item_init __P((HASH_CURSOR *));
  */
 void
-__ham_item_init(cursorp)
-	HASH_CURSOR *cursorp;
+__ham_item_init(hcp)
+	HASH_CURSOR *hcp;
 {
-	cursorp->pagep = NULL;
-	cursorp->bucket = BUCKET_INVALID;
-	cursorp->lock = 0;
-	cursorp->bndx = NDX_INVALID;
-	cursorp->pgno = PGNO_INVALID;
-	cursorp->dpgno = PGNO_INVALID;
-	cursorp->dndx = NDX_INVALID;
-	cursorp->dpagep = NULL;
-	cursorp->flags = 0;
-	cursorp->seek_size = 0;
-	cursorp->seek_found_page = PGNO_INVALID;
+	/*
+	 * If this cursor still holds any locks, we must
+	 * release them if we are not running with transactions.
+	 */
+	if (hcp->lock && hcp->dbc->txn == NULL)
+	    (void)lock_put(hcp->dbc->dbp->dbenv->lk_info, hcp->lock);
+
+	/*
+	 * The following fields must *not* be initialized here
+	 * because they may have meaning across inits.
+	 * 	hlock, hdr, split_buf, stats
+	 */
+	hcp->bucket = BUCKET_INVALID;
+	hcp->lbucket = BUCKET_INVALID;
+	hcp->lock = 0;
+	hcp->pagep = NULL;
+	hcp->pgno = PGNO_INVALID;
+	hcp->bndx = NDX_INVALID;
+	hcp->dpagep = NULL;
+	hcp->dpgno = PGNO_INVALID;
+	hcp->dndx = NDX_INVALID;
+	hcp->dup_off = 0;
+	hcp->dup_len = 0;
+	hcp->dup_tlen = 0;
+	hcp->seek_size = 0;
+	hcp->seek_found_page = PGNO_INVALID;
+	hcp->flags = 0;
 }
 
 /*
- * PUBLIC: int __ham_item_done __P((HTAB *, HASH_CURSOR *, int));
+ * PUBLIC: int __ham_item_done __P((DBC *, int));
  */
 int
-__ham_item_done(hashp, cursorp, dirty)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_done(dbc, dirty)
+	DBC *dbc;
 	int dirty;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	int ret, t_ret;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	t_ret = ret = 0;
 
-	if (cursorp->pagep)
-		ret = __ham_put_page(hashp->dbp, cursorp->pagep,
-		    dirty && cursorp->dpagep == NULL);
-	cursorp->pagep = NULL;
+	if (hcp->pagep)
+		ret = __ham_put_page(dbp, hcp->pagep,
+		    dirty && hcp->dpagep == NULL);
+	hcp->pagep = NULL;
 
-	if (cursorp->dpagep)
-		t_ret = __ham_put_page(hashp->dbp, cursorp->dpagep, dirty);
-	cursorp->dpagep = NULL;
+	if (hcp->dpagep)
+		t_ret = __ham_put_page(dbp, hcp->dpagep, dirty);
+	hcp->dpagep = NULL;
 
 	if (ret == 0 && t_ret != 0)
 		ret = t_ret;
 
 	/*
-	 * If we are running with transactions, then we must
-	 * not relinquish locks explicitly.
-	 */
-	if (cursorp->lock && hashp->dbp->txn == NULL)
-	    t_ret = lock_put(hashp->dbp->dbenv->lk_info, cursorp->lock);
-	cursorp->lock = 0;
-
-
-	/*
 	 * We don't throw out the page number since we might want to
 	 * continue getting on this page.
 	 */
@@ -245,40 +266,42 @@ __ham_item_done(hashp, cursorp, dirty)
 /*
  * Returns the last item in a bucket.
  *
- * PUBLIC: int __ham_item_last __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_item_last __P((DBC *, db_lockmode_t));
  */
 int
-__ham_item_last(hashp, cursorp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_last(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	HASH_CURSOR *hcp;
 	int ret;
 
-	if ((ret = __ham_item_reset(hashp, cursorp)) != 0)
+	hcp = (HASH_CURSOR *)dbc->internal;
+	if ((ret = __ham_item_reset(dbc)) != 0)
 		return (ret);
 
-	cursorp->bucket = hashp->hdr->max_bucket;
-	F_SET(cursorp, H_OK);
-	return (__ham_item_prev(hashp, cursorp, mode));
+	hcp->bucket = hcp->hdr->max_bucket;
+	F_SET(hcp, H_OK);
+	return (__ham_item_prev(dbc, mode));
 }
 
 /*
- * PUBLIC: int __ham_item_first __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_item_first __P((DBC *, db_lockmode_t));
  */
 int
-__ham_item_first(hashp, cursorp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_first(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	HASH_CURSOR *hcp;
 	int ret;
 
-	if ((ret = __ham_item_reset(hashp, cursorp)) != 0)
+	hcp = (HASH_CURSOR *)dbc->internal;
+	if ((ret = __ham_item_reset(dbc)) != 0)
 		return (ret);
-	F_SET(cursorp, H_OK);
-	cursorp->bucket = 0;
-	return (__ham_item_next(hashp, cursorp, mode));
+	F_SET(hcp, H_OK);
+	hcp->bucket = 0;
+	return (__ham_item_next(dbc, mode));
 }
 
 /*
@@ -287,17 +310,20 @@ __ham_item_first(hashp, cursorp, mode)
  *	bigkeys, just returns the page number and index of the bigkey
  *	pointer pair.
  *
- * PUBLIC: int __ham_item_prev __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_item_prev __P((DBC *, db_lockmode_t));
  */
 int
-__ham_item_prev(hashp, cursorp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_prev(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	db_pgno_t next_pgno;
 	int ret;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	/*
 	 * There are N cases for backing up in a hash file.
 	 * Case 1: In the middle of a page, no duplicates, just dec the index.
@@ -307,52 +333,56 @@ __ham_item_prev(hashp, cursorp, mode)
 	 * Case 4: At the beginning of a page; go to previous page.
 	 * Case 5: At the beginning of a bucket; go to prev bucket.
 	 */
-	F_CLR(cursorp, H_OK | H_NOMORE | H_DELETED);
+	F_CLR(hcp, H_OK | H_NOMORE | H_DELETED);
 
 	/*
 	 * First handle the duplicates.  Either you'll get the key here
 	 * or you'll exit the duplicate set and drop into the code below
 	 * to handle backing up through keys.
 	 */
-	if (F_ISSET(cursorp, H_ISDUP)) {
-		if (cursorp->dpgno == PGNO_INVALID) {
+	if (F_ISSET(hcp, H_ISDUP)) {
+		if (hcp->dpgno == PGNO_INVALID) {
 			/* Duplicates are on-page. */
-			if (cursorp->dup_off != 0) {
-				if ((ret = __ham_get_cpage(hashp,
-				    cursorp, mode)) != 0)
+			if (hcp->dup_off != 0) {
+				if ((ret = __ham_get_cpage(dbc, mode)) != 0)
 					return (ret);
 				else {
 					HASH_CURSOR *h;
-					h = cursorp;
+					h = hcp;
 					memcpy(&h->dup_len, HKEYDATA_DATA(
 					    H_PAIRDATA(h->pagep, h->bndx))
 					    + h->dup_off - sizeof(db_indx_t),
 					    sizeof(db_indx_t));
-					cursorp->dup_off -=
-					    DUP_SIZE(cursorp->dup_len);
-					cursorp->dndx--;
-					return (__ham_item(hashp,
-					    cursorp, mode));
+					hcp->dup_off -=
+					    DUP_SIZE(hcp->dup_len);
+					hcp->dndx--;
+					return (__ham_item(dbc, mode));
 				}
 			}
-		} else if (cursorp->dndx > 0) {	/* Duplicates are off-page. */
-			cursorp->dndx--;
-			return (__ham_item(hashp, cursorp, mode));
-		} else if ((ret = __ham_get_cpage(hashp, cursorp, mode)) != 0)
+		} else if (hcp->dndx > 0) {	/* Duplicates are off-page. */
+			hcp->dndx--;
+			return (__ham_item(dbc, mode));
+		} else if ((ret = __ham_get_cpage(dbc, mode)) != 0)
 			return (ret);
-		else if (PREV_PGNO(cursorp->dpagep) == PGNO_INVALID) {
-			F_CLR(cursorp, H_ISDUP); /* End of dups */
-			cursorp->dpgno = PGNO_INVALID;
-			if (cursorp->dpagep != NULL)
-				(void)__ham_put_page(hashp->dbp,
-				    cursorp->dpagep, 0);
-			cursorp->dpagep = NULL;
-		} else if ((ret = __ham_next_cpage(hashp, cursorp,
-		    PREV_PGNO(cursorp->dpagep), 0, H_ISDUP)) != 0)
+		else if (PREV_PGNO(hcp->dpagep) == PGNO_INVALID) {
+			if (F_ISSET(hcp, H_DUPONLY)) {
+				F_CLR(hcp, H_OK);
+				F_SET(hcp, H_NOMORE);
+				return (0);
+			} else {
+				F_CLR(hcp, H_ISDUP); /* End of dups */
+				hcp->dpgno = PGNO_INVALID;
+				if (hcp->dpagep != NULL)
+					(void)__ham_put_page(dbp,
+					    hcp->dpagep, 0);
+				hcp->dpagep = NULL;
+			}
+		} else if ((ret = __ham_next_cpage(dbc,
+		    PREV_PGNO(hcp->dpagep), 0, H_ISDUP)) != 0)
 			return (ret);
 		else {
-			cursorp->dndx = NUM_ENT(cursorp->pagep) - 1;
-			return (__ham_item(hashp, cursorp, mode));
+			hcp->dndx = NUM_ENT(hcp->pagep) - 1;
+			return (__ham_item(dbc, mode));
 		}
 	}
 
@@ -362,95 +392,123 @@ __ham_item_prev(hashp, cursorp, mode)
 	 * midpage, beginning of page, beginning of bucket.
 	 */
 
-	if (cursorp->bndx == 0) { 		/* Beginning of page. */
-		if ((ret = __ham_get_cpage(hashp, cursorp, mode)) != 0)
+	if (F_ISSET(hcp, H_DUPONLY)) {
+		F_CLR(hcp, H_OK);
+		F_SET(hcp, H_NOMORE);
+		return (0);
+	}
+
+	if (hcp->bndx == 0) { 		/* Beginning of page. */
+		if ((ret = __ham_get_cpage(dbc, mode)) != 0)
 			return (ret);
-		cursorp->pgno = PREV_PGNO(cursorp->pagep);
-		if (cursorp->pgno == PGNO_INVALID) {
+		hcp->pgno = PREV_PGNO(hcp->pagep);
+		if (hcp->pgno == PGNO_INVALID) {
 			/* Beginning of bucket. */
-			F_SET(cursorp, H_NOMORE);
+			F_SET(hcp, H_NOMORE);
 			return (DB_NOTFOUND);
-		} else if ((ret = __ham_next_cpage(hashp,
-		    cursorp, cursorp->pgno, 0, 0)) != 0)
+		} else if ((ret =
+		    __ham_next_cpage(dbc, hcp->pgno, 0, 0)) != 0)
 			return (ret);
 		else
-			cursorp->bndx = H_NUMPAIRS(cursorp->pagep);
+			hcp->bndx = H_NUMPAIRS(hcp->pagep);
 	}
 
 	/*
 	 * Either we've got the cursor set up to be decremented, or we
 	 * have to find the end of a bucket.
 	 */
-	if (cursorp->bndx == NDX_INVALID) {
-		if (cursorp->pagep == NULL)
-			next_pgno = BUCKET_TO_PAGE(hashp, cursorp->bucket);
+	if (hcp->bndx == NDX_INVALID) {
+		if (hcp->pagep == NULL)
+			next_pgno = BUCKET_TO_PAGE(hcp, hcp->bucket);
 		else
 			goto got_page;
 
 		do {
-			if ((ret = __ham_next_cpage(hashp,
-			    cursorp, next_pgno, 0, 0)) != 0)
+			if ((ret = __ham_next_cpage(dbc, next_pgno, 0, 0)) != 0)
 				return (ret);
-got_page:		next_pgno = NEXT_PGNO(cursorp->pagep);
-			cursorp->bndx = H_NUMPAIRS(cursorp->pagep);
+got_page:		next_pgno = NEXT_PGNO(hcp->pagep);
+			hcp->bndx = H_NUMPAIRS(hcp->pagep);
 		} while (next_pgno != PGNO_INVALID);
 
-		if (cursorp->bndx == 0) {
+		if (hcp->bndx == 0) {
 			/* Bucket was empty. */
-			F_SET(cursorp, H_NOMORE);
+			F_SET(hcp, H_NOMORE);
 			return (DB_NOTFOUND);
 		}
 	}
 
-	cursorp->bndx--;
+	hcp->bndx--;
 
-	return (__ham_item(hashp, cursorp, mode));
+	return (__ham_item(dbc, mode));
 }
 
 /*
  * Sets the cursor to the next key/data pair on a page.
  *
- * PUBLIC: int __ham_item_next __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_item_next __P((DBC *, db_lockmode_t));
  */
 int
-__ham_item_next(hashp, cursorp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_item_next(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	HASH_CURSOR *hcp;
+
+	hcp = (HASH_CURSOR *)dbc->internal;
 	/*
 	 * Deleted on-page duplicates are a weird case. If we delete the last
 	 * one, then our cursor is at the very end of a duplicate set and
 	 * we actually need to go on to the next key.
 	 */
-	if (F_ISSET(cursorp, H_DELETED)) {
-		if (cursorp->bndx != NDX_INVALID &&
-		    F_ISSET(cursorp, H_ISDUP) &&
-		    cursorp->dpgno == PGNO_INVALID &&
-		    cursorp->dup_tlen == cursorp->dup_off) {
-			F_CLR(cursorp, H_ISDUP);
-			cursorp->dpgno = PGNO_INVALID;
-			cursorp->bndx++;
+	if (F_ISSET(hcp, H_DELETED)) {
+		if (hcp->bndx != NDX_INVALID &&
+		    F_ISSET(hcp, H_ISDUP) &&
+		    hcp->dpgno == PGNO_INVALID &&
+		    hcp->dup_tlen == hcp->dup_off) {
+			if (F_ISSET(hcp, H_DUPONLY)) {
+				F_CLR(hcp, H_OK);
+				F_SET(hcp, H_NOMORE);
+				return (0);
+			} else {
+				F_CLR(hcp, H_ISDUP);
+				hcp->dpgno = PGNO_INVALID;
+				hcp->bndx++;
+			}
+		} else if (!F_ISSET(hcp, H_ISDUP) &&
+		    F_ISSET(hcp, H_DUPONLY)) {
+			F_CLR(hcp, H_OK);
+			F_SET(hcp, H_NOMORE);
+			return (0);
 		}
-		F_CLR(cursorp, H_DELETED);
-	} else if (cursorp->bndx == NDX_INVALID) {
-		cursorp->bndx = 0;
-		cursorp->dpgno = PGNO_INVALID;
-		F_CLR(cursorp, H_ISDUP);
-	} else if (F_ISSET(cursorp, H_ISDUP) && cursorp->dpgno != PGNO_INVALID)
-		cursorp->dndx++;
-	else if (F_ISSET(cursorp, H_ISDUP)) {
-		cursorp->dndx++;
-		cursorp->dup_off += DUP_SIZE(cursorp->dup_len);
-		if (cursorp->dup_off >= cursorp->dup_tlen) {
-			F_CLR(cursorp, H_ISDUP);
-			cursorp->dpgno = PGNO_INVALID;
-			cursorp->bndx++;
+		F_CLR(hcp, H_DELETED);
+	} else if (hcp->bndx == NDX_INVALID) {
+		hcp->bndx = 0;
+		hcp->dpgno = PGNO_INVALID;
+		F_CLR(hcp, H_ISDUP);
+	} else if (F_ISSET(hcp, H_ISDUP) && hcp->dpgno != PGNO_INVALID)
+		hcp->dndx++;
+	else if (F_ISSET(hcp, H_ISDUP)) {
+		if (hcp->dup_off + DUP_SIZE(hcp->dup_len) >=
+		    hcp->dup_tlen && F_ISSET(hcp, H_DUPONLY)) {
+			F_CLR(hcp, H_OK);
+			F_SET(hcp, H_NOMORE);
+			return (0);
+		}
+		hcp->dndx++;
+		hcp->dup_off += DUP_SIZE(hcp->dup_len);
+		if (hcp->dup_off >= hcp->dup_tlen) {
+			F_CLR(hcp, H_ISDUP);
+			hcp->dpgno = PGNO_INVALID;
+			hcp->bndx++;
 		}
+	} else if (F_ISSET(hcp, H_DUPONLY)) {
+		F_CLR(hcp, H_OK);
+		F_SET(hcp, H_NOMORE);
+		return (0);
 	} else
-		cursorp->bndx++;
+		hcp->bndx++;
 
-	return (__ham_item(hashp, cursorp, mode));
+	return (__ham_item(dbc, mode));
 }
 
 /*
@@ -537,18 +595,15 @@ __ham_reputpair(p, psize, ndx, key, data)
 
 
 /*
- * PUBLIC: int __ham_del_pair __P((HTAB *, HASH_CURSOR *, int));
- *
- * XXX
- * TODO: if the item is an offdup, delete the other pages and then remove
- * the pair. If the offpage page is 0, then you can just remove the pair.
+ * PUBLIC: int __ham_del_pair __P((DBC *, int));
  */
 int
-__ham_del_pair(hashp, cursorp, reclaim_page)
-	HTAB *hashp;
-	HASH_CURSOR *cursorp;
+__ham_del_pair(dbc, reclaim_page)
+	DBC *dbc;
 	int reclaim_page;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DBT data_dbt, key_dbt;
 	DB_ENV *dbenv;
 	DB_LSN new_lsn, *n_lsn, tmp_lsn;
@@ -557,13 +612,16 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 	db_pgno_t chg_pgno, pgno;
 	int ret, tret;
 
-	dbenv = hashp->dbp->dbenv;
-	ndx = cursorp->bndx;
-	if (cursorp->pagep == NULL && (ret =
-	    __ham_get_page(hashp->dbp, cursorp->pgno, &cursorp->pagep)) != 0)
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+
+	dbenv = dbp->dbenv;
+	ndx = hcp->bndx;
+	if (hcp->pagep == NULL &&
+	    (ret = __ham_get_page(dbp, hcp->pgno, &hcp->pagep)) != 0)
 		return (ret);
 
-	p = cursorp->pagep;
+	p = hcp->pagep;
 
 	/*
 	 * We optimize for the normal case which is when neither the key nor
@@ -576,7 +634,7 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 	if (HPAGE_PTYPE(H_PAIRKEY(p, ndx)) == H_OFFPAGE) {
 		memcpy(&pgno, HOFFPAGE_PGNO(P_ENTRY(p, H_KEYINDEX(ndx))),
 		    sizeof(db_pgno_t));
-		ret = __db_doff(hashp->dbp, pgno, __ham_del_page);
+		ret = __db_doff(dbc, pgno, __ham_del_page);
 	}
 
 	if (ret == 0)
@@ -585,14 +643,14 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 			memcpy(&pgno,
 			    HOFFPAGE_PGNO(P_ENTRY(p, H_DATAINDEX(ndx))),
 			    sizeof(db_pgno_t));
-			ret = __db_doff(hashp->dbp, pgno, __ham_del_page);
+			ret = __db_doff(dbc, pgno, __ham_del_page);
 			break;
 		case H_OFFDUP:
 			memcpy(&pgno,
 			    HOFFDUP_PGNO(P_ENTRY(p, H_DATAINDEX(ndx))),
 			    sizeof(db_pgno_t));
-			ret = __db_ddup(hashp->dbp, pgno, __ham_del_page);
-			F_CLR(cursorp, H_ISDUP);
+			ret = __db_ddup(dbc, pgno, __ham_del_page);
+			F_CLR(hcp, H_ISDUP);
 			break;
 		case H_DUPLICATE:
 			/*
@@ -600,7 +658,7 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 			 * we had better clear the flag so that we update the
 			 * cursor appropriately.
 			 */
-			F_CLR(cursorp, H_ISDUP);
+			F_CLR(hcp, H_ISDUP);
 			break;
 		}
 
@@ -608,17 +666,17 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		return (ret);
 
 	/* Now log the delete off this page. */
-	if (DB_LOGGING(hashp->dbp)) {
+	if (DB_LOGGING(dbc)) {
 		key_dbt.data = P_ENTRY(p, H_KEYINDEX(ndx));
 		key_dbt.size =
-		    LEN_HITEM(p, hashp->hdr->pagesize, H_KEYINDEX(ndx));
+		    LEN_HITEM(p, hcp->hdr->pagesize, H_KEYINDEX(ndx));
 		data_dbt.data = P_ENTRY(p, H_DATAINDEX(ndx));
 		data_dbt.size =
-		    LEN_HITEM(p, hashp->hdr->pagesize, H_DATAINDEX(ndx));
+		    LEN_HITEM(p, hcp->hdr->pagesize, H_DATAINDEX(ndx));
 
 		if ((ret = __ham_insdel_log(dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, DELPAIR,
-		    hashp->dbp->log_fileid, PGNO(p), (u_int32_t)ndx,
+		    dbc->txn, &new_lsn, 0, DELPAIR,
+		    dbp->log_fileid, PGNO(p), (u_int32_t)ndx,
 		    &LSN(p), &key_dbt, &data_dbt)) != 0)
 			return (ret);
 
@@ -626,15 +684,16 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		LSN(p) = new_lsn;
 	}
 
-	__ham_dpair(hashp->dbp, p, ndx);
+	__ham_dpair(dbp, p, ndx);
 
 	/*
-	 * If we are locking, we will not maintain this.
-	 * XXXX perhaps we can retain incremental numbers and apply them
+	 * If we are locking, we will not maintain this, because it is
+	 * a hot spot.
+	 * XXX perhaps we can retain incremental numbers and apply them
 	 * later.
 	 */
-	if (!F_ISSET(hashp->dbp, DB_AM_LOCKING))
-		--hashp->hdr->nelem;
+	if (!F_ISSET(dbp, DB_AM_LOCKING))
+		--hcp->hdr->nelem;
 
 	/*
 	 * If we need to reclaim the page, then check if the page is empty.
@@ -653,25 +712,25 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		 * are more pages in the chain.
 		 */
 		if ((ret =
-		    __ham_get_page(hashp->dbp, NEXT_PGNO(p), &n_pagep)) != 0)
+		    __ham_get_page(dbp, NEXT_PGNO(p), &n_pagep)) != 0)
 			return (ret);
 
 		if (NEXT_PGNO(n_pagep) != PGNO_INVALID) {
 			if ((ret =
-			    __ham_get_page(hashp->dbp, NEXT_PGNO(n_pagep),
+			    __ham_get_page(dbp, NEXT_PGNO(n_pagep),
 			    &nn_pagep)) != 0) {
-				(void) __ham_put_page(hashp->dbp, n_pagep, 0);
+				(void) __ham_put_page(dbp, n_pagep, 0);
 				return (ret);
 			}
 		}
 
-		if (DB_LOGGING(hashp->dbp)) {
+		if (DB_LOGGING(dbc)) {
 			key_dbt.data = n_pagep;
-			key_dbt.size = hashp->hdr->pagesize;
+			key_dbt.size = hcp->hdr->pagesize;
 			if ((ret = __ham_copypage_log(dbenv->lg_info,
-			    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-			    hashp->dbp->log_fileid, PGNO(p), &LSN(p),
-			    PGNO(n_pagep), &LSN(n_pagep), NEXT_PGNO(n_pagep),
+			    dbc->txn, &new_lsn, 0, dbp->log_fileid, PGNO(p),
+			    &LSN(p), PGNO(n_pagep), &LSN(n_pagep),
+			    NEXT_PGNO(n_pagep),
 			    NEXT_PGNO(n_pagep) == PGNO_INVALID ? NULL :
 			    &LSN(nn_pagep), &key_dbt)) != 0)
 				return (ret);
@@ -684,12 +743,12 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		}
 		if (NEXT_PGNO(n_pagep) != PGNO_INVALID) {
 			PREV_PGNO(nn_pagep) = PGNO(p);
-			(void)__ham_put_page(hashp->dbp, nn_pagep, 1);
+			(void)__ham_put_page(dbp, nn_pagep, 1);
 		}
 
 		tmp_pgno = PGNO(p);
 		tmp_lsn = LSN(p);
-		memcpy(p, n_pagep, hashp->hdr->pagesize);
+		memcpy(p, n_pagep, hcp->hdr->pagesize);
 		PGNO(p) = tmp_pgno;
 		LSN(p) = tmp_lsn;
 		PREV_PGNO(p) = PGNO_INVALID;
@@ -697,25 +756,25 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		/*
 		 * Cursor is advanced to the beginning of the next page.
 		 */
-		cursorp->bndx = 0;
-		cursorp->pgno = PGNO(p);
-		F_SET(cursorp, H_DELETED);
+		hcp->bndx = 0;
+		hcp->pgno = PGNO(p);
+		F_SET(hcp, H_DELETED);
 		chg_pgno = PGNO(p);
-		if ((ret = __ham_dirty_page(hashp, p)) != 0 ||
-		    (ret = __ham_del_page(hashp->dbp, n_pagep)) != 0)
+		if ((ret = __ham_dirty_page(dbp, p)) != 0 ||
+		    (ret = __ham_del_page(dbc, n_pagep)) != 0)
 			return (ret);
 	} else if (reclaim_page &&
 	    NUM_ENT(p) == 0 && PREV_PGNO(p) != PGNO_INVALID) {
 		PAGE *n_pagep, *p_pagep;
 
 		if ((ret =
-		    __ham_get_page(hashp->dbp, PREV_PGNO(p), &p_pagep)) != 0)
+		    __ham_get_page(dbp, PREV_PGNO(p), &p_pagep)) != 0)
 			return (ret);
 
 		if (NEXT_PGNO(p) != PGNO_INVALID) {
-			if ((ret = __ham_get_page(hashp->dbp,
+			if ((ret = __ham_get_page(dbp,
 			    NEXT_PGNO(p), &n_pagep)) != 0) {
-				(void)__ham_put_page(hashp->dbp, p_pagep, 0);
+				(void)__ham_put_page(dbp, p_pagep, 0);
 				return (ret);
 			}
 			n_lsn = &LSN(n_pagep);
@@ -728,10 +787,10 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		if (n_pagep != NULL)
 			PREV_PGNO(n_pagep) = PGNO(p_pagep);
 
-		if (DB_LOGGING(hashp->dbp)) {
+		if (DB_LOGGING(dbc)) {
 			if ((ret = __ham_newpage_log(dbenv->lg_info,
-			    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, DELOVFL,
-			    hashp->dbp->log_fileid, PREV_PGNO(p), &LSN(p_pagep),
+			    dbc->txn, &new_lsn, 0, DELOVFL,
+			    dbp->log_fileid, PREV_PGNO(p), &LSN(p_pagep),
 			    PGNO(p), &LSN(p), NEXT_PGNO(p), n_lsn)) != 0)
 				return (ret);
 
@@ -741,21 +800,21 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 				LSN(n_pagep) = new_lsn;
 			LSN(p) = new_lsn;
 		}
-		cursorp->pgno = NEXT_PGNO(p);
-		cursorp->bndx = 0;
+		hcp->pgno = NEXT_PGNO(p);
+		hcp->bndx = 0;
 		/*
 		 * Since we are about to delete the cursor page and we have
 		 * just moved the cursor, we need to make sure that the
 		 * old page pointer isn't left hanging around in the cursor.
 		 */
-		cursorp->pagep = NULL;
+		hcp->pagep = NULL;
 		chg_pgno = PGNO(p);
-		ret = __ham_del_page(hashp->dbp, p);
-		if ((tret = __ham_put_page(hashp->dbp, p_pagep, 1)) != 0 &&
+		ret = __ham_del_page(dbc, p);
+		if ((tret = __ham_put_page(dbp, p_pagep, 1)) != 0 &&
 		    ret == 0)
 			ret = tret;
 		if (n_pagep != NULL &&
-		    (tret = __ham_put_page(hashp->dbp, n_pagep, 1)) != 0 &&
+		    (tret = __ham_put_page(dbp, n_pagep, 1)) != 0 &&
 		    ret == 0)
 			ret = tret;
 		if (ret != 0)
@@ -766,19 +825,19 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
 		 * so that we update the cursor correctly on the next call
 		 * to next.
 		 */
-		F_SET(cursorp, H_DELETED);
-		chg_pgno = cursorp->pgno;
-		ret = __ham_dirty_page(hashp, p);
+		F_SET(hcp, H_DELETED);
+		chg_pgno = hcp->pgno;
+		ret = __ham_dirty_page(dbp, p);
 	}
-	__ham_c_update(cursorp, chg_pgno, 0, 0, 0);
+	__ham_c_update(hcp, chg_pgno, 0, 0, 0);
 
 	/*
 	 * Since we just deleted a pair from the master page, anything
-	 * in cursorp->dpgno should be cleared.
+	 * in hcp->dpgno should be cleared.
 	 */
-	cursorp->dpgno = PGNO_INVALID;
+	hcp->dpgno = PGNO_INVALID;
 
-	F_CLR(cursorp, H_OK);
+	F_CLR(hcp, H_OK);
 	return (ret);
 }
 
@@ -787,15 +846,16 @@ __ham_del_pair(hashp, cursorp, reclaim_page)
  *	Given the key data indicated by the cursor, replace part/all of it
  *	according to the fields in the dbt.
  *
- * PUBLIC: int __ham_replpair __P((HTAB *, HASH_CURSOR *, DBT *, u_int32_t));
+ * PUBLIC: int __ham_replpair __P((DBC *, DBT *, u_int32_t));
  */
 int
-__ham_replpair(hashp, hcp, dbt, make_dup)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_replpair(dbc, dbt, make_dup)
+	DBC *dbc;
 	DBT *dbt;
 	u_int32_t make_dup;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DBT old_dbt, tdata, tmp;
 	DB_LSN	new_lsn;
 	int32_t change;			/* XXX: Possible overflow. */
@@ -814,6 +874,8 @@ __ham_replpair(hashp, hcp, dbt, make_dup)
 	 * be the common case).  We handle case 3 as a delete and
 	 * add.
 	 */
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 
 	/*
 	 * We need to compute the number of bytes that we are adding or
@@ -833,7 +895,7 @@ __ham_replpair(hashp, hcp, dbt, make_dup)
 		memcpy(&len, HOFFPAGE_TLEN(hk), sizeof(u_int32_t));
 	else
 		len = LEN_HKEYDATA(hcp->pagep,
-		    hashp->dbp->pgsize, H_DATAINDEX(hcp->bndx));
+		    dbp->pgsize, H_DATAINDEX(hcp->bndx));
 
 	if (dbt->doff + dbt->dlen > len)
 		change += dbt->doff + dbt->dlen - len;
@@ -854,41 +916,39 @@ __ham_replpair(hashp, hcp, dbt, make_dup)
 		tmp.flags = 0;
 		F_SET(&tmp, DB_DBT_MALLOC | DB_DBT_INTERNAL);
 		if ((ret =
-		    __db_ret(hashp->dbp, hcp->pagep, H_KEYINDEX(hcp->bndx),
-		    &tmp, &hcp->big_key, &hcp->big_keylen)) != 0)
+		    __db_ret(dbp, hcp->pagep, H_KEYINDEX(hcp->bndx),
+		    &tmp, &dbc->rkey.data, &dbc->rkey.size)) != 0)
 			return (ret);
 
 		if (dbt->doff == 0 && dbt->dlen == len) {
-			ret = __ham_del_pair(hashp, hcp, 0);
+			ret = __ham_del_pair(dbc, 0);
 			if (ret == 0)
-			    ret = __ham_add_el(hashp,
-			        hcp, &tmp, dbt, H_KEYDATA);
+			    ret = __ham_add_el(dbc, &tmp, dbt, H_KEYDATA);
 		} else {					/* Case B */
 			type = HPAGE_PTYPE(hk) != H_OFFPAGE ?
 			    HPAGE_PTYPE(hk) : H_KEYDATA;
 			tdata.flags = 0;
 			F_SET(&tdata, DB_DBT_MALLOC | DB_DBT_INTERNAL);
 
-			if ((ret = __db_ret(hashp->dbp, hcp->pagep,
-			    H_DATAINDEX(hcp->bndx), &tdata, &hcp->big_data,
-			    &hcp->big_datalen)) != 0)
+			if ((ret = __db_ret(dbp, hcp->pagep,
+			    H_DATAINDEX(hcp->bndx), &tdata, &dbc->rdata.data,
+			    &dbc->rdata.size)) != 0)
 				goto err;
 
 			/* Now we can delete the item. */
-			if ((ret = __ham_del_pair(hashp, hcp, 0)) != 0) {
-				__db_free(tdata.data);
+			if ((ret = __ham_del_pair(dbc, 0)) != 0) {
+				__os_free(tdata.data, tdata.size);
 				goto err;
 			}
 
 			/* Now shift old data around to make room for new. */
 			if (change > 0) {
-				tdata.data = (void *)__db_realloc(tdata.data,
-				    tdata.size + change);
+				 if ((ret = __os_realloc(&tdata.data,
+				     tdata.size + change)) != 0)
+					return (ret);
 				memset((u_int8_t *)tdata.data + tdata.size,
 				    0, change);
 			}
-			if (tdata.data == NULL)
-				return (ENOMEM);
 			end = (u_int8_t *)tdata.data + tdata.size;
 
 			src = (u_int8_t *)tdata.data + dbt->doff + dbt->dlen;
@@ -902,10 +962,10 @@ __ham_replpair(hashp, hcp, dbt, make_dup)
 			tdata.size += change;
 
 			/* Now add the pair. */
-			ret = __ham_add_el(hashp, hcp, &tmp, &tdata, type);
-			__db_free(tdata.data);
+			ret = __ham_add_el(dbc, &tmp, &tdata, type);
+			__os_free(tdata.data, tdata.size);
 		}
-err:		__db_free(tmp.data);
+err:		__os_free(tmp.data, tmp.size);
 		return (ret);
 	}
 
@@ -921,12 +981,11 @@ err:		__db_free(tmp.data);
 	 * all the parameters here.  Then log the call before moving
 	 * anything around.
 	 */
-	if (DB_LOGGING(hashp->dbp)) {
+	if (DB_LOGGING(dbc)) {
 		old_dbt.data = beg;
 		old_dbt.size = dbt->dlen;
-		if ((ret = __ham_replace_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-		    hashp->dbp->log_fileid, PGNO(hcp->pagep),
+		if ((ret = __ham_replace_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, dbp->log_fileid, PGNO(hcp->pagep),
 		    (u_int32_t)H_DATAINDEX(hcp->bndx), &LSN(hcp->pagep),
 		    (u_int32_t)dbt->doff, &old_dbt, dbt, make_dup)) != 0)
 			return (ret);
@@ -934,7 +993,7 @@ err:		__db_free(tmp.data);
 		LSN(hcp->pagep) = new_lsn;	/* Structure assignment. */
 	}
 
-	__ham_onpage_replace(hcp->pagep, hashp->dbp->pgsize,
+	__ham_onpage_replace(hcp->pagep, dbp->pgsize,
 	    (u_int32_t)H_DATAINDEX(hcp->bndx), (int32_t)dbt->doff, change, dbt);
 
 	return (0);
@@ -997,13 +1056,15 @@ __ham_onpage_replace(pagep, pgsize, ndx, off, change, dbt)
 }
 
 /*
- * PUBLIC: int __ham_split_page __P((HTAB *, u_int32_t, u_int32_t));
+ * PUBLIC: int __ham_split_page __P((DBC *, u_int32_t, u_int32_t));
  */
 int
-__ham_split_page(hashp, obucket, nbucket)
-	HTAB *hashp;
+__ham_split_page(dbc, obucket, nbucket)
+	DBC *dbc;
 	u_int32_t obucket, nbucket;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DBT key, page_dbt;
 	DB_ENV *dbenv;
 	DB_LSN new_lsn;
@@ -1014,33 +1075,34 @@ __ham_split_page(hashp, obucket, nbucket)
 	int ret, tret;
 	void *big_buf;
 
-	dbenv = hashp->dbp->dbenv;
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+	dbenv = dbp->dbenv;
 	temp_pagep = old_pagep = new_pagep = NULL;
 
-	bucket_pgno = BUCKET_TO_PAGE(hashp, obucket);
-	if ((ret = __ham_get_page(hashp->dbp, bucket_pgno, &old_pagep)) != 0)
+	bucket_pgno = BUCKET_TO_PAGE(hcp, obucket);
+	if ((ret = __ham_get_page(dbp, bucket_pgno, &old_pagep)) != 0)
 		return (ret);
-	if ((ret = __ham_new_page(hashp, BUCKET_TO_PAGE(hashp, nbucket), P_HASH,
+	if ((ret = __ham_new_page(dbp, BUCKET_TO_PAGE(hcp, nbucket), P_HASH,
 	    &new_pagep)) != 0)
 		goto err;
 
-	temp_pagep = hashp->split_buf;
-	memcpy(temp_pagep, old_pagep, hashp->hdr->pagesize);
+	temp_pagep = hcp->split_buf;
+	memcpy(temp_pagep, old_pagep, hcp->hdr->pagesize);
 
-	if (DB_LOGGING(hashp->dbp)) {
-		page_dbt.size = hashp->hdr->pagesize;
+	if (DB_LOGGING(dbc)) {
+		page_dbt.size = hcp->hdr->pagesize;
 		page_dbt.data = old_pagep;
 		if ((ret = __ham_splitdata_log(dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-		    hashp->dbp->log_fileid, SPLITOLD, PGNO(old_pagep),
-		    &page_dbt, &LSN(old_pagep))) != 0)
+		    dbc->txn, &new_lsn, 0, dbp->log_fileid, SPLITOLD,
+		    PGNO(old_pagep), &page_dbt, &LSN(old_pagep))) != 0)
 			goto err;
 	}
 
-	P_INIT(old_pagep, hashp->hdr->pagesize, PGNO(old_pagep), PGNO_INVALID,
+	P_INIT(old_pagep, hcp->hdr->pagesize, PGNO(old_pagep), PGNO_INVALID,
 	    PGNO_INVALID, 0, P_HASH);
 
-	if (DB_LOGGING(hashp->dbp))
+	if (DB_LOGGING(dbc))
 		LSN(old_pagep) = new_lsn;	/* Structure assignment. */
 
 	big_len = 0;
@@ -1049,11 +1111,11 @@ __ham_split_page(hashp, obucket, nbucket)
 	while (temp_pagep != NULL) {
 		for (n = 0; n < (db_indx_t)H_NUMPAIRS(temp_pagep); n++) {
 			if ((ret =
-			    __db_ret(hashp->dbp, temp_pagep, H_KEYINDEX(n),
+			    __db_ret(dbp, temp_pagep, H_KEYINDEX(n),
 			    &key, &big_buf, &big_len)) != 0)
 				goto err;
 
-			if (__ham_call_hash(hashp, key.data, key.size)
+			if (__ham_call_hash(hcp, key.data, key.size)
 			    == obucket)
 				pp = &old_pagep;
 			else
@@ -1064,59 +1126,59 @@ __ham_split_page(hashp, obucket, nbucket)
 			 * page to store the key/data pair.
 			 */
 
-			len = LEN_HITEM(temp_pagep, hashp->hdr->pagesize,
+			len = LEN_HITEM(temp_pagep, hcp->hdr->pagesize,
 			    H_DATAINDEX(n)) +
-			    LEN_HITEM(temp_pagep, hashp->hdr->pagesize,
+			    LEN_HITEM(temp_pagep, hcp->hdr->pagesize,
 			    H_KEYINDEX(n)) +
 			    2 * sizeof(db_indx_t);
 
 			if (P_FREESPACE(*pp) < len) {
-				if (DB_LOGGING(hashp->dbp)) {
-					page_dbt.size = hashp->hdr->pagesize;
+				if (DB_LOGGING(dbc)) {
+					page_dbt.size = hcp->hdr->pagesize;
 					page_dbt.data = *pp;
 					if ((ret = __ham_splitdata_log(
-					    dbenv->lg_info,
-					    (DB_TXN *)hashp->dbp->txn,
-					    &new_lsn, 0,
-					    hashp->dbp->log_fileid, SPLITNEW,
-					    PGNO(*pp), &page_dbt,
+					    dbenv->lg_info, dbc->txn,
+					    &new_lsn, 0, dbp->log_fileid,
+					    SPLITNEW, PGNO(*pp), &page_dbt,
 					    &LSN(*pp))) != 0)
 						goto err;
 					LSN(*pp) = new_lsn;
 				}
-				if ((ret = __ham_add_ovflpage(hashp,
-				    *pp, 1, pp)) != 0)
+				if ((ret =
+				    __ham_add_ovflpage(dbc, *pp, 1, pp)) != 0)
 					goto err;
 			}
-			__ham_copy_item(hashp, temp_pagep, H_KEYINDEX(n), *pp);
-			__ham_copy_item(hashp, temp_pagep, H_DATAINDEX(n), *pp);
+			__ham_copy_item(dbp->pgsize,
+			    temp_pagep, H_KEYINDEX(n), *pp);
+			__ham_copy_item(dbp->pgsize,
+			    temp_pagep, H_DATAINDEX(n), *pp);
 		}
 		next_pgno = NEXT_PGNO(temp_pagep);
 
 		/* Clear temp_page; if it's a link overflow page, free it. */
 		if (PGNO(temp_pagep) != bucket_pgno && (ret =
-		    __ham_del_page(hashp->dbp, temp_pagep)) != 0)
+		    __ham_del_page(dbc, temp_pagep)) != 0)
 			goto err;
 
 		if (next_pgno == PGNO_INVALID)
 			temp_pagep = NULL;
 		else if ((ret =
-		    __ham_get_page(hashp->dbp, next_pgno, &temp_pagep)) != 0)
+		    __ham_get_page(dbp, next_pgno, &temp_pagep)) != 0)
 			goto err;
 
-		if (temp_pagep != NULL && DB_LOGGING(hashp->dbp)) {
-			page_dbt.size = hashp->hdr->pagesize;
+		if (temp_pagep != NULL && DB_LOGGING(dbc)) {
+			page_dbt.size = hcp->hdr->pagesize;
 			page_dbt.data = temp_pagep;
 			if ((ret = __ham_splitdata_log(dbenv->lg_info,
-			    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-			    hashp->dbp->log_fileid, SPLITOLD, PGNO(temp_pagep),
+			    dbc->txn, &new_lsn, 0, dbp->log_fileid,
+			    SPLITOLD, PGNO(temp_pagep),
 			    &page_dbt, &LSN(temp_pagep))) != 0)
 				goto err;
 			LSN(temp_pagep) = new_lsn;
 		}
 	}
 	if (big_buf != NULL)
-		__db_free(big_buf);
+		__os_free(big_buf, big_len);
 
 	/*
 	 * If the original bucket spanned multiple pages, then we've got
@@ -1124,42 +1186,41 @@ __ham_split_page(hashp, obucket, nbucket)
 	 * should be deleted.
 	 */
 	if (temp_pagep != NULL && PGNO(temp_pagep) != bucket_pgno &&
-	    (ret = __ham_del_page(hashp->dbp, temp_pagep)) != 0)
+	    (ret = __ham_del_page(dbc, temp_pagep)) != 0)
 		goto err;
 
 	/*
 	 * Write new buckets out.
 	 */
-	if (DB_LOGGING(hashp->dbp)) {
-		page_dbt.size = hashp->hdr->pagesize;
+	if (DB_LOGGING(dbc)) {
+		page_dbt.size = hcp->hdr->pagesize;
 		page_dbt.data = old_pagep;
 		if ((ret = __ham_splitdata_log(dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-		    hashp->dbp->log_fileid, SPLITNEW, PGNO(old_pagep),
+		   dbc->txn, &new_lsn, 0, dbp->log_fileid,
+		   SPLITNEW, PGNO(old_pagep),
 		    &page_dbt, &LSN(old_pagep))) != 0)
 			goto err;
 		LSN(old_pagep) = new_lsn;
 
 		page_dbt.data = new_pagep;
 		if ((ret = __ham_splitdata_log(dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0,
-		    hashp->dbp->log_fileid, SPLITNEW, PGNO(new_pagep),
-		    &page_dbt, &LSN(new_pagep))) != 0)
+		    dbc->txn, &new_lsn, 0, dbp->log_fileid,
+		    SPLITNEW, PGNO(new_pagep), &page_dbt, &LSN(new_pagep))) != 0)
 			goto err;
 		LSN(new_pagep) = new_lsn;
 	}
-	ret = __ham_put_page(hashp->dbp, old_pagep, 1);
-	if ((tret = __ham_put_page(hashp->dbp, new_pagep, 1)) != 0 &&
+	ret = __ham_put_page(dbp, old_pagep, 1);
+	if ((tret = __ham_put_page(dbp, new_pagep, 1)) != 0 &&
 	    ret == 0)
 		ret = tret;
 
 	if (0) {
 err:		if (old_pagep != NULL)
-			(void)__ham_put_page(hashp->dbp, old_pagep, 1);
+			(void)__ham_put_page(dbp, old_pagep, 1);
 		if (new_pagep != NULL)
-			(void)__ham_put_page(hashp->dbp, new_pagep, 1);
+			(void)__ham_put_page(dbp, new_pagep, 1);
 		if (temp_pagep != NULL && PGNO(temp_pagep) != bucket_pgno)
-			(void)__ham_put_page(hashp->dbp, temp_pagep, 1);
+			(void)__ham_put_page(dbp, temp_pagep, 1);
 	}
 	return (ret);
 }
@@ -1171,16 +1232,16 @@ err:		if (old_pagep != NULL)
  * to which we just added something.  This allows us to link overflow
  * pages and return the new page having correctly put the last page.
  *
- * PUBLIC: int __ham_add_el
- * PUBLIC:    __P((HTAB *, HASH_CURSOR *, const DBT *, const DBT *, int));
+ * PUBLIC: int __ham_add_el __P((DBC *, const DBT *, const DBT *, int));
  */
 int
-__ham_add_el(hashp, hcp, key, val, type)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_add_el(dbc, key, val, type)
+	DBC *dbc;
 	const DBT *key, *val;
 	int type;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	const DBT *pkey, *pdata;
 	DBT key_dbt, data_dbt;
 	DB_LSN new_lsn;
@@ -1190,17 +1251,19 @@ __ham_add_el(hashp, hcp, key, val, type)
 	int do_expand, is_keybig, is_databig, ret;
 	int key_type, data_type;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	do_expand = 0;
 
-	if (hcp->pagep == NULL && (ret = __ham_get_page(hashp->dbp,
+	if (hcp->pagep == NULL && (ret = __ham_get_page(dbp,
 	    hcp->seek_found_page != PGNO_INVALID ?  hcp->seek_found_page :
 	    hcp->pgno, &hcp->pagep)) != 0)
 		return (ret);
 
 	key_size = HKEYDATA_PSIZE(key->size);
 	data_size = HKEYDATA_PSIZE(val->size);
-	is_keybig = ISBIG(hashp, key->size);
-	is_databig = ISBIG(hashp, val->size);
+	is_keybig = ISBIG(hcp, key->size);
+	is_databig = ISBIG(hcp, val->size);
 	if (is_keybig)
 		key_size = HOFFPAGE_PSIZE;
 	if (is_databig)
@@ -1220,7 +1283,7 @@ __ham_add_el(hashp, hcp, key, val, type)
 			break;
 		next_pgno = NEXT_PGNO(hcp->pagep);
 		if ((ret =
-		    __ham_next_cpage(hashp, hcp, next_pgno, 0, 0)) != 0)
+		    __ham_next_cpage(dbc, next_pgno, 0, 0)) != 0)
 			return (ret);
 	}
 
@@ -1229,7 +1292,7 @@ __ham_add_el(hashp, hcp, key, val, type)
 	 */
 	if (P_FREESPACE(hcp->pagep) < pairsize) {
 		do_expand = 1;
-		if ((ret = __ham_add_ovflpage(hashp,
+		if ((ret = __ham_add_ovflpage(dbc,
 		    hcp->pagep, 1, &hcp->pagep)) !=  0)
 			return (ret);
 		hcp->pgno = PGNO(hcp->pagep);
@@ -1241,10 +1304,13 @@ __ham_add_el(hashp, hcp, key, val, type)
 	hcp->bndx = H_NUMPAIRS(hcp->pagep);
 	F_CLR(hcp, H_DELETED);
 	if (is_keybig) {
-		if ((ret = __db_poff(hashp->dbp,
+		koff.type = H_OFFPAGE;
+		UMRW(koff.unused[0]);
+		UMRW(koff.unused[1]);
+		UMRW(koff.unused[2]);
+		if ((ret = __db_poff(dbc,
 		    key, &koff.pgno, __ham_overflow_page)) != 0)
 			return (ret);
-		koff.type = H_OFFPAGE;
 		koff.tlen = key->size;
 		key_dbt.data = &koff;
 		key_dbt.size = sizeof(koff);
@@ -1256,10 +1322,13 @@ __ham_add_el(hashp, hcp, key, val, type)
 	}
 
 	if (is_databig) {
-		if ((ret = __db_poff(hashp->dbp,
+		doff.type = H_OFFPAGE;
+		UMRW(doff.unused[0]);
+		UMRW(doff.unused[1]);
+		UMRW(doff.unused[2]);
+		if ((ret = __db_poff(dbc,
 		    val, &doff.pgno, __ham_overflow_page)) != 0)
 			return (ret);
-		doff.type = H_OFFPAGE;
 		doff.tlen = val->size;
 		data_dbt.data = &doff;
 		data_dbt.size = sizeof(doff);
@@ -1270,16 +1339,16 @@ __ham_add_el(hashp, hcp, key, val, type)
 		data_type = type;
 	}
 
-	if (DB_LOGGING(hashp->dbp)) {
+	if (DB_LOGGING(dbc)) {
 		rectype = PUTPAIR;
 		if (is_databig)
 			rectype |= PAIR_DATAMASK;
 		if (is_keybig)
 			rectype |= PAIR_KEYMASK;
 
-		if ((ret = __ham_insdel_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, rectype,
-		    hashp->dbp->log_fileid, PGNO(hcp->pagep),
+		if ((ret = __ham_insdel_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, rectype,
+		    dbp->log_fileid, PGNO(hcp->pagep),
 		    (u_int32_t)H_NUMPAIRS(hcp->pagep),
 		    &LSN(hcp->pagep), pkey, pdata)) != 0)
 			return (ret);
@@ -1303,11 +1372,11 @@ __ham_add_el(hashp, hcp, key, val, type)
 	/*
 	 * XXX Maybe keep incremental numbers here
 	 */
-	if (!F_ISSET(hashp->dbp, DB_AM_LOCKING))
-		hashp->hdr->nelem++;
+	if (!F_ISSET(dbp, DB_AM_LOCKING))
+		hcp->hdr->nelem++;
 
-	if (do_expand || (hashp->hdr->ffactor != 0 &&
-	    (u_int32_t)H_NUMPAIRS(hcp->pagep) > hashp->hdr->ffactor))
+	if (do_expand || (hcp->hdr->ffactor != 0 &&
+	    (u_int32_t)H_NUMPAIRS(hcp->pagep) > hcp->hdr->ffactor))
 		F_SET(hcp, H_EXPAND);
 	return (0);
 }
@@ -1319,11 +1388,11 @@ __ham_add_el(hashp, hcp, key, val, type)
  * H_DUPLICATE, H_OFFDUP).  Since we log splits at a high level, we
  * do not need to do any logging here.
  *
- * PUBLIC: void __ham_copy_item __P((HTAB *, PAGE *, u_int32_t, PAGE *));
+ * PUBLIC: void __ham_copy_item __P((size_t, PAGE *, u_int32_t, PAGE *));
  */
 void
-__ham_copy_item(hashp, src_page, src_ndx, dest_page)
-	HTAB *hashp;
+__ham_copy_item(pgsize, src_page, src_ndx, dest_page)
+	size_t pgsize;
 	PAGE *src_page;
 	u_int32_t src_ndx;
 	PAGE *dest_page;
@@ -1337,7 +1406,7 @@ __ham_copy_item(hashp, src_page, src_ndx, dest_page)
 	src = P_ENTRY(src_page, src_ndx);
 
 	/* Set up space on dest. */
-	len = LEN_HITEM(src_page, hashp->hdr->pagesize, src_ndx);
+	len = LEN_HITEM(src_page, pgsize, src_ndx);
 	HOFFSET(dest_page) -= len;
 	dest_page->inp[NUM_ENT(dest_page)] = HOFFSET(dest_page);
 	dest = P_ENTRY(dest_page, NUM_ENT(dest_page));
@@ -1352,29 +1421,31 @@ __ham_copy_item(hashp, src_page, src_ndx, dest_page)
  *      pointer on success
  *      NULL on error
  *
- * PUBLIC: int __ham_add_ovflpage __P((HTAB *, PAGE *, int, PAGE **));
+ * PUBLIC: int __ham_add_ovflpage __P((DBC *, PAGE *, int, PAGE **));
  */
 int
-__ham_add_ovflpage(hashp, pagep, release, pp)
-	HTAB *hashp;
+__ham_add_ovflpage(dbc, pagep, release, pp)
+	DBC *dbc;
 	PAGE *pagep;
 	int release;
 	PAGE **pp;
 {
-	DB_ENV *dbenv;
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DB_LSN new_lsn;
 	PAGE *new_pagep;
 	int ret;
 
-	dbenv = hashp->dbp->dbenv;
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	if ((ret = __ham_overflow_page(hashp->dbp, P_HASH, &new_pagep)) != 0)
+	if ((ret = __ham_overflow_page(dbc, P_HASH, &new_pagep)) != 0)
 		return (ret);
 
-	if (DB_LOGGING(hashp->dbp)) {
-		if ((ret = __ham_newpage_log(dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, PUTOVFL,
-		    hashp->dbp->log_fileid, PGNO(pagep), &LSN(pagep),
+	if (DB_LOGGING(dbc)) {
+		if ((ret = __ham_newpage_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, PUTOVFL,
+		    dbp->log_fileid, PGNO(pagep), &LSN(pagep),
 		    PGNO(new_pagep), &LSN(new_pagep), PGNO_INVALID, NULL)) != 0)
 			return (ret);
 
@@ -1385,78 +1456,76 @@ __ham_add_ovflpage(hashp, pagep, release, pp)
 	PREV_PGNO(new_pagep) = PGNO(pagep);
 
 	if (release)
-		ret = __ham_put_page(hashp->dbp, pagep, 1);
+		ret = __ham_put_page(dbp, pagep, 1);
 
-	hashp->hash_overflows++;
+	hcp->stats.hash_overflows++;
 	*pp = new_pagep;
 	return (ret);
 }
 
 
 /*
- * PUBLIC: int __ham_new_page __P((HTAB *, u_int32_t, u_int32_t, PAGE **));
+ * PUBLIC: int __ham_new_page __P((DB *, u_int32_t, u_int32_t, PAGE **));
  */
 int
-__ham_new_page(hashp, addr, type, pp)
-	HTAB *hashp;
+__ham_new_page(dbp, addr, type, pp)
+	DB *dbp;
 	u_int32_t addr, type;
 	PAGE **pp;
 {
 	PAGE *pagep;
 	int ret;
 
-	if ((ret = memp_fget(hashp->dbp->mpf,
+	if ((ret = memp_fget(dbp->mpf,
 	    &addr, DB_MPOOL_CREATE, &pagep)) != 0)
 		return (ret);
 
-#ifdef DEBUG_SLOW
-	__account_page(hashp, addr, 1);
-#endif
 	/* This should not be necessary because page-in should do it. */
-	P_INIT(pagep,
-	    hashp->hdr->pagesize, addr, PGNO_INVALID, PGNO_INVALID, 0, type);
+	P_INIT(pagep, dbp->pgsize, addr, PGNO_INVALID, PGNO_INVALID, 0, type);
 
 	*pp = pagep;
 	return (0);
 }
 
 /*
- * PUBLIC: int __ham_del_page __P((DB *, PAGE *));
+ * PUBLIC: int __ham_del_page __P((DBC *, PAGE *));
  */
 int
-__ham_del_page(dbp, pagep)
-	DB *dbp;
+__ham_del_page(dbc, pagep)
+	DBC *dbc;
 	PAGE *pagep;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DB_LSN new_lsn;
-	HTAB *hashp;
 	int ret;
 
-	hashp = (HTAB *)dbp->internal;
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	ret = 0;
-	DIRTY_META(hashp, ret);
+	DIRTY_META(dbp, hcp, ret);
 	if (ret != 0) {
 		if (ret != EAGAIN)
-			__db_err(hashp->dbp->dbenv,
+			__db_err(dbp->dbenv,
 			    "free_ovflpage: unable to lock meta data page %s\n",
 			    strerror(ret));
 		/*
 		 * If we are going to return an error, then we should free
 		 * the page, so it doesn't stay pinned forever.
 		 */
-		(void)__ham_put_page(hashp->dbp, pagep, 0);
+		(void)__ham_put_page(dbp, pagep, 0);
 		return (ret);
 	}
 
-	if (DB_LOGGING(hashp->dbp)) {
-		if ((ret = __ham_newpgno_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, DELPGNO,
-		    hashp->dbp->log_fileid, PGNO(pagep), hashp->hdr->last_freed,
+	if (DB_LOGGING(dbc)) {
+		if ((ret = __ham_newpgno_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, DELPGNO,
+		    dbp->log_fileid, PGNO(pagep), hcp->hdr->last_freed,
 		    (u_int32_t)TYPE(pagep), NEXT_PGNO(pagep), P_INVALID,
-		    &LSN(pagep), &hashp->hdr->lsn)) != 0)
+		    &LSN(pagep), &hcp->hdr->lsn)) != 0)
 			return (ret);
 
-		hashp->hdr->lsn = new_lsn;
+		hcp->hdr->lsn = new_lsn;
 		LSN(pagep) = new_lsn;
 	}
 
@@ -1466,16 +1535,16 @@ __ham_del_page(dbp, pagep)
 		DB_LSN __lsn;
 		__pgno = pagep->pgno;
 		__lsn = pagep->lsn;
-		memset(pagep, 0xff, dbp->pgsize);
+		memset(pagep, 0xdb, dbp->pgsize);
 		pagep->pgno = __pgno;
 		pagep->lsn = __lsn;
 	}
 #endif
 	TYPE(pagep) = P_INVALID;
-	NEXT_PGNO(pagep) = hashp->hdr->last_freed;
-	hashp->hdr->last_freed = PGNO(pagep);
+	NEXT_PGNO(pagep) = hcp->hdr->last_freed;
+	hcp->hdr->last_freed = PGNO(pagep);
 
-	return (__ham_put_page(hashp->dbp, pagep, 1));
+	return (__ham_put_page(dbp, pagep, 1));
 }
 
 
@@ -1489,8 +1558,7 @@ __ham_put_page(dbp, pagep, is_dirty)
 	int32_t is_dirty;
 {
 #ifdef DEBUG_SLOW
-	__account_page((HTAB *)dbp->cookie,
-	    ((BKT *)((char *)pagep - sizeof(BKT)))->pgno, -1);
+	__account_page(dbp, ((BKT *)((char *)pagep - sizeof(BKT)))->pgno, -1);
 #endif
 	return (memp_fput(dbp->mpf, pagep, (is_dirty ? DB_MPOOL_DIRTY : 0)));
 }
@@ -1499,14 +1567,14 @@ __ham_put_page(dbp, pagep, is_dirty)
  * __ham_dirty_page --
  *	Mark a page dirty.
  *
- * PUBLIC: int __ham_dirty_page __P((HTAB *, PAGE *));
+ * PUBLIC: int __ham_dirty_page __P((DB *, PAGE *));
  */
 int
-__ham_dirty_page(hashp, pagep)
-	HTAB *hashp;
+__ham_dirty_page(dbp, pagep)
+	DB *dbp;
 	PAGE *pagep;
 {
-	return (memp_fset(hashp->dbp->mpf, pagep, DB_MPOOL_DIRTY));
+	return (memp_fset(dbp->mpf, pagep, DB_MPOOL_DIRTY));
 }
 
 /*
@@ -1523,31 +1591,33 @@ __ham_get_page(dbp, addr, pagep)
 	ret = memp_fget(dbp->mpf, &addr, DB_MPOOL_CREATE, pagep);
 #ifdef DEBUG_SLOW
 	if (*pagep != NULL)
-		__account_page((HTAB *)dbp->internal, addr, 1);
+		__account_page(dbp, addr, 1);
 #endif
 	return (ret);
 }
 
 /*
- * PUBLIC: int __ham_overflow_page __P((DB *, u_int32_t, PAGE **));
+ * PUBLIC: int __ham_overflow_page
+ * PUBLIC:     __P((DBC *, u_int32_t, PAGE **));
  */
 int
-__ham_overflow_page(dbp, type, pp)
-	DB *dbp;
+__ham_overflow_page(dbc, type, pp)
+	DBC *dbc;
 	u_int32_t type;
 	PAGE **pp;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DB_LSN *lsnp, new_lsn;
-	HTAB *hashp;
 	PAGE *p;
 	db_pgno_t new_addr, next_free, newalloc_flag;
 	u_int32_t offset, splitnum;
 	int ret;
 
-	hashp = (HTAB *)dbp->internal;
-
 	ret = 0;
-	DIRTY_META(hashp, ret);
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+	DIRTY_META(dbp, hcp, ret);
 	if (ret != 0)
 		return (ret);
 
@@ -1558,22 +1628,22 @@ __ham_overflow_page(dbp, type, pp)
 	 * after the log do we get to complete allocation of the
 	 * new page.
 	 */
-	new_addr = hashp->hdr->last_freed;
+	new_addr = hcp->hdr->last_freed;
 	if (new_addr != PGNO_INVALID) {
-		if ((ret = __ham_get_page(hashp->dbp, new_addr, &p)) != 0)
+		if ((ret = __ham_get_page(dbp, new_addr, &p)) != 0)
 			return (ret);
 		next_free = NEXT_PGNO(p);
 		lsnp = &LSN(p);
 		newalloc_flag = 0;
 	} else {
-		splitnum = hashp->hdr->ovfl_point;
-		hashp->hdr->spares[splitnum]++;
-		offset = hashp->hdr->spares[splitnum] -
-		    (splitnum ? hashp->hdr->spares[splitnum - 1] : 0);
-		new_addr = PGNO_OF(hashp, hashp->hdr->ovfl_point, offset);
-		if (new_addr > MAX_PAGES(hashp)) {
-			__db_err(hashp->dbp->dbenv, "hash: out of file pages");
-			hashp->hdr->spares[splitnum]--;
+		splitnum = hcp->hdr->ovfl_point;
+		hcp->hdr->spares[splitnum]++;
+		offset = hcp->hdr->spares[splitnum] -
+		    (splitnum ? hcp->hdr->spares[splitnum - 1] : 0);
+		new_addr = PGNO_OF(hcp, hcp->hdr->ovfl_point, offset);
+		if (new_addr > MAX_PAGES(hcp)) {
+			__db_err(dbp->dbenv, "hash: out of file pages");
+			hcp->hdr->spares[splitnum]--;
 			return (ENOMEM);
 		}
 		next_free = PGNO_INVALID;
@@ -1582,29 +1652,29 @@ __ham_overflow_page(dbp, type, pp)
 		newalloc_flag = 1;
 	}
 
-	if (DB_LOGGING(hashp->dbp)) {
-		if ((ret = __ham_newpgno_log(hashp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hashp->dbp->txn, &new_lsn, 0, ALLOCPGNO,
-		    hashp->dbp->log_fileid, new_addr, next_free,
-		    0, newalloc_flag, type, lsnp, &hashp->hdr->lsn)) != 0)
+	if (DB_LOGGING(dbc)) {
+		if ((ret = __ham_newpgno_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, ALLOCPGNO,
+		    dbp->log_fileid, new_addr, next_free,
+		    0, newalloc_flag, type, lsnp, &hcp->hdr->lsn)) != 0)
 			return (ret);
 
-		hashp->hdr->lsn = new_lsn;
+		hcp->hdr->lsn = new_lsn;
 		if (lsnp != NULL)
 			*lsnp = new_lsn;
 	}
 
 	if (p != NULL) {
 		/* We just took something off the free list, initialize it. */
-		hashp->hdr->last_freed = next_free;
-		P_INIT(p, hashp->hdr->pagesize, PGNO(p), PGNO_INVALID,
+		hcp->hdr->last_freed = next_free;
+		P_INIT(p, hcp->hdr->pagesize, PGNO(p), PGNO_INVALID,
 		    PGNO_INVALID, 0, (u_int8_t)type);
 	} else {
 		/* Get the new page. */
-		if ((ret = __ham_new_page(hashp, new_addr, type, &p)) != 0)
+		if ((ret = __ham_new_page(dbp, new_addr, type, &p)) != 0)
 			return (ret);
 	}
-	if (DB_LOGGING(hashp->dbp))
+	if (DB_LOGGING(dbc))
 		LSN(p) = new_lsn;
 
 	*pp = p;
@@ -1614,94 +1684,123 @@ __ham_overflow_page(dbp, type, pp)
 #ifdef DEBUG
 /*
  * PUBLIC: #ifdef DEBUG
- * PUBLIC: db_pgno_t __bucket_to_page __P((HTAB *, db_pgno_t));
+ * PUBLIC: db_pgno_t __bucket_to_page __P((HASH_CURSOR *, db_pgno_t));
  * PUBLIC: #endif
  */
 db_pgno_t
-__bucket_to_page(hashp, n)
-	HTAB *hashp;
+__bucket_to_page(hcp, n)
+	HASH_CURSOR *hcp;
 	db_pgno_t n;
 {
 	int ret_val;
 
 	ret_val = n + 1;
 	if (n != 0)
-		ret_val += hashp->hdr->spares[__db_log2(n + 1) - 1];
+		ret_val += hcp->hdr->spares[__db_log2(n + 1) - 1];
 	return (ret_val);
 }
 #endif
 
 /*
  * Create a bunch of overflow pages at the current split point.
- * PUBLIC: void __ham_init_ovflpages __P((HTAB *));
+ * PUBLIC: void __ham_init_ovflpages __P((DBC *));
  */
 void
-__ham_init_ovflpages(hp)
-	HTAB *hp;
+__ham_init_ovflpages(dbc)
+	DBC *dbc;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	DB_LSN new_lsn;
 	PAGE *p;
 	db_pgno_t last_pgno, new_pgno;
 	u_int32_t i, curpages, numpages;
 
-	curpages = hp->hdr->spares[hp->hdr->ovfl_point] -
-	    hp->hdr->spares[hp->hdr->ovfl_point - 1];
-	numpages = hp->hdr->ovfl_point + 1 - curpages;
-
-	last_pgno = hp->hdr->last_freed;
-	new_pgno = PGNO_OF(hp, hp->hdr->ovfl_point, curpages + 1);
-	if (DB_LOGGING(hp->dbp)) {
-		(void)__ham_ovfl_log(hp->dbp->dbenv->lg_info,
-		    (DB_TXN *)hp->dbp->txn, &new_lsn, 0,
-		    hp->dbp->log_fileid, new_pgno,
-		    numpages, last_pgno, hp->hdr->ovfl_point, &hp->hdr->lsn);
-		hp->hdr->lsn = new_lsn;
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+
+	curpages = hcp->hdr->spares[hcp->hdr->ovfl_point] -
+	    hcp->hdr->spares[hcp->hdr->ovfl_point - 1];
+	numpages = hcp->hdr->ovfl_point + 1 - curpages;
+
+	last_pgno = hcp->hdr->last_freed;
+	new_pgno = PGNO_OF(hcp, hcp->hdr->ovfl_point, curpages + 1);
+	if (DB_LOGGING(dbc)) {
+		(void)__ham_ovfl_log(dbp->dbenv->lg_info,
+		    dbc->txn, &new_lsn, 0, dbp->log_fileid, new_pgno,
+		    numpages, last_pgno, hcp->hdr->ovfl_point, &hcp->hdr->lsn);
+		hcp->hdr->lsn = new_lsn;
 	} else
 		ZERO_LSN(new_lsn);
 
-	hp->hdr->spares[hp->hdr->ovfl_point] += numpages;
+	hcp->hdr->spares[hcp->hdr->ovfl_point] += numpages;
 	for (i = numpages; i > 0; i--) {
-		if (__ham_new_page(hp,
-		    PGNO_OF(hp, hp->hdr->ovfl_point, curpages + i),
+		if (__ham_new_page(dbp,
+		    PGNO_OF(hcp, hcp->hdr->ovfl_point, curpages + i),
 		    P_INVALID, &p) != 0)
 			break;
 		LSN(p) = new_lsn;
 		NEXT_PGNO(p) = last_pgno;
 		last_pgno = PGNO(p);
-		(void)__ham_put_page(hp->dbp, p, 1);
+		(void)__ham_put_page(dbp, p, 1);
 	}
-	hp->hdr->last_freed = last_pgno;
+	hcp->hdr->last_freed = last_pgno;
 }
 
 /*
- * PUBLIC: int __ham_get_cpage __P((HTAB *, HASH_CURSOR *, db_lockmode_t));
+ * PUBLIC: int __ham_get_cpage __P((DBC *, db_lockmode_t));
  */
 int
-__ham_get_cpage(hashp, hcp, mode)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_get_cpage(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	int ret;
 
-	if (hcp->lock == 0 && F_ISSET(hashp->dbp, DB_AM_LOCKING) &&
-	    (ret = __ham_lock_bucket(hashp->dbp, hcp, mode)) != 0)
-		return (ret);
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
+
+	/*
+	 * There are three cases with respect to buckets and locks.  If there
+	 * is no lock held, then if we are locking, we should get the lock.
+	 * If there is a lock held and it's for the current bucket, we don't
+	 * need to do anything.  If there is a lock, but it's for a different
+	 * bucket, then we need to release and get.
+	 */
+	if (F_ISSET(dbp, DB_AM_LOCKING)) {
+		if (hcp->lock != 0 && hcp->lbucket != hcp->bucket) {
+			/*
+			 * If this is the original lock, don't release it,
+			 * because we may need to restore it upon exit.
+			 */
+			if (dbc->txn == NULL &&
+			    !F_ISSET(hcp, H_ORIGINAL) && (ret =
+			    lock_put(dbp->dbenv->lk_info, hcp->lock)) != 0)
+				return (ret);
+			F_CLR(hcp, H_ORIGINAL);
+			hcp->lock = 0;
+		}
+		if (hcp->lock == 0 && (ret = __ham_lock_bucket(dbc, mode)) != 0)
+			return (ret);
+		hcp->lbucket = hcp->bucket;
+	}
 
 	if (hcp->pagep == NULL) {
 		if (hcp->pgno == PGNO_INVALID) {
-			hcp->pgno = BUCKET_TO_PAGE(hashp, hcp->bucket);
+			hcp->pgno = BUCKET_TO_PAGE(hcp, hcp->bucket);
 			hcp->bndx = 0;
 		}
 
 		if ((ret =
-		    __ham_get_page(hashp->dbp, hcp->pgno, &hcp->pagep)) != 0)
+		    __ham_get_page(dbp, hcp->pgno, &hcp->pagep)) != 0)
 			return (ret);
 	}
 
 	if (hcp->dpgno != PGNO_INVALID && hcp->dpagep == NULL)
 		if ((ret =
-		    __ham_get_page(hashp->dbp, hcp->dpgno, &hcp->dpagep)) != 0)
+		    __ham_get_page(dbp, hcp->dpgno, &hcp->dpagep)) != 0)
 			return (ret);
 	return (0);
 }
@@ -1711,28 +1810,30 @@ __ham_get_cpage(hashp, hcp, mode)
  * If the flag is set to H_ISDUP, then we are talking about the
  * duplicate page, not the main page.
  *
- * PUBLIC: int __ham_next_cpage
- * PUBLIC:    __P((HTAB *, HASH_CURSOR *, db_pgno_t, int, u_int32_t));
+ * PUBLIC: int __ham_next_cpage __P((DBC *, db_pgno_t, int, u_int32_t));
  */
 int
-__ham_next_cpage(hashp, hcp, pgno, dirty, flags)
-	HTAB *hashp;
-	HASH_CURSOR *hcp;
+__ham_next_cpage(dbc, pgno, dirty, flags)
+	DBC *dbc;
 	db_pgno_t pgno;
 	int dirty;
 	u_int32_t flags;
 {
+	DB *dbp;
+	HASH_CURSOR *hcp;
 	PAGE *p;
 	int ret;
 
+	dbp = dbc->dbp;
+	hcp = (HASH_CURSOR *)dbc->internal;
 	if (LF_ISSET(H_ISDUP) && hcp->dpagep != NULL &&
-	    (ret = __ham_put_page(hashp->dbp, hcp->dpagep, dirty)) != 0)
+	    (ret = __ham_put_page(dbp, hcp->dpagep, dirty)) != 0)
 		return (ret);
 	else if (!LF_ISSET(H_ISDUP) && hcp->pagep != NULL &&
-	    (ret = __ham_put_page(hashp->dbp, hcp->pagep, dirty)) != 0)
+	    (ret = __ham_put_page(dbp, hcp->pagep, dirty)) != 0)
 		return (ret);
 
-	if ((ret = __ham_get_page(hashp->dbp, pgno, &p)) != 0)
+	if ((ret = __ham_get_page(dbp, pgno, &p)) != 0)
 		return (ret);
 
 	if (LF_ISSET(H_ISDUP)) {
@@ -1753,22 +1854,21 @@ __ham_next_cpage(hashp, hcp, pgno, dirty, flags)
  *	Get the lock on a particular bucket.
  */
 static int
-__ham_lock_bucket(dbp, hcp, mode)
-	DB *dbp;
-	HASH_CURSOR *hcp;
+__ham_lock_bucket(dbc, mode)
+	DBC *dbc;
 	db_lockmode_t mode;
 {
+	HASH_CURSOR *hcp;
 	int ret;
 
-	/*
-	 * What a way to trounce on the memory system.  It might be
-	 * worth copying the lk_info into the hashp.
-	 */
-	ret = 0;
-	dbp->lock.pgno = (db_pgno_t)(hcp->bucket);
-	ret = lock_get(dbp->dbenv->lk_info,
-	    dbp->txn == NULL ?  dbp->locker : dbp->txn->txnid, 0,
-	    &dbp->lock_dbt, mode, &hcp->lock);
+	hcp = (HASH_CURSOR *)dbc->internal;
+	dbc->lock.pgno = (db_pgno_t)(hcp->bucket);
+	if (dbc->txn == NULL)
+		ret = lock_get(dbc->dbp->dbenv->lk_info, dbc->locker, 0,
+		    &dbc->lock_dbt, mode, &hcp->lock);
+	else
+		ret = lock_tget(dbc->dbp->dbenv->lk_info, dbc->txn, 0,
+		    &dbc->lock_dbt, mode, &hcp->lock);
 
 	return (ret < 0 ? EAGAIN : ret);
 }
@@ -1827,45 +1927,3 @@ __ham_dpair(dbp, p, pndx)
 	HOFFSET(p) = HOFFSET(p) + delta;
 	NUM_ENT(p) = NUM_ENT(p) - 2;
 }
-
-#ifdef DEBUG_SLOW
-static void
-__account_page(hashp, pgno, inout)
-	HTAB *hashp;
-	db_pgno_t pgno;
-	int inout;
-{
-	static struct {
-		db_pgno_t pgno;
-		int times;
-	} list[100];
-	static int last;
-	int i, j;
-
-	if (inout == -1)			/* XXX: Kluge */
-		inout = 0;
-
-	/* Find page in list. */
-	for (i = 0; i < last; i++)
-		if (list[i].pgno == pgno)
-			break;
-	/* Not found. */
-	if (i == last) {
-		list[last].times = inout;
-		list[last].pgno = pgno;
-		last++;
-	}
-	list[i].times = inout;
-	if (list[i].times == 0) {
-		for (j = i; j < last; j++)
-			list[j] = list[j + 1];
-		last--;
-	}
-	for (i = 0; i < last; i++, list[i].times++)
-		if (list[i].times > 20 &&
-		    !__is_bitmap_pgno(hashp, list[i].pgno))
-			(void)fprintf(stderr,
-			    "Warning: pg %lu has been out for %d times\n",
-			    (u_long)list[i].pgno, list[i].times);
-}
-#endif /* DEBUG_SLOW */
diff --git a/db2/hash/hash_rec.c b/db2/hash/hash_rec.c
index 727f615828..b58f2c6eb7 100644
--- a/db2/hash/hash_rec.c
+++ b/db2/hash/hash_rec.c
@@ -47,7 +47,7 @@
 #include "config.h"
 
 #ifndef lint
-static const char sccsid[] = "@(#)hash_rec.c	10.19 (Sleepycat) 5/23/98";
+static const char sccsid[] = "@(#)hash_rec.c	10.22 (Sleepycat) 10/21/98";
 #endif /* not lint */
 
 #ifndef NO_SYSTEM_INCLUDES
@@ -80,17 +80,19 @@ __ham_insdel_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_insdel_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	u_int32_t op;
 	int cmp_n, cmp_p, getmeta, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_insdel_print);
 	REC_INTRO(__ham_insdel_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
 	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
 	if (ret != 0) {
@@ -101,16 +103,15 @@ __ham_insdel_recover(logp, dbtp, lsnp, redo, info)
 			 * would not have to undo anything.  In this case,
 			 * don't bother creating a page.
 			 */
-			*lsnp = argp->prev_lsn;
-			ret = 0;
-			goto out;
+			goto done;
 		} else if ((ret = memp_fget(mpf, &argp->pgno,
 		    DB_MPOOL_CREATE, &pagep)) != 0)
 			goto out;
 	}
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, hcp, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	cmp_n = log_compare(lsnp, &LSN(pagep));
@@ -144,7 +145,7 @@ __ham_insdel_recover(logp, dbtp, lsnp, redo, info)
 			    !redo || PAIR_ISDATABIG(argp->opcode) ?
 			    H_OFFPAGE : H_KEYDATA);
 		} else
-			(void) __ham_reputpair(pagep, hashp->hdr->pagesize,
+			(void) __ham_reputpair(pagep, hcp->hdr->pagesize,
 			    argp->ndx, &argp->key, &argp->data);
 
 		LSN(pagep) = redo ? *lsnp : argp->pagelsn;
@@ -163,10 +164,11 @@ __ham_insdel_recover(logp, dbtp, lsnp, redo, info)
 			goto out;
 
 	/* Return the previous LSN. */
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -187,16 +189,18 @@ __ham_newpage_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_newpage_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	int cmp_n, cmp_p, change, getmeta, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_newpage_print);
 	REC_INTRO(__ham_newpage_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
 	ret = memp_fget(mpf, &argp->new_pgno, 0, &pagep);
 	if (ret != 0) {
@@ -214,8 +218,9 @@ __ham_newpage_recover(logp, dbtp, lsnp, redo, info)
 			goto out;
 	}
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	/*
@@ -289,11 +294,13 @@ ppage:	if (argp->prev_pgno != PGNO_INVALID) {
 		}
 
 		if (!change) {
-			if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
+			if ((ret =
+			    __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
 				goto out;
 		} else {
 			LSN(pagep) = redo ? *lsnp : argp->prevlsn;
-			if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
+			if ((ret =
+			    __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
 				goto out;
 		}
 	}
@@ -310,9 +317,7 @@ npage:	if (argp->next_pgno != PGNO_INVALID) {
 				 * so we would not have to undo anything.  In
 				 * this case, don't bother creating a page.
 				 */
-				*lsnp = argp->prev_lsn;
-				ret = 0;
-				goto out;
+				goto done;
 			} else if ((ret =
 			    memp_fget(mpf, &argp->next_pgno,
 			    DB_MPOOL_CREATE, &pagep)) != 0)
@@ -346,10 +351,11 @@ npage:	if (argp->next_pgno != PGNO_INVALID) {
 				goto out;
 		}
 	}
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -372,19 +378,21 @@ __ham_replace_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_replace_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
 	DBT dbt;
-	HTAB *hashp;
 	PAGE *pagep;
 	int32_t grow;
 	int change, cmp_n, cmp_p, getmeta, ret;
 	u_int8_t *hk;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_replace_print);
 	REC_INTRO(__ham_replace_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
 	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
 	if (ret != 0) {
@@ -395,16 +403,15 @@ __ham_replace_recover(logp, dbtp, lsnp, redo, info)
 			 * would not have to undo anything.  In this case,
 			 * don't bother creating a page.
 			 */
-			*lsnp = argp->prev_lsn;
-			ret = 0;
-			goto out;
+			goto done;
 		} else if ((ret = memp_fget(mpf, &argp->pgno,
 		    DB_MPOOL_CREATE, &pagep)) != 0)
 			goto out;
 	}
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	cmp_n = log_compare(lsnp, &LSN(pagep));
@@ -444,10 +451,11 @@ __ham_replace_recover(logp, dbtp, lsnp, redo, info)
 	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
 		goto out;
 
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -468,19 +476,22 @@ __ham_newpgno_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_newpgno_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	int change, cmp_n, cmp_p, getmeta, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_newpgno_print);
 	REC_INTRO(__ham_newpgno_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	/*
@@ -488,34 +499,34 @@ __ham_newpgno_recover(logp, dbtp, lsnp, redo, info)
 	 * to update the meta data; then we need to update the page.
 	 * We'll do the meta-data first.
 	 */
-	cmp_n = log_compare(lsnp, &hashp->hdr->lsn);
-	cmp_p = log_compare(&hashp->hdr->lsn, &argp->metalsn);
+	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
+	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
 
 	change = 0;
 	if ((cmp_p == 0 && redo && argp->opcode == ALLOCPGNO) ||
 	    (cmp_n == 0 && !redo && argp->opcode == DELPGNO)) {
 		/* Need to redo an allocation or undo a deletion. */
-		hashp->hdr->last_freed = argp->free_pgno;
+		hcp->hdr->last_freed = argp->free_pgno;
 		if (redo && argp->old_pgno != 0) /* Must be ALLOCPGNO */
-			hashp->hdr->spares[hashp->hdr->ovfl_point]++;
+			hcp->hdr->spares[hcp->hdr->ovfl_point]++;
 		change = 1;
 	} else if (cmp_p == 0 && redo && argp->opcode == DELPGNO) {
 		/* Need to redo a deletion */
-		hashp->hdr->last_freed = argp->pgno;
+		hcp->hdr->last_freed = argp->pgno;
 		change = 1;
 	} else if (cmp_n == 0 && !redo && argp->opcode == ALLOCPGNO) {
 		/* undo an allocation. */
 		if (argp->old_pgno == 0)
-			hashp->hdr->last_freed = argp->pgno;
+			hcp->hdr->last_freed = argp->pgno;
 		else {
-			hashp->hdr->spares[hashp->hdr->ovfl_point]--;
-			hashp->hdr->last_freed = 0;
+			hcp->hdr->spares[hcp->hdr->ovfl_point]--;
+			hcp->hdr->last_freed = 0;
 		}
 		change = 1;
 	}
 	if (change) {
-		hashp->hdr->lsn = redo ? *lsnp : argp->metalsn;
-		F_SET(file_dbp, DB_HS_DIRTYMETA);
+		hcp->hdr->lsn = redo ? *lsnp : argp->metalsn;
+		F_SET(hcp, H_DIRTY);
 	}
 
 
@@ -530,9 +541,7 @@ __ham_newpgno_recover(logp, dbtp, lsnp, redo, info)
 			 * would not have to undo anything.  In this case,
 			 * don't bother creating a page.
 			 */
-			*lsnp = argp->prev_lsn;
-			ret = 0;
-			goto out;
+			goto done;
 		} else if ((ret = memp_fget(mpf, &argp->pgno,
 		    DB_MPOOL_CREATE, &pagep)) != 0)
 			goto out;
@@ -565,10 +574,11 @@ __ham_newpgno_recover(logp, dbtp, lsnp, redo, info)
 	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
 		goto out;
 
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 
 }
@@ -590,19 +600,22 @@ __ham_splitmeta_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_splitmeta_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	int change, cmp_n, cmp_p, getmeta, ret;
 	u_int32_t pow;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_splitmeta_print);
 	REC_INTRO(__ham_splitmeta_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	/*
@@ -610,43 +623,45 @@ __ham_splitmeta_recover(logp, dbtp, lsnp, redo, info)
 	 * to update the meta data; then we need to update the page.
 	 * We'll do the meta-data first.
 	 */
-	cmp_n = log_compare(lsnp, &hashp->hdr->lsn);
-	cmp_p = log_compare(&hashp->hdr->lsn, &argp->metalsn);
+	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
+	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
 
 	change = 0;
 	if (cmp_p == 0 && redo) {
 		/* Need to redo the split information. */
-		hashp->hdr->max_bucket = argp->bucket + 1;
-		pow = __db_log2(hashp->hdr->max_bucket + 1);
-		if (pow > hashp->hdr->ovfl_point) {
-			hashp->hdr->spares[pow] =
-				hashp->hdr->spares[hashp->hdr->ovfl_point];
-			hashp->hdr->ovfl_point = pow;
+		hcp->hdr->max_bucket = argp->bucket + 1;
+		pow = __db_log2(hcp->hdr->max_bucket + 1);
+		if (pow > hcp->hdr->ovfl_point) {
+			hcp->hdr->spares[pow] =
+				hcp->hdr->spares[hcp->hdr->ovfl_point];
+			hcp->hdr->ovfl_point = pow;
 		}
-		if (hashp->hdr->max_bucket > hashp->hdr->high_mask) {
-			hashp->hdr->low_mask = hashp->hdr->high_mask;
-			hashp->hdr->high_mask =
-			    hashp->hdr->max_bucket | hashp->hdr->low_mask;
+		if (hcp->hdr->max_bucket > hcp->hdr->high_mask) {
+			hcp->hdr->low_mask = hcp->hdr->high_mask;
+			hcp->hdr->high_mask =
+			    hcp->hdr->max_bucket | hcp->hdr->low_mask;
 		}
 		change = 1;
 	} else if (cmp_n == 0 && !redo) {
 		/* Need to undo the split information. */
-		hashp->hdr->max_bucket = argp->bucket;
-		hashp->hdr->ovfl_point = argp->ovflpoint;
-		hashp->hdr->spares[hashp->hdr->ovfl_point] = argp->spares;
-		pow = 1 << __db_log2(hashp->hdr->max_bucket + 1);
-		hashp->hdr->high_mask = pow - 1;
-		hashp->hdr->low_mask = (pow >> 1) - 1;
+		hcp->hdr->max_bucket = argp->bucket;
+		hcp->hdr->ovfl_point = argp->ovflpoint;
+		hcp->hdr->spares[hcp->hdr->ovfl_point] = argp->spares;
+		pow = 1 << __db_log2(hcp->hdr->max_bucket + 1);
+		hcp->hdr->high_mask = pow - 1;
+		hcp->hdr->low_mask = (pow >> 1) - 1;
 		change = 1;
 	}
 	if (change) {
-		hashp->hdr->lsn = redo ? *lsnp : argp->metalsn;
-		F_SET(file_dbp, DB_HS_DIRTYMETA);
+		hcp->hdr->lsn = redo ? *lsnp : argp->metalsn;
+		F_SET(hcp, H_DIRTY);
 	}
-	*lsnp = argp->prev_lsn;
+
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -665,16 +680,18 @@ __ham_splitdata_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_splitdata_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	int change, cmp_n, cmp_p, getmeta, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_splitdata_print);
 	REC_INTRO(__ham_splitdata_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
 	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
 	if (ret != 0) {
@@ -685,16 +702,15 @@ __ham_splitdata_recover(logp, dbtp, lsnp, redo, info)
 			 * would not have to undo anything.  In this case,
 			 * don't bother creating a page.
 			 */
-			*lsnp = argp->prev_lsn;
-			ret = 0;
-			goto out;
+			goto done;
 		} else if ((ret = memp_fget(mpf, &argp->pgno,
 		    DB_MPOOL_CREATE, &pagep)) != 0)
 			goto out;
 	}
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
 	cmp_n = log_compare(lsnp, &LSN(pagep));
@@ -732,10 +748,11 @@ __ham_splitdata_recover(logp, dbtp, lsnp, redo, info)
 	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
 		goto out;
 
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -755,50 +772,52 @@ __ham_ovfl_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_ovfl_args *argp;
-	DB *mdbp, *file_dbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	db_pgno_t max_pgno, pgno;
 	int cmp_n, cmp_p, getmeta, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_ovfl_print);
 	REC_INTRO(__ham_ovfl_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 
-	cmp_n = log_compare(lsnp, &hashp->hdr->lsn);
-	cmp_p = log_compare(&hashp->hdr->lsn, &argp->metalsn);
+	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
+	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
 
 	if (cmp_p == 0 && redo) {
 		/* Redo the allocation. */
-		hashp->hdr->last_freed = argp->start_pgno;
-		hashp->hdr->spares[argp->ovflpoint] += argp->npages;
-		hashp->hdr->lsn = *lsnp;
-		F_SET(file_dbp, DB_HS_DIRTYMETA);
+		hcp->hdr->last_freed = argp->start_pgno;
+		hcp->hdr->spares[argp->ovflpoint] += argp->npages;
+		hcp->hdr->lsn = *lsnp;
+		F_SET(hcp, H_DIRTY);
 	} else if (cmp_n == 0 && !redo) {
-		hashp->hdr->last_freed = argp->free_pgno;
-		hashp->hdr->spares[argp->ovflpoint] -= argp->npages;
-		hashp->hdr->lsn = argp->metalsn;
-		F_SET(file_dbp, DB_HS_DIRTYMETA);
+		hcp->hdr->last_freed = argp->free_pgno;
+		hcp->hdr->spares[argp->ovflpoint] -= argp->npages;
+		hcp->hdr->lsn = argp->metalsn;
+		F_SET(hcp, H_DIRTY);
 	}
 
 	max_pgno = argp->start_pgno + argp->npages - 1;
 	ret = 0;
 	for (pgno = argp->start_pgno; pgno <= max_pgno; pgno++) {
-		ret = memp_fget(mpf, &pgno, 0, &pagep);
-		if (ret != 0) {
-			if (redo && (ret = memp_fget(mpf, &pgno,
-			    DB_MPOOL_CREATE, &pagep)) != 0)
-				goto out;
-			else if (!redo) {
-				(void)__ham_put_page(file_dbp, pagep, 0);
+		if ((ret = memp_fget(mpf, &pgno, 0, &pagep)) != 0) {
+			if (!redo) {
+				ret = 0;
 				continue;
 			}
+			if ((ret = memp_fget(mpf,
+			    &pgno, DB_MPOOL_CREATE, &pagep)) != 0)
+				goto out;
 		}
 		if (redo && log_compare((const DB_LSN *)lsnp,
 		    (const DB_LSN *)&LSN(pagep)) > 0) {
@@ -816,9 +835,11 @@ __ham_ovfl_recover(logp, dbtp, lsnp, redo, info)
 			goto out;
 	}
 
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
+
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
 
@@ -838,19 +859,22 @@ __ham_copypage_recover(logp, dbtp, lsnp, redo, info)
 	void *info;
 {
 	__ham_copypage_args *argp;
-	DB *file_dbp, *mdbp;
+	DB *file_dbp;
+	DBC *dbc;
+	HASH_CURSOR *hcp;
 	DB_MPOOLFILE *mpf;
-	HTAB *hashp;
 	PAGE *pagep;
 	int cmp_n, cmp_p, getmeta, modified, ret;
 
 	getmeta = 0;
-	hashp = NULL;				/* XXX: shut the compiler up. */
+	hcp = NULL;
 	REC_PRINT(__ham_copypage_print);
 	REC_INTRO(__ham_copypage_read);
+	hcp = (HASH_CURSOR *)dbc->internal;
 
-	hashp = (HTAB *)file_dbp->internal;
-	GET_META(file_dbp, hashp);
+	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
+	if (ret != 0)
+		goto out;
 	getmeta = 1;
 	modified = 0;
 
@@ -881,7 +905,7 @@ __ham_copypage_recover(logp, dbtp, lsnp, redo, info)
 		modified = 1;
 	} else if (cmp_n == 0 && !redo) {
 		/* Need to undo update described. */
-		P_INIT(pagep, hashp->hdr->pagesize, argp->pgno, PGNO_INVALID,
+		P_INIT(pagep, hcp->hdr->pagesize, argp->pgno, PGNO_INVALID,
 		    argp->next_pgno, 0, P_HASH);
 		LSN(pagep) = argp->pagelsn;
 		modified = 1;
@@ -918,10 +942,8 @@ donext:	ret = memp_fget(mpf, &argp->next_pgno, 0, &pagep);
 		goto out;
 
 	/* Now fix up the next's next page. */
-do_nn:	if (argp->nnext_pgno == PGNO_INVALID) {
-		*lsnp = argp->prev_lsn;
-		goto out;
-	}
+do_nn:	if (argp->nnext_pgno == PGNO_INVALID)
+		goto done;
 
 	ret = memp_fget(mpf, &argp->nnext_pgno, 0, &pagep);
 	if (ret != 0) {
@@ -932,9 +954,7 @@ do_nn:	if (argp->nnext_pgno == PGNO_INVALID) {
 			 * would not have to undo anything.  In this case,
 			 * don't bother creating a page.
 			 */
-			ret = 0;
-			*lsnp = argp->prev_lsn;
-			goto out;
+			goto done;
 		} else if ((ret = memp_fget(mpf, &argp->nnext_pgno,
 		    DB_MPOOL_CREATE, &pagep)) != 0)
 			goto out;
@@ -957,9 +977,10 @@ do_nn:	if (argp->nnext_pgno == PGNO_INVALID) {
 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
 		goto out;
 
-	*lsnp = argp->prev_lsn;
+done:	*lsnp = argp->prev_lsn;
+	ret = 0;
 
 out:	if (getmeta)
-		RELEASE_META(file_dbp, hashp);
+		RELEASE_META(file_dbp, hcp);
 	REC_CLOSE;
 }
diff --git a/db2/hash/hash_stat.c b/db2/hash/hash_stat.c
index b57ca0950d..1b493d5f40 100644
--- a/db2/hash/hash_stat.c
+++ b/db2/hash/hash_stat.c
@@ -8,7 +8,7 @@
 #include "config.h"
 
 #ifndef lint
-static const char sccsid[] = "@(#)hash_stat.c	10.8 (Sleepycat) 4/26/98";
+static const char sccsid[] = "@(#)hash_stat.c	10.12 (Sleepycat) 12/19/98";
 #endif /* not lint */
 
 #ifndef NO_SYSTEM_INCLUDES
@@ -23,35 +23,22 @@ static const char sccsid[] = "@(#)hash_stat.c	10.8 (Sleepycat) 4/26/98";
 
 /*
  * __ham_stat --
- *	Gather/print the hash statistics.
+ *	Gather/print the hash statistics
  *
- * PUBLIC: int __ham_stat __P((DB *, FILE *));
+ * PUBLIC: int __ham_stat __P((DB *, void *, void *(*)(size_t), u_int32_t));
  */
 int
-__ham_stat(dbp, fp)
+__ham_stat(dbp, spp, db_malloc, flags)
 	DB *dbp;
-	FILE *fp;
+	void *spp;
+	void *(*db_malloc) __P((size_t));
+	u_int32_t flags;
 {
-	HTAB *hashp;
-	int i;
+	COMPQUIET(spp, NULL);
+	COMPQUIET(db_malloc, NULL);
+	COMPQUIET(flags, 0);
 
-	hashp = (HTAB *)dbp->internal;
+	DB_PANIC_CHECK(dbp);
 
-	fprintf(fp, "hash: accesses %lu collisions %lu\n",
-	    hashp->hash_accesses, hashp->hash_collisions);
-	fprintf(fp, "hash: expansions %lu\n", hashp->hash_expansions);
-	fprintf(fp, "hash: overflows %lu\n", hashp->hash_overflows);
-	fprintf(fp, "hash: big key/data pages %lu\n", hashp->hash_bigpages);
-
-	SET_LOCKER(dbp, NULL);
-	GET_META(dbp, hashp);
-	fprintf(fp, "keys %lu maxp %lu\n",
-	    (u_long)hashp->hdr->nelem, (u_long)hashp->hdr->max_bucket);
-
-	for (i = 0; i < NCACHED; i++)
-		fprintf(fp,
-		    "spares[%d] = %lu\n", i, (u_long)hashp->hdr->spares[i]);
-
-	RELEASE_META(dbp, hashp);
-	return (0);
+	return (__db_eopnotsup(dbp->dbenv));
 }