#include "config.h"
#ifndef lint
static const char sccsid[] = "@(#)bt_cursor.c 10.81 (Sleepycat) 12/16/98";
#endif
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#endif
#include "db_int.h"
#include "db_page.h"
#include "btree.h"
#include "shqueue.h"
#include "db_shash.h"
#include "lock.h"
#include "lock_ext.h"
static int __bam_c_close __P((DBC *));
static int __bam_c_del __P((DBC *, u_int32_t));
static int __bam_c_destroy __P((DBC *));
static int __bam_c_first __P((DBC *, CURSOR *));
static int __bam_c_get __P((DBC *, DBT *, DBT *, u_int32_t));
static int __bam_c_getstack __P((DBC *, CURSOR *));
static int __bam_c_last __P((DBC *, CURSOR *));
static int __bam_c_next __P((DBC *, CURSOR *, int));
static int __bam_c_physdel __P((DBC *, CURSOR *, PAGE *));
static int __bam_c_prev __P((DBC *, CURSOR *));
static int __bam_c_put __P((DBC *, DBT *, DBT *, u_int32_t));
static void __bam_c_reset __P((CURSOR *));
static int __bam_c_rget __P((DBC *, DBT *, u_int32_t));
static int __bam_c_search __P((DBC *, CURSOR *, const DBT *, u_int32_t, int *));
static int __bam_dsearch __P((DBC *, CURSOR *, DBT *, u_int32_t *));
#undef DISCARD
#define DISCARD(dbc, cp) { \
if ((cp)->page != NULL) { \
(void)memp_fput((dbc)->dbp->mpf, (cp)->page, 0); \
(cp)->page = NULL; \
} \
if ((cp)->lock != LOCK_INVALID) { \
(void)__BT_TLPUT((dbc), (cp)->lock); \
(cp)->lock = LOCK_INVALID; \
} \
}
#undef IS_CUR_DELETED
#define IS_CUR_DELETED(cp) \
(((cp)->dpgno == PGNO_INVALID && \
B_DISSET(GET_BKEYDATA((cp)->page, \
(cp)->indx + O_INDX)->type)) || \
((cp)->dpgno != PGNO_INVALID && \
B_DISSET(GET_BKEYDATA((cp)->page, (cp)->dindx)->type)))
#undef IS_DELETED
#define IS_DELETED(cp, indx) \
(((cp)->dpgno == PGNO_INVALID && \
B_DISSET(GET_BKEYDATA((cp)->page, (indx) + O_INDX)->type)) || \
((cp)->dpgno != PGNO_INVALID && \
B_DISSET(GET_BKEYDATA((cp)->page, (indx))->type)))
#undef POSSIBLE_DUPLICATE
#define POSSIBLE_DUPLICATE(cursor, saved_copy) \
((cursor)->pgno == (saved_copy).pgno && \
((cursor)->indx == (saved_copy).indx || \
((cursor)->dpgno == PGNO_INVALID && \
(saved_copy).dpgno == PGNO_INVALID && \
(cursor)->page->inp[(cursor)->indx] == \
(cursor)->page->inp[(saved_copy).indx])))
static void
__bam_c_reset(cp)
CURSOR *cp;
{
cp->sp = cp->csp = cp->stack;
cp->esp = cp->stack + sizeof(cp->stack) / sizeof(cp->stack[0]);
cp->page = NULL;
cp->pgno = PGNO_INVALID;
cp->indx = 0;
cp->dpgno = PGNO_INVALID;
cp->dindx = 0;
cp->lock = LOCK_INVALID;
cp->mode = DB_LOCK_NG;
cp->recno = RECNO_OOB;
cp->flags = 0;
}
int
__bam_c_init(dbc)
DBC *dbc;
{
DB *dbp;
CURSOR *cp;
int ret;
if ((ret = __os_calloc(1, sizeof(CURSOR), &cp)) != 0)
return (ret);
dbp = dbc->dbp;
cp->dbc = dbc;
if (dbp->type == DB_RECNO || F_ISSET(dbp, DB_BT_RECNUM)) {
if ((ret = __os_malloc(sizeof(db_recno_t),
NULL, &dbc->rkey.data)) != 0) {
__os_free(cp, sizeof(CURSOR));
return (ret);
}
dbc->rkey.ulen = sizeof(db_recno_t);
}
dbc->internal = cp;
if (dbp->type == DB_BTREE) {
dbc->c_am_close = __bam_c_close;
dbc->c_am_destroy = __bam_c_destroy;
dbc->c_del = __bam_c_del;
dbc->c_get = __bam_c_get;
dbc->c_put = __bam_c_put;
} else {
dbc->c_am_close = __bam_c_close;
dbc->c_am_destroy = __bam_c_destroy;
dbc->c_del = __ram_c_del;
dbc->c_get = __ram_c_get;
dbc->c_put = __ram_c_put;
}
__bam_c_reset(cp);
return (0);
}
static int
__bam_c_close(dbc)
DBC *dbc;
{
CURSOR *cp;
DB *dbp;
int ret;
dbp = dbc->dbp;
cp = dbc->internal;
ret = 0;
if (dbp->type == DB_BTREE && F_ISSET(cp, C_DELETED))
ret = __bam_c_physdel(dbc, cp, NULL);
if (cp->lock != LOCK_INVALID) {
(void)__BT_TLPUT(dbc, cp->lock);
cp->lock = LOCK_INVALID;
}
#ifdef DIAGNOSTIC
if (cp->csp != cp->stack)
__db_err(dbp->dbenv, "btree cursor close: stack not empty");
#endif
__bam_c_reset(cp);
return (ret);
}
static int
__bam_c_destroy(dbc)
DBC *dbc;
{
__os_free(dbc->internal, sizeof(CURSOR));
return (0);
}
static int
__bam_c_del(dbc, flags)
DBC *dbc;
u_int32_t flags;
{
CURSOR *cp;
DB *dbp;
DB_LOCK lock;
PAGE *h;
db_pgno_t pgno;
db_indx_t indx;
int ret;
dbp = dbc->dbp;
cp = dbc->internal;
h = NULL;
DB_PANIC_CHECK(dbp);
if ((ret = __db_cdelchk(dbp, flags,
F_ISSET(dbp, DB_AM_RDONLY), cp->pgno != PGNO_INVALID)) != 0)
return (ret);
if (F_ISSET(dbp, DB_AM_CDB))
if (!F_ISSET(dbc, DBC_RMW | DBC_WRITER))
return (EINVAL);
DEBUG_LWRITE(dbc, dbc->txn, "bam_c_del", NULL, NULL, flags);
if (F_ISSET(cp, C_DELETED))
return (DB_KEYEMPTY);
if (F_ISSET(dbp, DB_AM_LOCKING) && cp->mode != DB_LOCK_WRITE) {
if ((ret = __bam_lget(dbc,
0, cp->pgno, DB_LOCK_WRITE, &lock)) != 0)
goto err;
(void)__BT_TLPUT(dbc, cp->lock);
cp->lock = lock;
cp->mode = DB_LOCK_WRITE;
}
if (cp->dpgno == PGNO_INVALID) {
pgno = cp->pgno;
indx = cp->indx;
} else {
pgno = cp->dpgno;
indx = cp->dindx;
}
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
goto err;
if (DB_LOGGING(dbc) &&
(ret = __bam_cdel_log(dbp->dbenv->lg_info, dbc->txn, &LSN(h),
0, dbp->log_fileid, PGNO(h), &LSN(h), indx)) != 0) {
(void)memp_fput(dbp->mpf, h, 0);
goto err;
}
if (cp->dpgno == PGNO_INVALID)
B_DSET(GET_BKEYDATA(h, indx + O_INDX)->type);
else
B_DSET(GET_BKEYDATA(h, indx)->type);
(void)__bam_ca_delete(dbp, pgno, indx, 1);
ret = memp_fput(dbp->mpf, h, DB_MPOOL_DIRTY);
h = NULL;
if (F_ISSET(dbp, DB_BT_RECNUM)) {
if ((ret = __bam_c_getstack(dbc, cp)) != 0)
goto err;
if ((ret = __bam_adjust(dbc, -1)) != 0)
goto err;
(void)__bam_stkrel(dbc, 0);
}
err: if (h != NULL)
(void)memp_fput(dbp->mpf, h, 0);
return (ret);
}
static int
__bam_c_get(dbc, key, data, flags)
DBC *dbc;
DBT *key, *data;
u_int32_t flags;
{
CURSOR *cp, copy, start;
DB *dbp;
PAGE *h;
int exact, ret, tmp_rmw;
dbp = dbc->dbp;
cp = dbc->internal;
DB_PANIC_CHECK(dbp);
if ((ret = __db_cgetchk(dbp,
key, data, flags, cp->pgno != PGNO_INVALID)) != 0)
return (ret);
tmp_rmw = 0;
if (LF_ISSET(DB_RMW)) {
if (!F_ISSET(dbp, DB_AM_CDB)) {
tmp_rmw = 1;
F_SET(dbc, DBC_RMW);
}
LF_CLR(DB_RMW);
}
DEBUG_LREAD(dbc, dbc->txn, "bam_c_get",
flags == DB_SET || flags == DB_SET_RANGE ? key : NULL, NULL, flags);
if (flags == DB_GET_RECNO) {
ret = __bam_c_rget(dbc, data, flags);
if (tmp_rmw)
F_CLR(dbc, DBC_RMW);
return (ret);
}
cp->page = NULL;
copy = *cp;
cp->lock = LOCK_INVALID;
switch (flags) {
case DB_CURRENT:
if (F_ISSET(cp, C_DELETED)) {
ret = DB_KEYEMPTY;
goto err;
}
if ((ret = __bam_lget(dbc,
0, cp->pgno, DB_LOCK_READ, &cp->lock)) == 0)
ret = memp_fget(dbp->mpf,
cp->dpgno == PGNO_INVALID ? &cp->pgno : &cp->dpgno,
0, &cp->page);
if (ret != 0)
goto err;
break;
case DB_NEXT_DUP:
if (cp->pgno == PGNO_INVALID) {
ret = EINVAL;
goto err;
}
if ((ret = __bam_c_next(dbc, cp, 1)) != 0)
goto err;
if (!POSSIBLE_DUPLICATE(cp, copy)) {
ret = DB_NOTFOUND;
goto err;
}
break;
case DB_NEXT:
if (cp->pgno != PGNO_INVALID) {
if ((ret = __bam_c_next(dbc, cp, 1)) != 0)
goto err;
break;
}
case DB_FIRST:
if ((ret = __bam_c_first(dbc, cp)) != 0)
goto err;
break;
case DB_PREV:
if (cp->pgno != PGNO_INVALID) {
if ((ret = __bam_c_prev(dbc, cp)) != 0)
goto err;
break;
}
case DB_LAST:
if ((ret = __bam_c_last(dbc, cp)) != 0)
goto err;
break;
case DB_SET:
if ((ret = __bam_c_search(dbc, cp, key, flags, &exact)) != 0)
goto err;
start = *cp;
if ((ret = __bam_dup(dbc, cp, cp->indx, 0)) != 0)
goto err;
if (cp->dpgno != PGNO_INVALID && IS_CUR_DELETED(cp)) {
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
goto err;
if (!POSSIBLE_DUPLICATE(cp, start)) {
ret = DB_NOTFOUND;
goto err;
}
}
break;
case DB_SET_RECNO:
if ((ret = __bam_c_search(dbc, cp, key, flags, &exact)) != 0)
goto err;
break;
case DB_GET_BOTH:
if (F_ISSET(dbc, DBC_CONTINUE | DBC_KEYSET)) {
if ((ret = memp_fget(dbp->mpf,
cp->dpgno == PGNO_INVALID ? &cp->pgno : &cp->dpgno,
0, &cp->page)) != 0)
goto err;
if (F_ISSET(dbc, DBC_CONTINUE) &&
(ret = __bam_c_next(dbc, cp, 1)) != 0)
goto err;
} else {
if ((ret =
__bam_c_search(dbc, cp, key, flags, &exact)) != 0)
goto err;
if ((ret = __bam_dup(dbc, cp, cp->indx, 0)) != 0)
goto err;
}
if ((ret = __bam_dsearch(dbc, cp, data, NULL)) != 0)
goto err;
if (IS_CUR_DELETED(cp)) {
ret = DB_NOTFOUND;
goto err;
}
break;
case DB_SET_RANGE:
if ((ret = __bam_c_search(dbc, cp, key, flags, &exact)) != 0)
goto err;
if (cp->indx == NUM_ENT(cp->page) &&
(ret = __bam_c_next(dbc, cp, 0)) != 0)
goto err;
if ((ret = __bam_dup(dbc, cp, cp->indx, 0)) != 0)
goto err;
if (IS_CUR_DELETED(cp) && (ret = __bam_c_next(dbc, cp, 0)) != 0)
goto err;
break;
}
if (flags != DB_SET) {
if (cp->dpgno != PGNO_INVALID) {
if ((ret = memp_fget(dbp->mpf, &cp->pgno, 0, &h)) != 0)
goto err;
} else
h = cp->page;
ret = __db_ret(dbp,
h, cp->indx, key, &dbc->rkey.data, &dbc->rkey.ulen);
if (cp->dpgno != PGNO_INVALID)
(void)memp_fput(dbp->mpf, h, 0);
if (ret)
goto err;
}
if ((ret = __db_ret(dbp, cp->page,
cp->dpgno == PGNO_INVALID ? cp->indx + O_INDX : cp->dindx,
data, &dbc->rdata.data, &dbc->rdata.ulen)) != 0)
goto err;
if (F_ISSET(©, C_DELETED)) {
F_CLR(©, C_DELETED);
if ((ret = __bam_c_physdel(dbc, ©, cp->page)) != 0)
goto err;
}
F_CLR(cp, C_DELETED);
if (copy.lock != LOCK_INVALID)
(void)__BT_TLPUT(dbc, copy.lock);
if ((ret = memp_fput(dbp->mpf, cp->page, 0)) != 0)
goto err;
if (0) {
err: if (cp->page != NULL)
(void)memp_fput(dbp->mpf, cp->page, 0);
if (cp->lock != LOCK_INVALID)
(void)__BT_TLPUT(dbc, cp->lock);
*cp = copy;
}
if (tmp_rmw)
F_CLR(dbc, DBC_RMW);
return (ret);
}
static int
__bam_dsearch(dbc, cp, data, iflagp)
DBC *dbc;
CURSOR *cp;
DBT *data;
u_int32_t *iflagp;
{
DB *dbp;
CURSOR copy, last;
int cmp, ret;
dbp = dbc->dbp;
if (cp->dpgno != PGNO_INVALID) {
if ((ret = __db_dsearch(dbc, iflagp != NULL,
data, cp->dpgno, &cp->dindx, &cp->page, &cmp)) != 0)
return (ret);
cp->dpgno = cp->page->pgno;
if (iflagp == NULL) {
if (cmp != 0)
return (DB_NOTFOUND);
return (0);
}
*iflagp = DB_BEFORE;
return (0);
}
copy = *cp;
for (;;) {
last = *cp;
if ((cmp = __bam_cmp(dbp, data, cp->page, cp->indx + O_INDX,
dbp->dup_compare == NULL ?
__bam_defcmp : dbp->dup_compare)) == 0) {
if (iflagp != NULL)
*iflagp = DB_AFTER;
return (0);
}
if (dbp->dup_compare != NULL && cmp < 0) {
if (iflagp == NULL)
return (DB_NOTFOUND);
*iflagp = DB_BEFORE;
return (0);
}
if ((cp->indx += P_INDX) >= NUM_ENT(cp->page)) {
if (iflagp == NULL)
return (DB_NOTFOUND);
goto use_last;
}
if (!POSSIBLE_DUPLICATE(cp, copy)) {
if (iflagp == NULL)
return (DB_NOTFOUND);
use_last: *cp = last;
*iflagp = DB_AFTER;
return (0);
}
}
}
static int
__bam_c_rget(dbc, data, flags)
DBC *dbc;
DBT *data;
u_int32_t flags;
{
CURSOR *cp;
DB *dbp;
DBT dbt;
db_recno_t recno;
int exact, ret;
COMPQUIET(flags, 0);
dbp = dbc->dbp;
cp = dbc->internal;
if ((ret = memp_fget(dbp->mpf, &cp->pgno, 0, &cp->page)) != 0)
return (ret);
memset(&dbt, 0, sizeof(DBT));
dbt.flags = DB_DBT_MALLOC | DB_DBT_INTERNAL;
if ((ret = __db_ret(dbp, cp->page, cp->indx, &dbt, NULL, NULL)) != 0)
goto err;
exact = 1;
if ((ret = __bam_search(dbc, &dbt,
F_ISSET(dbc, DBC_RMW) ? S_FIND_WR : S_FIND,
1, &recno, &exact)) != 0)
goto err;
ret = __db_retcopy(data, &recno, sizeof(recno),
&dbc->rdata.data, &dbc->rdata.ulen, dbp->db_malloc);
__bam_stkrel(dbc, 0);
err: (void)memp_fput(dbp->mpf, cp->page, 0);
__os_free(dbt.data, dbt.size);
return (ret);
}
static int
__bam_c_put(dbc, key, data, flags)
DBC *dbc;
DBT *key, *data;
u_int32_t flags;
{
CURSOR *cp, copy;
DB *dbp;
DBT dbt;
db_indx_t indx;
db_pgno_t pgno;
u_int32_t iiflags, iiop;
int exact, needkey, ret, stack;
void *arg;
dbp = dbc->dbp;
cp = dbc->internal;
DB_PANIC_CHECK(dbp);
DEBUG_LWRITE(dbc, dbc->txn, "bam_c_put",
flags == DB_KEYFIRST || flags == DB_KEYLAST ? key : NULL,
data, flags);
if ((ret = __db_cputchk(dbp, key, data, flags,
F_ISSET(dbp, DB_AM_RDONLY), cp->pgno != PGNO_INVALID)) != 0)
return (ret);
if (F_ISSET(dbp, DB_AM_CDB)) {
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);
}
if (0) {
split:
if (needkey) {
memset(&dbt, 0, sizeof(DBT));
if ((ret = __db_ret(dbp, cp->page, indx,
&dbt, &dbc->rkey.data, &dbc->rkey.ulen)) != 0)
goto err;
arg = &dbt;
} else
arg = key;
if (stack) {
(void)__bam_stkrel(dbc, 1);
stack = 0;
} else
DISCARD(dbc, cp);
*cp = copy;
if ((ret = __bam_split(dbc, arg)) != 0)
goto err;
}
cp->page = NULL;
copy = *cp;
cp->lock = LOCK_INVALID;
iiflags = needkey = ret = stack = 0;
switch (flags) {
case DB_AFTER:
case DB_BEFORE:
case DB_CURRENT:
needkey = 1;
if (cp->dpgno == PGNO_INVALID) {
pgno = cp->pgno;
indx = cp->indx;
} else {
pgno = cp->dpgno;
indx = cp->dindx;
}
if (F_ISSET(dbp, DB_BT_RECNUM) &&
(flags != DB_CURRENT || F_ISSET(cp, C_DELETED))) {
if ((ret = __bam_c_getstack(dbc, cp)) != 0)
goto err;
cp->page = cp->csp->page;
stack = 1;
iiflags = BI_DOINCR;
} else {
if ((ret = __bam_lget(dbc,
0, cp->pgno, DB_LOCK_WRITE, &cp->lock)) == 0)
ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page);
if (ret != 0)
goto err;
iiflags = 0;
}
if (flags == DB_CURRENT && dbp->dup_compare != NULL)
if (__bam_cmp(dbp, data,
cp->page, indx, dbp->dup_compare) != 0) {
ret = EINVAL;
goto err;
}
iiop = flags;
break;
case DB_KEYFIRST:
case DB_KEYLAST:
if ((ret = __bam_c_search(dbc, cp, key,
flags == DB_KEYFIRST || dbp->dup_compare != NULL ?
DB_KEYFIRST : DB_KEYLAST, &exact)) != 0)
goto err;
stack = 1;
if (exact) {
if (F_ISSET(dbp, DB_AM_DUP)) {
if ((ret = __bam_dup(dbc, cp, cp->indx,
dbp->dup_compare == NULL &&
flags != DB_KEYFIRST)) != 0)
goto err;
if (dbp->dup_compare == NULL)
iiop = flags == DB_KEYFIRST ?
DB_BEFORE : DB_AFTER;
else
if ((ret = __bam_dsearch(dbc,
cp, data, &iiop)) != 0)
goto err;
} else
iiop = DB_CURRENT;
iiflags = 0;
} else {
iiop = DB_BEFORE;
iiflags = BI_NEWKEY;
}
if (cp->dpgno == PGNO_INVALID) {
pgno = cp->pgno;
indx = cp->indx;
} else {
pgno = cp->dpgno;
indx = cp->dindx;
}
break;
}
ret = __bam_iitem(dbc, &cp->page, &indx, key, data, iiop, iiflags);
if (ret == DB_NEEDSPLIT)
goto split;
if (ret != 0)
goto err;
if (iiop == DB_CURRENT) {
(void)__bam_ca_delete(dbp, pgno, indx, 0);
if (cp->pgno == copy.pgno && cp->indx == copy.indx &&
cp->dpgno == copy.dpgno && cp->dindx == copy.dindx)
F_CLR(©, C_DELETED);
}
if (cp->dpgno == PGNO_INVALID)
cp->indx = indx;
else
cp->dindx = indx;
if (F_ISSET(©, C_DELETED)) {
F_CLR(©, C_DELETED);
if ((ret = __bam_c_physdel(dbc, ©, cp->page)) != 0)
goto err;
}
F_CLR(cp, C_DELETED);
if (copy.lock != LOCK_INVALID)
(void)__BT_TLPUT(dbc, copy.lock);
if (stack && BT_STK_POP(cp) != NULL)
(void)__bam_stkrel(dbc, 0);
if ((ret = memp_fput(dbp->mpf, cp->page, 0)) != 0)
goto err;
if (0) {
err:
if (stack)
(void)__bam_stkrel(dbc, 0);
else
DISCARD(dbc, cp);
*cp = copy;
}
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
__bam_c_first(dbc, cp)
DBC *dbc;
CURSOR *cp;
{
DB *dbp;
db_pgno_t pgno;
int ret;
dbp = dbc->dbp;
for (pgno = PGNO_ROOT;;) {
if ((ret =
__bam_lget(dbc, 0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
if (ISLEAF(cp->page))
break;
pgno = GET_BINTERNAL(cp->page, 0)->pgno;
DISCARD(dbc, cp);
}
cp->pgno = cp->page->pgno;
cp->indx = 0;
cp->dpgno = PGNO_INVALID;
if ((ret = __bam_dup(dbc, cp, cp->indx, 0)) != 0)
return (ret);
if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
return (ret);
return (0);
}
static int
__bam_c_last(dbc, cp)
DBC *dbc;
CURSOR *cp;
{
DB *dbp;
db_pgno_t pgno;
int ret;
dbp = dbc->dbp;
for (pgno = PGNO_ROOT;;) {
if ((ret =
__bam_lget(dbc, 0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
if (ISLEAF(cp->page))
break;
pgno =
GET_BINTERNAL(cp->page, NUM_ENT(cp->page) - O_INDX)->pgno;
DISCARD(dbc, cp);
}
cp->pgno = cp->page->pgno;
cp->indx = NUM_ENT(cp->page) == 0 ? 0 : NUM_ENT(cp->page) - P_INDX;
cp->dpgno = PGNO_INVALID;
if ((ret = __bam_dup(dbc, cp, cp->indx, 1)) != 0)
return (ret);
if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
if ((ret = __bam_c_prev(dbc, cp)) != 0)
return (ret);
return (0);
}
static int
__bam_c_next(dbc, cp, initial_move)
DBC *dbc;
CURSOR *cp;
int initial_move;
{
DB *dbp;
db_indx_t adjust, indx;
db_pgno_t pgno;
int ret;
dbp = dbc->dbp;
if (cp->dpgno == PGNO_INVALID) {
adjust = dbp->type == DB_BTREE ? P_INDX : O_INDX;
pgno = cp->pgno;
indx = cp->indx;
} else {
adjust = O_INDX;
pgno = cp->dpgno;
indx = cp->dindx;
}
if (cp->page == NULL) {
if ((ret =
__bam_lget(dbc, 0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
}
if (initial_move)
indx += adjust;
for (;;) {
if (indx >= NUM_ENT(cp->page)) {
pgno = cp->page->next_pgno;
if (pgno == PGNO_INVALID) {
if (cp->dpgno == PGNO_INVALID)
return (DB_NOTFOUND);
cp->dpgno = PGNO_INVALID;
adjust = P_INDX;
pgno = cp->pgno;
indx = cp->indx + P_INDX;
} else
indx = 0;
DISCARD(dbc, cp);
if ((ret = __bam_lget(dbc,
0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret =
memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
continue;
}
if (IS_DELETED(cp, indx)) {
indx += adjust;
continue;
}
if (cp->dpgno == PGNO_INVALID) {
cp->pgno = cp->page->pgno;
cp->indx = indx;
if ((ret = __bam_dup(dbc, cp, indx, 0)) != 0)
return (ret);
if (cp->dpgno != PGNO_INVALID) {
indx = cp->dindx;
adjust = O_INDX;
continue;
}
} else {
cp->dpgno = cp->page->pgno;
cp->dindx = indx;
}
break;
}
return (0);
}
static int
__bam_c_prev(dbc, cp)
DBC *dbc;
CURSOR *cp;
{
DB *dbp;
db_indx_t indx, adjust;
db_pgno_t pgno;
int ret, set_indx;
dbp = dbc->dbp;
if (cp->dpgno == PGNO_INVALID) {
adjust = dbp->type == DB_BTREE ? P_INDX : O_INDX;
pgno = cp->pgno;
indx = cp->indx;
} else {
adjust = O_INDX;
pgno = cp->dpgno;
indx = cp->dindx;
}
if (cp->page == NULL) {
if ((ret =
__bam_lget(dbc, 0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
}
for (;;) {
if (indx == 0) {
pgno = cp->page->prev_pgno;
if (pgno == PGNO_INVALID) {
if (cp->dpgno == PGNO_INVALID)
return (DB_NOTFOUND);
cp->dpgno = PGNO_INVALID;
adjust = P_INDX;
pgno = cp->pgno;
indx = cp->indx;
set_indx = 0;
} else
set_indx = 1;
DISCARD(dbc, cp);
if ((ret = __bam_lget(dbc,
0, pgno, DB_LOCK_READ, &cp->lock)) != 0)
return (ret);
if ((ret =
memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
if (set_indx)
indx = NUM_ENT(cp->page);
if (indx == 0)
continue;
}
indx -= adjust;
if (IS_DELETED(cp, indx))
continue;
if (cp->dpgno == PGNO_INVALID) {
cp->pgno = cp->page->pgno;
cp->indx = indx;
if ((ret = __bam_dup(dbc, cp, indx, 1)) != 0)
return (ret);
if (cp->dpgno != PGNO_INVALID) {
indx = cp->dindx + O_INDX;
adjust = O_INDX;
continue;
}
} else {
cp->dpgno = cp->page->pgno;
cp->dindx = indx;
}
break;
}
return (0);
}
static int
__bam_c_search(dbc, cp, key, flags, exactp)
DBC *dbc;
CURSOR *cp;
const DBT *key;
u_int32_t flags;
int *exactp;
{
BTREE *t;
DB *dbp;
DB_LOCK lock;
PAGE *h;
db_recno_t recno;
db_indx_t indx;
u_int32_t sflags;
int cmp, needexact, ret;
dbp = dbc->dbp;
t = dbp->internal;
switch (flags) {
case DB_SET_RECNO:
if ((ret = __ram_getno(dbc, key, &recno, 0)) != 0)
return (ret);
sflags = F_ISSET(dbc, DBC_RMW) ? S_FIND_WR : S_FIND;
needexact = *exactp = 1;
ret = __bam_rsearch(dbc, &recno, sflags, 1, exactp);
break;
case DB_SET:
case DB_GET_BOTH:
sflags = F_ISSET(dbc, DBC_RMW) ? S_FIND_WR : S_FIND;
needexact = *exactp = 1;
goto search;
case DB_SET_RANGE:
sflags = F_ISSET(dbc, DBC_RMW) ? S_FIND_WR : S_FIND;
needexact = *exactp = 0;
goto search;
case DB_KEYFIRST:
sflags = S_KEYFIRST;
goto fast_search;
case DB_KEYLAST:
sflags = S_KEYLAST;
fast_search: needexact = *exactp = 0;
h = NULL;
lock = LOCK_INVALID;
if (F_ISSET(dbp, DB_BT_RECNUM))
goto search;
if (t->bt_lpgno == PGNO_INVALID)
goto search;
if (__bam_lget(dbc, 0, t->bt_lpgno, DB_LOCK_WRITE, &lock))
goto fast_miss;
if (memp_fget(dbp->mpf, &t->bt_lpgno, 0, &h))
goto fast_miss;
if (TYPE(h) != P_LBTREE)
goto fast_miss;
if (NUM_ENT(h) == 0)
goto fast_miss;
if (h->next_pgno == PGNO_INVALID) {
indx = NUM_ENT(h) - P_INDX;
if ((cmp =
__bam_cmp(dbp, key, h, indx, t->bt_compare)) < 0)
goto try_begin;
if (cmp > 0) {
indx += P_INDX;
goto fast_hit;
}
if (flags == DB_KEYLAST)
goto fast_hit;
for (;
indx > 0 && h->inp[indx - P_INDX] == h->inp[indx];
indx -= P_INDX)
;
goto fast_hit;
}
try_begin: if (h->prev_pgno == PGNO_INVALID) {
indx = 0;
if ((cmp =
__bam_cmp(dbp, key, h, indx, t->bt_compare)) > 0)
goto fast_miss;
if (cmp < 0)
goto fast_hit;
if (flags == DB_KEYFIRST)
goto fast_hit;
for (;
indx < (db_indx_t)(NUM_ENT(h) - P_INDX) &&
h->inp[indx] == h->inp[indx + P_INDX];
indx += P_INDX)
;
goto fast_hit;
}
goto fast_miss;
fast_hit:
*exactp = cmp == 0;
BT_STK_CLR(cp);
BT_STK_ENTER(cp, h, indx, lock, ret);
break;
fast_miss: if (h != NULL)
(void)memp_fput(dbp->mpf, h, 0);
if (lock != LOCK_INVALID)
(void)__BT_LPUT(dbc, lock);
search: ret = __bam_search(dbc, key, sflags, 1, NULL, exactp);
break;
default:
abort();
}
if (ret != 0)
return (ret);
cp->page = cp->csp->page;
cp->pgno = cp->csp->page->pgno;
cp->indx = cp->csp->indx;
cp->lock = cp->csp->lock;
cp->dpgno = PGNO_INVALID;
if (flags == DB_KEYFIRST || flags == DB_KEYLAST)
t->bt_lpgno =
((cp->page->next_pgno == PGNO_INVALID &&
cp->indx >= NUM_ENT(cp->page)) ||
(cp->page->prev_pgno == PGNO_INVALID && cp->indx == 0)) ?
cp->pgno : PGNO_INVALID;
if (needexact && *exactp == 0)
return (DB_NOTFOUND);
return (0);
}
int
__bam_dup(dbc, cp, indx, last_dup)
DBC *dbc;
CURSOR *cp;
u_int32_t indx;
int last_dup;
{
BOVERFLOW *bo;
DB *dbp;
db_pgno_t pgno;
int ret;
dbp = dbc->dbp;
bo = GET_BOVERFLOW(cp->page, indx + O_INDX);
if (B_TYPE(bo->type) != B_DUPLICATE)
return (0);
pgno = bo->pgno;
if ((ret = memp_fput(dbp->mpf, cp->page, 0)) != 0)
return (ret);
cp->page = NULL;
if (last_dup) {
if ((ret = __db_dend(dbc, pgno, &cp->page)) != 0)
return (ret);
indx = NUM_ENT(cp->page) - O_INDX;
} else {
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &cp->page)) != 0)
return (ret);
indx = 0;
}
cp->dpgno = cp->page->pgno;
cp->dindx = indx;
return (0);
}
static int
__bam_c_physdel(dbc, cp, h)
DBC *dbc;
CURSOR *cp;
PAGE *h;
{
enum { DELETE_ITEM, DELETE_PAGE, NOTHING_FURTHER } cmd;
BOVERFLOW bo;
DB *dbp;
DBT dbt;
DB_LOCK lock;
db_indx_t indx;
db_pgno_t pgno, next_pgno, prev_pgno;
int delete_page, local_page, ret;
dbp = dbc->dbp;
delete_page = ret = 0;
if (cp->dpgno == PGNO_INVALID) {
pgno = cp->pgno;
indx = cp->indx;
} else {
pgno = cp->dpgno;
indx = cp->dindx;
}
if (__bam_ca_delete(dbp, pgno, indx, 1) > 0)
return (0);
if (F_ISSET(dbp, DB_AM_CDB) && 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);
if ((h == NULL || h->pgno != pgno)) {
if ((ret = __bam_lget(dbc, 0, pgno, DB_LOCK_WRITE, &lock)) != 0)
return (ret);
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
return (ret);
local_page = 1;
} else
local_page = 0;
if (TYPE(h) == P_DUPLICATE) {
pgno = PGNO(h);
prev_pgno = PREV_PGNO(h);
next_pgno = NEXT_PGNO(h);
if (NUM_ENT(h) == 1 &&
prev_pgno == PGNO_INVALID && next_pgno == PGNO_INVALID)
cmd = DELETE_PAGE;
else {
cmd = DELETE_ITEM;
if ((ret = __db_drem(dbc, &h, indx, __bam_free)) != 0)
goto err;
if ((h != NULL && pgno == h->pgno) ||
prev_pgno != PGNO_INVALID)
cmd = NOTHING_FURTHER;
}
if (local_page) {
if (h != NULL)
(void)memp_fput(dbp->mpf, h, 0);
(void)__BT_TLPUT(dbc, lock);
local_page = 0;
}
if (cmd == NOTHING_FURTHER)
goto done;
if ((ret =
__bam_lget(dbc, 0, cp->pgno, DB_LOCK_WRITE, &lock)) != 0)
goto err;
if ((ret = memp_fget(dbp->mpf, &cp->pgno, 0, &h)) != 0) {
(void)__BT_TLPUT(dbc, lock);
goto err;
}
local_page = 1;
indx = cp->indx;
if (cmd == DELETE_PAGE)
goto btd;
indx += O_INDX;
bo = *GET_BOVERFLOW(h, indx);
(void)__db_ditem(dbc, h, indx, BOVERFLOW_SIZE);
bo.pgno = next_pgno;
memset(&dbt, 0, sizeof(dbt));
dbt.data = &bo;
dbt.size = BOVERFLOW_SIZE;
(void)__db_pitem(dbc, h, indx, BOVERFLOW_SIZE, &dbt, NULL);
(void)memp_fset(dbp->mpf, h, DB_MPOOL_DIRTY);
goto done;
}
btd:
if (NUM_ENT(h) == 2 && h->pgno != PGNO_ROOT) {
memset(&dbt, 0, sizeof(DBT));
dbt.flags = DB_DBT_MALLOC | DB_DBT_INTERNAL;
if ((ret = __db_ret(dbp, h, 0, &dbt, NULL, NULL)) != 0)
goto err;
delete_page = 1;
}
if ((ret = __bam_ditem(dbc, h, indx)) != 0)
goto err;
if ((ret = __bam_ditem(dbc, h, indx)) != 0)
goto err;
if (local_page) {
(void)memp_fput(dbp->mpf, h, 0);
(void)__BT_TLPUT(dbc, lock);
local_page = 0;
}
if (delete_page)
ret = __bam_dpage(dbc, &dbt);
err:
done: if (delete_page)
__os_free(dbt.data, dbt.size);
if (local_page) {
if (h != NULL)
(void)memp_fput(dbp->mpf, h, 0);
(void)__BT_TLPUT(dbc, lock);
}
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
__bam_c_getstack(dbc, cp)
DBC *dbc;
CURSOR *cp;
{
DB *dbp;
DBT dbt;
PAGE *h;
db_pgno_t pgno;
int exact, ret;
dbp = dbc->dbp;
h = NULL;
memset(&dbt, 0, sizeof(DBT));
ret = 0;
pgno = cp->pgno;
if ((ret = memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
return (ret);
dbt.flags = DB_DBT_MALLOC | DB_DBT_INTERNAL;
if ((ret = __db_ret(dbp, h, 0, &dbt, NULL, NULL)) != 0)
goto err;
exact = 0;
ret = __bam_search(dbc, &dbt, S_KEYFIRST, 1, NULL, &exact);
err: if (h != NULL)
(void)memp_fput(dbp->mpf, h, 0);
if (dbt.data != NULL)
__os_free(dbt.data, dbt.size);
return (ret);
}