📄 hash.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2002 * Sleepycat Software. All rights reserved. *//* * Copyright (c) 1990, 1993, 1994 * Margo Seltzer. All rights reserved. *//* * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. 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 "db_config.h"#ifndef lintstatic const char revid[] = "$Id: hash.c,v 11.166 2002/08/06 06:11:25 bostic Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <stdlib.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/btree.h"#include "dbinc/hash.h"#include "dbinc/lock.h"static int __ham_bulk __P((DBC *, DBT *, u_int32_t));static int __ham_c_close __P((DBC *, db_pgno_t, int *));static int __ham_c_del __P((DBC *));static int __ham_c_destroy __P((DBC *));static int __ham_c_get __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));static int __ham_c_put __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));static int __ham_c_writelock __P((DBC *));static int __ham_dup_return __P((DBC *, DBT *, u_int32_t));static int __ham_expand_table __P((DBC *));static int __ham_lookup __P((DBC *, const DBT *, u_int32_t, db_lockmode_t, db_pgno_t *));static int __ham_overwrite __P((DBC *, DBT *, u_int32_t));/* * __ham_quick_delete -- * When performing a DB->del operation that does not involve secondary * indices and is not removing an off-page duplicate tree, we can * speed things up substantially by removing the entire duplicate * set, if any is present, in one operation, rather than by conjuring * up and deleting each of the items individually. (All are stored * in one big HKEYDATA structure.) We don't bother to distinguish * on-page duplicate sets from single, non-dup items; they're deleted * in exactly the same way. * * This function is called by __db_delete when the appropriate * conditions are met, and it performs the delete in the optimized way. * * The cursor should be set to the first item in the duplicate * set, or to the sole key/data pair when the key does not have a * duplicate set, before the function is called. * * PUBLIC: int __ham_quick_delete __P((DBC *)); */int__ham_quick_delete(dbc) DBC *dbc;{ int ret, t_ret; if ((ret = __ham_get_meta(dbc)) != 0) return (ret); /* Assert that we're not using secondary indices. */ DB_ASSERT(!F_ISSET(dbc->dbp, DB_AM_SECONDARY)); /* * We should assert that we're not a primary either, but that * would require grabbing the dbp's mutex, so we don't bother. */ /* Assert that we're set, but not to an off-page duplicate. */ DB_ASSERT(IS_INITIALIZED(dbc)); DB_ASSERT(((HASH_CURSOR *)dbc->internal)->opd == NULL); ret = __ham_del_pair(dbc, 1); if ((t_ret = __ham_release_meta(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}/* ****************** 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;{ DB_ENV *dbenv; HASH_CURSOR *new_curs; int ret; dbenv = dbc->dbp->dbenv; if ((ret = __os_calloc(dbenv, 1, sizeof(struct cursor_t), &new_curs)) != 0) return (ret); if ((ret = __os_malloc(dbenv, dbc->dbp->pgsize, &new_curs->split_buf)) != 0) { __os_free(dbenv, new_curs); return (ret); } dbc->internal = (DBC_INTERNAL *) new_curs; dbc->c_close = __db_c_close; dbc->c_count = __db_c_count; dbc->c_del = __db_c_del; dbc->c_dup = __db_c_dup; dbc->c_get = dbc->c_real_get = __db_c_get; dbc->c_pget = __db_c_pget; dbc->c_put = __db_c_put; dbc->c_am_bulk = __ham_bulk; dbc->c_am_close = __ham_c_close; dbc->c_am_del = __ham_c_del; dbc->c_am_destroy = __ham_c_destroy; dbc->c_am_get = __ham_c_get; dbc->c_am_put = __ham_c_put; dbc->c_am_writelock = __ham_c_writelock; __ham_item_init(dbc); return (0);}/* * __ham_c_close -- * Close down the cursor from a single use. */static int__ham_c_close(dbc, root_pgno, rmroot) DBC *dbc; db_pgno_t root_pgno; int *rmroot;{ DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; HKEYDATA *dp; int doroot, gotmeta, ret, t_ret; u_int32_t dirty; COMPQUIET(rmroot, 0); mpf = dbc->dbp->mpf; dirty = 0; doroot = gotmeta = ret = 0; hcp = (HASH_CURSOR *) dbc->internal; /* Check for off page dups. */ if (dbc->internal->opd != NULL) { if ((ret = __ham_get_meta(dbc)) != 0) goto done; gotmeta = 1; if ((ret = __ham_get_cpage(dbc, DB_LOCK_READ)) != 0) goto out; dp = (HKEYDATA *)H_PAIRDATA(dbc->dbp, hcp->page, hcp->indx); /* If its not a dup we aborted before we changed it. */ if (HPAGE_PTYPE(dp) == H_OFFDUP) memcpy(&root_pgno, HOFFPAGE_PGNO(dp), sizeof(db_pgno_t)); else root_pgno = PGNO_INVALID; if ((ret = hcp->opd->c_am_close(hcp->opd, root_pgno, &doroot)) != 0) goto out; if (doroot != 0) { if ((ret = __ham_del_pair(dbc, 1)) != 0) goto out; dirty = DB_MPOOL_DIRTY; } }out: if (hcp->page != NULL && (t_ret = mpf->put(mpf, hcp->page, dirty)) != 0 && ret == 0) ret = t_ret; if (gotmeta != 0 && (t_ret = __ham_release_meta(dbc)) != 0 && ret == 0) ret = t_ret;done: __ham_item_init(dbc); return (ret);}/* * __ham_c_destroy -- * Cleanup the access method private part of a cursor. */static int__ham_c_destroy(dbc) DBC *dbc;{ HASH_CURSOR *hcp; hcp = (HASH_CURSOR *)dbc->internal; if (hcp->split_buf != NULL) __os_free(dbc->dbp->dbenv, hcp->split_buf); __os_free(dbc->dbp->dbenv, hcp); return (0);}/* * __ham_c_count -- * Return a count of on-page duplicates. * * PUBLIC: int __ham_c_count __P((DBC *, db_recno_t *)); */int__ham_c_count(dbc, recnop) DBC *dbc; db_recno_t *recnop;{ DB *dbp; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; db_indx_t len; db_recno_t recno; int ret, t_ret; u_int8_t *p, *pend; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; recno = 0; if ((ret = __ham_get_cpage(dbc, DB_LOCK_READ)) != 0) return (ret); switch (HPAGE_PTYPE(H_PAIRDATA(dbp, hcp->page, hcp->indx))) { case H_KEYDATA: case H_OFFPAGE: recno = 1; break; case H_DUPLICATE: p = HKEYDATA_DATA(H_PAIRDATA(dbp, hcp->page, hcp->indx)); pend = p + LEN_HDATA(dbp, hcp->page, dbp->pgsize, hcp->indx); for (; p < pend; recno++) { /* p may be odd, so copy rather than just dereffing */ memcpy(&len, p, sizeof(db_indx_t)); p += 2 * sizeof(db_indx_t) + len; } break; default: ret = __db_pgfmt(dbp->dbenv, hcp->pgno); goto err; } *recnop = recno;err: if ((t_ret = mpf->put(mpf, hcp->page, 0)) != 0 && ret == 0) ret = t_ret; hcp->page = NULL; return (ret);}static int__ham_c_del(dbc) DBC *dbc;{ DB *dbp; DBT repldbt; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; int ret, t_ret; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; if (F_ISSET(hcp, H_DELETED)) return (DB_NOTFOUND); if ((ret = __ham_get_meta(dbc)) != 0) goto out; if ((ret = __ham_get_cpage(dbc, DB_LOCK_WRITE)) != 0) goto out; /* Off-page duplicates. */ if (HPAGE_TYPE(dbp, hcp->page, H_DATAINDEX(hcp->indx)) == H_OFFDUP) goto out; if (F_ISSET(hcp, H_ISDUP)) { /* On-page duplicate. */ if (hcp->dup_off == 0 && DUP_SIZE(hcp->dup_len) == LEN_HDATA(dbp, hcp->page, hcp->hdr->dbmeta.pagesize, hcp->indx)) ret = __ham_del_pair(dbc, 1); else { repldbt.flags = 0; F_SET(&repldbt, DB_DBT_PARTIAL); repldbt.doff = hcp->dup_off; repldbt.dlen = DUP_SIZE(hcp->dup_len); repldbt.size = 0; repldbt.data = HKEYDATA_DATA(H_PAIRDATA(dbp, hcp->page, hcp->indx)); if ((ret = __ham_replpair(dbc, &repldbt, 0)) == 0) { hcp->dup_tlen -= DUP_SIZE(hcp->dup_len); F_SET(hcp, H_DELETED); ret = __ham_c_update(dbc, DUP_SIZE(hcp->dup_len), 0, 1); } } } else /* Not a duplicate */ ret = __ham_del_pair(dbc, 1);out: if (hcp->page != NULL) { if ((t_ret = mpf->put(mpf, hcp->page, ret == 0 ? DB_MPOOL_DIRTY : 0)) && ret == 0) ret = t_ret; hcp->page = NULL; } if ((t_ret = __ham_release_meta(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __ham_c_dup -- * Duplicate a hash cursor, such that the new one holds appropriate * locks for the position of the original. * * PUBLIC: int __ham_c_dup __P((DBC *, DBC *)); */int__ham_c_dup(orig_dbc, new_dbc) DBC *orig_dbc, *new_dbc;{ HASH_CURSOR *orig, *new; orig = (HASH_CURSOR *)orig_dbc->internal; new = (HASH_CURSOR *)new_dbc->internal; new->bucket = orig->bucket; new->lbucket = orig->lbucket; new->dup_off = orig->dup_off; new->dup_len = orig->dup_len; new->dup_tlen = orig->dup_tlen; if (F_ISSET(orig, H_DELETED)) F_SET(new, H_DELETED); if (F_ISSET(orig, H_ISDUP)) F_SET(new, H_ISDUP); /* * If the old cursor held a lock and we're not in transactions, get one * for the new one. The reason that we don't need a new lock if we're * in a transaction is because we already hold a lock and will continue * to do so until commit, so there is no point in reaquiring it. We * don't know if the old lock was a read or write lock, but it doesn't * matter. We'll get a read lock. We know that this locker already * holds a lock of the correct type, so if we need a write lock and * request it, we know that we'll get it. */ if (!LOCK_ISSET(orig->lock) || orig_dbc->txn != NULL) return (0); return (__ham_lock_bucket(new_dbc, DB_LOCK_READ));}static int__ham_c_get(dbc, key, data, flags, pgnop) DBC *dbc; DBT *key; DBT *data; u_int32_t flags; db_pgno_t *pgnop;{ DB *dbp; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; db_lockmode_t lock_type; int get_key, ret, t_ret; hcp = (HASH_CURSOR *)dbc->internal; dbp = dbc->dbp; mpf = dbp->mpf; /* Clear OR'd in additional bits so we can check for flag equality. */ if (F_ISSET(dbc, DBC_RMW)) lock_type = DB_LOCK_WRITE; else lock_type = DB_LOCK_READ; if ((ret = __ham_get_meta(dbc)) != 0) return (ret); hcp->seek_size = 0; ret = 0; get_key = 1; switch (flags) { case DB_PREV_NODUP: F_SET(hcp, H_NEXT_NODUP); /* FALLTHROUGH */ case DB_PREV: if (IS_INITIALIZED(dbc)) { ret = __ham_item_prev(dbc, lock_type, pgnop); break; } /* FALLTHROUGH */ case DB_LAST: ret = __ham_item_last(dbc, lock_type, pgnop); break; case DB_NEXT_NODUP: F_SET(hcp, H_NEXT_NODUP); /* FALLTHROUGH */ case DB_NEXT: if (IS_INITIALIZED(dbc)) { ret = __ham_item_next(dbc, lock_type, pgnop); break; } /* FALLTHROUGH */ case DB_FIRST: ret = __ham_item_first(dbc, lock_type, pgnop); break; case DB_NEXT_DUP: /* cgetchk has already determined that the cursor is set. */ F_SET(hcp, H_DUPONLY); ret = __ham_item_next(dbc, lock_type, pgnop); break; case DB_SET: case DB_SET_RANGE: case DB_GET_BOTH: case DB_GET_BOTH_RANGE: ret = __ham_lookup(dbc, key, 0, lock_type, pgnop); get_key = 0; break; case DB_GET_BOTHC: F_SET(hcp, H_DUPONLY); ret = __ham_item_next(dbc, lock_type, pgnop); get_key = 0; break; case DB_CURRENT: /* cgetchk has already determined that the cursor is set. */ if (F_ISSET(hcp, H_DELETED)) { ret = DB_KEYEMPTY; goto err; } ret = __ham_item(dbc, lock_type, pgnop); break; } /* * Must always enter this loop to do error handling and * check for big key/data pair. */ for (;;) { if (ret != 0 && ret != DB_NOTFOUND) goto err; else if (F_ISSET(hcp, H_OK)) { if (*pgnop == PGNO_INVALID) ret = __ham_dup_return(dbc, data, flags); break; } else if (!F_ISSET(hcp, H_NOMORE)) { __db_err(dbp->dbenv, "H_NOMORE returned to __ham_c_get"); ret = EINVAL; break; } /* * Ran out of entries in a bucket; change buckets. */ switch (flags) { case DB_LAST: case DB_PREV:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -