📄 hash_page.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_page.c,v 11.87 2002/08/15 02:46:20 bostic Exp $";#endif /* not lint *//* * PACKAGE: hashing * * DESCRIPTION: * Page manipulation for hashing package. */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/hash.h"#include "dbinc/lock.h"static int __ham_c_delpg __P((DBC *, db_pgno_t, db_pgno_t, u_int32_t, db_ham_mode, u_int32_t *));/* * PUBLIC: int __ham_item __P((DBC *, db_lockmode_t, db_pgno_t *)); */int__ham_item(dbc, mode, pgnop) DBC *dbc; db_lockmode_t mode; db_pgno_t *pgnop;{ DB *dbp; HASH_CURSOR *hcp; db_pgno_t next_pgno; int ret; dbp = dbc->dbp; hcp = (HASH_CURSOR *)dbc->internal; if (F_ISSET(hcp, H_DELETED)) { __db_err(dbp->dbenv, "Attempt to return a deleted item"); return (EINVAL); } F_CLR(hcp, H_OK | H_NOMORE); /* Check if we need to get a page for this cursor. */ if ((ret = __ham_get_cpage(dbc, mode)) != 0) return (ret);recheck: /* Check if we are looking for space in which to insert an item. */ if (hcp->seek_size && hcp->seek_found_page == PGNO_INVALID && hcp->seek_size < P_FREESPACE(dbp, hcp->page)) hcp->seek_found_page = hcp->pgno; /* Check for off-page duplicates. */ if (hcp->indx < NUM_ENT(hcp->page) && HPAGE_TYPE(dbp, hcp->page, H_DATAINDEX(hcp->indx)) == H_OFFDUP) { memcpy(pgnop, HOFFDUP_PGNO(H_PAIRDATA(dbp, hcp->page, hcp->indx)), sizeof(db_pgno_t)); F_SET(hcp, H_OK); return (0); } /* Check if we need to go on to the next page. */ if (F_ISSET(hcp, H_ISDUP)) /* * 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(&hcp->dup_len, HKEYDATA_DATA(H_PAIRDATA(dbp, hcp->page, hcp->indx)) + hcp->dup_off, sizeof(db_indx_t)); if (hcp->indx >= (db_indx_t)NUM_ENT(hcp->page)) { /* Fetch next page. */ if (NEXT_PGNO(hcp->page) == PGNO_INVALID) { F_SET(hcp, H_NOMORE); return (DB_NOTFOUND); } next_pgno = NEXT_PGNO(hcp->page); hcp->indx = 0; if ((ret = __ham_next_cpage(dbc, next_pgno, 0)) != 0) return (ret); goto recheck; } F_SET(hcp, H_OK); return (0);}/* * PUBLIC: int __ham_item_reset __P((DBC *)); */int__ham_item_reset(dbc) DBC *dbc;{ DB *dbp; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; int ret; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; ret = 0; if (hcp->page != NULL) ret = mpf->put(mpf, hcp->page, 0); __ham_item_init(dbc); return (ret);}/* * PUBLIC: void __ham_item_init __P((DBC *)); */void__ham_item_init(dbc) DBC *dbc;{ HASH_CURSOR *hcp; hcp = (HASH_CURSOR *)dbc->internal; /* * If this cursor still holds any locks, we must * release them if we are not running with transactions. */ (void)__TLPUT(dbc, 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; LOCK_INIT(hcp->lock); hcp->lock_mode = DB_LOCK_NG; 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; hcp->pgno = PGNO_INVALID; hcp->indx = NDX_INVALID; hcp->page = NULL;}/* * Returns the last item in a bucket. * * PUBLIC: int __ham_item_last __P((DBC *, db_lockmode_t, db_pgno_t *)); */int__ham_item_last(dbc, mode, pgnop) DBC *dbc; db_lockmode_t mode; db_pgno_t *pgnop;{ HASH_CURSOR *hcp; int ret; hcp = (HASH_CURSOR *)dbc->internal; if ((ret = __ham_item_reset(dbc)) != 0) return (ret); hcp->bucket = hcp->hdr->max_bucket; hcp->pgno = BUCKET_TO_PAGE(hcp, hcp->bucket); F_SET(hcp, H_OK); return (__ham_item_prev(dbc, mode, pgnop));}/* * PUBLIC: int __ham_item_first __P((DBC *, db_lockmode_t, db_pgno_t *)); */int__ham_item_first(dbc, mode, pgnop) DBC *dbc; db_lockmode_t mode; db_pgno_t *pgnop;{ HASH_CURSOR *hcp; int ret; hcp = (HASH_CURSOR *)dbc->internal; if ((ret = __ham_item_reset(dbc)) != 0) return (ret); F_SET(hcp, H_OK); hcp->bucket = 0; hcp->pgno = BUCKET_TO_PAGE(hcp, hcp->bucket); return (__ham_item_next(dbc, mode, pgnop));}/* * __ham_item_prev -- * Returns a pointer to key/data pair on a page. In the case of * bigkeys, just returns the page number and index of the bigkey * pointer pair. * * PUBLIC: int __ham_item_prev __P((DBC *, db_lockmode_t, db_pgno_t *)); */int__ham_item_prev(dbc, mode, pgnop) DBC *dbc; db_lockmode_t mode; db_pgno_t *pgnop;{ DB *dbp; HASH_CURSOR *hcp; db_pgno_t next_pgno; int ret; hcp = (HASH_CURSOR *)dbc->internal; dbp = dbc->dbp; /* * There are 5 cases for backing up in a hash file. * Case 1: In the middle of a page, no duplicates, just dec the index. * Case 2: In the middle of a duplicate set, back up one. * Case 3: At the beginning of a duplicate set, get out of set and * back up to next key. * 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(hcp, H_OK | H_NOMORE | H_DELETED); if ((ret = __ham_get_cpage(dbc, mode)) != 0) return (ret); /* * 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(hcp, H_NEXT_NODUP) && F_ISSET(hcp, H_ISDUP)) { if (HPAGE_TYPE(dbp, hcp->page, H_DATAINDEX(hcp->indx)) == H_OFFDUP) { memcpy(pgnop, HOFFDUP_PGNO(H_PAIRDATA(dbp, hcp->page, hcp->indx)), sizeof(db_pgno_t)); F_SET(hcp, H_OK); return (0); } /* Duplicates are on-page. */ if (hcp->dup_off != 0) { memcpy(&hcp->dup_len, HKEYDATA_DATA( H_PAIRDATA(dbp, hcp->page, hcp->indx)) + hcp->dup_off - sizeof(db_indx_t), sizeof(db_indx_t)); hcp->dup_off -= DUP_SIZE(hcp->dup_len); return (__ham_item(dbc, mode, pgnop)); } } /* * If we get here, we are not in a duplicate set, and just need * to back up the cursor. There are still three cases: * midpage, beginning of page, beginning of bucket. */ if (F_ISSET(hcp, H_DUPONLY)) { F_CLR(hcp, H_OK); F_SET(hcp, H_NOMORE); return (0); } else /* * We are no longer in a dup set; flag this so the dup code * will reinitialize should we stumble upon another one. */ F_CLR(hcp, H_ISDUP); if (hcp->indx == 0) { /* Beginning of page. */ hcp->pgno = PREV_PGNO(hcp->page); if (hcp->pgno == PGNO_INVALID) { /* Beginning of bucket. */ F_SET(hcp, H_NOMORE); return (DB_NOTFOUND); } else if ((ret = __ham_next_cpage(dbc, hcp->pgno, 0)) != 0) return (ret); else hcp->indx = NUM_ENT(hcp->page); } /* * Either we've got the cursor set up to be decremented, or we * have to find the end of a bucket. */ if (hcp->indx == NDX_INVALID) { DB_ASSERT(hcp->page != NULL); hcp->indx = NUM_ENT(hcp->page); for (next_pgno = NEXT_PGNO(hcp->page); next_pgno != PGNO_INVALID; next_pgno = NEXT_PGNO(hcp->page)) { if ((ret = __ham_next_cpage(dbc, next_pgno, 0)) != 0) return (ret); hcp->indx = NUM_ENT(hcp->page); } if (hcp->indx == 0) { /* Bucket was empty. */ F_SET(hcp, H_NOMORE); return (DB_NOTFOUND); } } hcp->indx -= 2; return (__ham_item(dbc, mode, pgnop));}/* * Sets the cursor to the next key/data pair on a page. * * PUBLIC: int __ham_item_next __P((DBC *, db_lockmode_t, db_pgno_t *)); */int__ham_item_next(dbc, mode, pgnop) DBC *dbc; db_lockmode_t mode; db_pgno_t *pgnop;{ HASH_CURSOR *hcp; int ret; hcp = (HASH_CURSOR *)dbc->internal; if ((ret = __ham_get_cpage(dbc, mode)) != 0) return (ret); /* * 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(hcp, H_DELETED)) { if (hcp->indx != NDX_INVALID && F_ISSET(hcp, H_ISDUP) && HPAGE_TYPE(dbc->dbp, hcp->page, H_DATAINDEX(hcp->indx)) == H_DUPLICATE && 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->indx += 2; } } else if (!F_ISSET(hcp, H_ISDUP) && F_ISSET(hcp, H_DUPONLY)) { F_CLR(hcp, H_OK); F_SET(hcp, H_NOMORE); return (0); } else if (F_ISSET(hcp, H_ISDUP) && F_ISSET(hcp, H_NEXT_NODUP)) { F_CLR(hcp, H_ISDUP); hcp->indx += 2; } F_CLR(hcp, H_DELETED); } else if (hcp->indx == NDX_INVALID) { hcp->indx = 0; F_CLR(hcp, H_ISDUP); } else if (F_ISSET(hcp, H_NEXT_NODUP)) { hcp->indx += 2; F_CLR(hcp, H_ISDUP); } else if (F_ISSET(hcp, H_ISDUP) && hcp->dup_tlen != 0) { 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->dup_off += DUP_SIZE(hcp->dup_len); if (hcp->dup_off >= hcp->dup_tlen) { F_CLR(hcp, H_ISDUP); hcp->indx += 2; } } else if (F_ISSET(hcp, H_DUPONLY)) { F_CLR(hcp, H_OK); F_SET(hcp, H_NOMORE); return (0); } else { hcp->indx += 2; F_CLR(hcp, H_ISDUP); } return (__ham_item(dbc, mode, pgnop));}/* * PUBLIC: void __ham_putitem __P((DB *, PAGE *p, const DBT *, int)); * * This is a little bit sleazy in that we're overloading the meaning * of the H_OFFPAGE type here. When we recover deletes, we have the * entire entry instead of having only the DBT, so we'll pass type * H_OFFPAGE to mean, "copy the whole entry" as opposed to constructing * an H_KEYDATA around it. */void__ham_putitem(dbp, p, dbt, type) DB *dbp; PAGE *p; const DBT *dbt; int type;{ u_int16_t n, off; db_indx_t *inp; n = NUM_ENT(p); inp = P_INP(dbp, p);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -