📄 bt_recno.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1997-2002 * Sleepycat Software. All rights reserved. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: bt_recno.c,v 11.106 2002/08/16 04:56:30 ubell Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <limits.h>#include <stdio.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/btree.h"#include "dbinc/db_shash.h"#include "dbinc/lock.h"static int __ram_add __P((DBC *, db_recno_t *, DBT *, u_int32_t, u_int32_t));static int __ram_source __P((DB *));static int __ram_sread __P((DBC *, db_recno_t));static int __ram_update __P((DBC *, db_recno_t, int));/* * In recno, there are two meanings to the on-page "deleted" flag. If we're * re-numbering records, it means the record was implicitly created. We skip * over implicitly created records if doing a cursor "next" or "prev", and * return DB_KEYEMPTY if they're explicitly requested.. If not re-numbering * records, it means that the record was implicitly created, or was deleted. * We skip over implicitly created or deleted records if doing a cursor "next" * or "prev", and return DB_KEYEMPTY if they're explicitly requested. * * If we're re-numbering records, then we have to detect in the cursor that * a record was deleted, and adjust the cursor as necessary on the next get. * If we're not re-numbering records, then we can detect that a record has * been deleted by looking at the actual on-page record, so we completely * ignore the cursor's delete flag. This is different from the B+tree code. * It also maintains whether the cursor references a deleted record in the * cursor, and it doesn't always check the on-page value. */#define CD_SET(cp) { \ if (F_ISSET(cp, C_RENUMBER)) \ F_SET(cp, C_DELETED); \}#define CD_CLR(cp) { \ if (F_ISSET(cp, C_RENUMBER)) { \ F_CLR(cp, C_DELETED); \ cp->order = INVALID_ORDER; \ } \}#define CD_ISSET(cp) \ (F_ISSET(cp, C_RENUMBER) && F_ISSET(cp, C_DELETED))/* * Macros for comparing the ordering of two cursors. * cp1 comes before cp2 iff one of the following holds: * cp1's recno is less than cp2's recno * recnos are equal, both deleted, and cp1's order is less than cp2's * recnos are equal, cp1 deleted, and cp2 not deleted */#define C_LESSTHAN(cp1, cp2) \ (((cp1)->recno < (cp2)->recno) || \ (((cp1)->recno == (cp2)->recno) && \ ((CD_ISSET((cp1)) && CD_ISSET((cp2)) && (cp1)->order < (cp2)->order) || \ (CD_ISSET((cp1)) && !CD_ISSET((cp2))))))/* * cp1 is equal to cp2 iff their recnos and delete flags are identical, * and if the delete flag is set their orders are also identical. */#define C_EQUAL(cp1, cp2) \ ((cp1)->recno == (cp2)->recno && CD_ISSET((cp1)) == CD_ISSET((cp2)) && \ (!CD_ISSET((cp1)) || (cp1)->order == (cp2)->order))/* * Do we need to log the current cursor adjustment? */#define CURADJ_LOG(dbc) \ (DBC_LOGGING((dbc)) && (dbc)->txn != NULL && (dbc)->txn->parent != NULL)/* * After a search, copy the found page into the cursor, discarding any * currently held lock. */#define STACK_TO_CURSOR(cp) { \ (cp)->page = (cp)->csp->page; \ (cp)->pgno = (cp)->csp->page->pgno; \ (cp)->indx = (cp)->csp->indx; \ (void)__TLPUT(dbc, (cp)->lock); \ (cp)->lock = (cp)->csp->lock; \ (cp)->lock_mode = (cp)->csp->lock_mode; \}/* * __ram_open -- * Recno open function. * * PUBLIC: int __ram_open __P((DB *, * PUBLIC: DB_TXN *, const char *, db_pgno_t, u_int32_t)); */int__ram_open(dbp, txn, name, base_pgno, flags) DB *dbp; DB_TXN *txn; const char *name; db_pgno_t base_pgno; u_int32_t flags;{ BTREE *t; DBC *dbc; int ret, t_ret; COMPQUIET(name, NULL); t = dbp->bt_internal; /* Initialize the remaining fields/methods of the DB. */ dbp->stat = __bam_stat; /* Start up the tree. */ if ((ret = __bam_read_root(dbp, txn, base_pgno, flags)) != 0) return (ret); /* * If the user specified a source tree, open it and map it in. * * !!! * We don't complain if the user specified transactions or threads. * It's possible to make it work, but you'd better know what you're * doing! */ if (t->re_source != NULL && (ret = __ram_source(dbp)) != 0) return (ret); /* If we're snapshotting an underlying source file, do it now. */ if (F_ISSET(dbp, DB_AM_SNAPSHOT)) { /* Allocate a cursor. */ if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) return (ret); /* Do the snapshot. */ if ((ret = __ram_update(dbc, DB_MAX_RECORDS, 0)) != 0 && ret == DB_NOTFOUND) ret = 0; /* Discard the cursor. */ if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) ret = t_ret; } return (ret);}/* * __ram_append -- * Recno append function. * * PUBLIC: int __ram_append __P((DBC *, DBT *, DBT *)); */int__ram_append(dbc, key, data) DBC *dbc; DBT *key, *data;{ BTREE_CURSOR *cp; int ret; cp = (BTREE_CURSOR *)dbc->internal; /* * Make sure we've read in all of the backing source file. If * we found the record or it simply didn't exist, add the * user's record. */ ret = __ram_update(dbc, DB_MAX_RECORDS, 0); if (ret == 0 || ret == DB_NOTFOUND) ret = __ram_add(dbc, &cp->recno, data, DB_APPEND, 0); /* Return the record number. */ if (ret == 0) ret = __db_retcopy(dbc->dbp->dbenv, key, &cp->recno, sizeof(cp->recno), &dbc->rkey->data, &dbc->rkey->ulen); return (ret);}/* * __ram_c_del -- * Recno cursor->c_del function. * * PUBLIC: int __ram_c_del __P((DBC *)); */int__ram_c_del(dbc) DBC *dbc;{ BKEYDATA bk; BTREE *t; BTREE_CURSOR *cp; DB *dbp; DB_LSN lsn; DBT hdr, data; EPG *epg; int exact, ret, stack; dbp = dbc->dbp; cp = (BTREE_CURSOR *)dbc->internal; t = dbp->bt_internal; stack = 0; /* * The semantics of cursors during delete are as follows: in * non-renumbering recnos, records are replaced with a marker * containing a delete flag. If the record referenced by this cursor * has already been deleted, we will detect that as part of the delete * operation, and fail. * * In renumbering recnos, cursors which represent deleted items * are flagged with the C_DELETED flag, and it is an error to * call c_del a second time without an intervening cursor motion. */ if (CD_ISSET(cp)) return (DB_KEYEMPTY); /* Search the tree for the key; delete only deletes exact matches. */ if ((ret = __bam_rsearch(dbc, &cp->recno, S_DELETE, 1, &exact)) != 0) goto err; if (!exact) { ret = DB_NOTFOUND; goto err; } stack = 1; /* Copy the page into the cursor. */ STACK_TO_CURSOR(cp); /* * If re-numbering records, the on-page deleted flag can only mean * that this record was implicitly created. Applications aren't * permitted to delete records they never created, return an error. * * If not re-numbering records, the on-page deleted flag means that * this record was implicitly created, or, was deleted at some time. * The former is an error because applications aren't permitted to * delete records they never created, the latter is an error because * if the record was "deleted", we could never have found it. */ if (B_DISSET(GET_BKEYDATA(dbp, cp->page, cp->indx)->type)) { ret = DB_KEYEMPTY; goto err; } if (F_ISSET(cp, C_RENUMBER)) { /* Delete the item, adjust the counts, adjust the cursors. */ if ((ret = __bam_ditem(dbc, cp->page, cp->indx)) != 0) goto err; __bam_adjust(dbc, -1); if (__ram_ca(dbc, CA_DELETE) > 0 && CURADJ_LOG(dbc) && (ret = __bam_rcuradj_log(dbp, dbc->txn, &lsn, 0, CA_DELETE, cp->root, cp->recno, cp->order)) != 0) goto err; /* * If the page is empty, delete it. * * We never delete a root page. First, root pages of primary * databases never go away, recno or otherwise. However, if * it's the root page of an off-page duplicates database, then * it can be deleted. We don't delete it here because we have * no way of telling the primary database page holder (e.g., * the hash access method) that its page element should cleaned * up because the underlying tree is gone. So, we keep the page * around until the last cursor referencing the empty tree is * are closed, and then clean it up. */ if (NUM_ENT(cp->page) == 0 && PGNO(cp->page) != cp->root) { /* * We already have a locked stack of pages. However, * there are likely entries in the stack that aren't * going to be emptied by removing the single reference * to the emptied page (or one of its parents). */ for (epg = cp->csp; epg >= cp->sp; --epg) if (NUM_ENT(epg->page) > 1) break; /* * We want to delete a single item out of the last page * that we're not deleting. */ ret = __bam_dpages(dbc, epg); /* * Regardless of the return from __bam_dpages, it will * discard our stack and pinned page. */ stack = 0; cp->page = NULL; } } else { /* Use a delete/put pair to replace the record with a marker. */ if ((ret = __bam_ditem(dbc, cp->page, cp->indx)) != 0) goto err; B_TSET(bk.type, B_KEYDATA, 1); bk.len = 0; memset(&hdr, 0, sizeof(hdr)); hdr.data = &bk; hdr.size = SSZA(BKEYDATA, data); memset(&data, 0, sizeof(data)); data.data = (void *)""; data.size = 0; if ((ret = __db_pitem(dbc, cp->page, cp->indx, BKEYDATA_SIZE(0), &hdr, &data)) != 0) goto err; } t->re_modified = 1;err: if (stack) __bam_stkrel(dbc, STK_CLRDBC); return (ret);}/* * __ram_c_get -- * Recno cursor->c_get function. * * PUBLIC: int __ram_c_get * PUBLIC: __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *)); */int__ram_c_get(dbc, key, data, flags, pgnop) DBC *dbc; DBT *key, *data; u_int32_t flags; db_pgno_t *pgnop;{ BTREE_CURSOR *cp; DB *dbp; int cmp, exact, ret; COMPQUIET(pgnop, NULL); dbp = dbc->dbp; cp = (BTREE_CURSOR *)dbc->internal; LF_CLR(DB_MULTIPLE|DB_MULTIPLE_KEY);retry: switch (flags) { case DB_CURRENT: /* * If we're using mutable records and the deleted flag is * set, the cursor is pointing at a nonexistent record; * return an error. */ if (CD_ISSET(cp)) return (DB_KEYEMPTY); break; case DB_NEXT_DUP: /* * If we're not in an off-page dup set, we know there's no * next duplicate since recnos don't have them. If we * are in an off-page dup set, the next item assuredly is * a dup, so we set flags to DB_NEXT and keep going. */ if (!F_ISSET(dbc, DBC_OPD)) return (DB_NOTFOUND); /* FALLTHROUGH */ case DB_NEXT_NODUP: /* * Recno databases don't have duplicates, set flags to DB_NEXT * and keep going. */ /* FALLTHROUGH */ case DB_NEXT: flags = DB_NEXT; /* * If record numbers are mutable: if we just deleted a record, * we have to avoid incrementing the record number so that we * return the right record by virtue of renumbering the tree. */ if (CD_ISSET(cp)) break; if (cp->recno != RECNO_OOB) { ++cp->recno; break; } /* FALLTHROUGH */ case DB_FIRST: flags = DB_NEXT; cp->recno = 1; break; case DB_PREV_NODUP: /* * Recno databases don't have duplicates, set flags to DB_PREV * and keep going. */ /* FALLTHROUGH */ case DB_PREV: flags = DB_PREV; if (cp->recno != RECNO_OOB) { if (cp->recno == 1) { ret = DB_NOTFOUND; goto err; } --cp->recno; break; } /* FALLTHROUGH */ case DB_LAST: flags = DB_PREV; if (((ret = __ram_update(dbc, DB_MAX_RECORDS, 0)) != 0) && ret != DB_NOTFOUND) goto err; if ((ret = __bam_nrecs(dbc, &cp->recno)) != 0) goto err; if (cp->recno == 0) { ret = DB_NOTFOUND; goto err; } break; case DB_GET_BOTHC: /* * If we're doing a join and these are offpage dups, * we want to keep searching forward from after the * current cursor position. Increment the recno by 1, * then proceed as for a DB_SET. * * Otherwise, we know there are no additional matching * data, as recnos don't have dups. return DB_NOTFOUND. */ if (F_ISSET(dbc, DBC_OPD)) { cp->recno++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -