#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/btree.h"
#include "dbinc/mp.h"
static int __bam_build
__P((DBC *, u_int32_t, DBT *, PAGE *, u_int32_t, u_int32_t));
static int __bam_dup_check __P((DBC *, u_int32_t,
PAGE *, u_int32_t, u_int32_t, db_indx_t *));
static int __bam_dup_convert __P((DBC *, PAGE *, u_int32_t, u_int32_t));
static int __bam_ovput
__P((DBC *, u_int32_t, db_pgno_t, PAGE *, u_int32_t, DBT *));
static u_int32_t
__bam_partsize __P((DB *, u_int32_t, DBT *, PAGE *, u_int32_t));
int
__bam_iitem(dbc, key, data, op, flags)
DBC *dbc;
DBT *key, *data;
u_int32_t op, flags;
{
BKEYDATA *bk, bk_tmp;
BTREE *t;
BTREE_CURSOR *cp;
DB *dbp;
DBT bk_hdr, tdbt;
DB_MPOOLFILE *mpf;
ENV *env;
PAGE *h;
db_indx_t cnt, indx;
u_int32_t data_size, have_bytes, need_bytes, needed, pages, pagespace;
int cmp, bigkey, bigdata, del, dupadjust;
int padrec, replace, ret, t_ret, was_deleted;
COMPQUIET(cnt, 0);
dbp = dbc->dbp;
env = dbp->env;
mpf = dbp->mpf;
cp = (BTREE_CURSOR *)dbc->internal;
t = dbp->bt_internal;
h = cp->page;
indx = cp->indx;
del = dupadjust = replace = was_deleted = 0;
if (F_ISSET(dbp, DB_AM_FIXEDLEN) &&
F_ISSET(data, DB_DBT_PARTIAL) && data->size != data->dlen)
return (__db_rec_repl(env, data->size, data->dlen));
data_size = F_ISSET(data, DB_DBT_PARTIAL) ?
__bam_partsize(dbp, op, data, h, indx) : data->size;
padrec = 0;
if (F_ISSET(dbp, DB_AM_FIXEDLEN)) {
if (data_size > t->re_len)
return (__db_rec_toobig(env, data_size, t->re_len));
if (!LF_ISSET(BI_DELETED) && data_size < t->re_len) {
padrec = 1;
data_size = t->re_len;
}
}
if (padrec || F_ISSET(data, DB_DBT_PARTIAL)) {
tdbt = *data;
if ((ret =
__bam_build(dbc, op, &tdbt, h, indx, data_size)) != 0)
return (ret);
data = &tdbt;
}
if (op == DB_CURRENT && dbp->dup_compare != NULL) {
if ((ret = __bam_cmp(dbp, dbc->thread_info, dbc->txn, data, h,
indx + (TYPE(h) == P_LBTREE ? O_INDX : 0),
dbp->dup_compare, &cmp)) != 0)
return (ret);
if (cmp != 0) {
__db_errx(env,
"Existing data sorts differently from put data");
return (EINVAL);
}
}
needed = 0;
bigdata = data_size > cp->ovflsize;
switch (op) {
case DB_KEYFIRST:
bigkey = key->size > cp->ovflsize;
if (bigkey)
needed += BOVERFLOW_PSIZE;
else
needed += BKEYDATA_PSIZE(key->size);
if (bigdata)
needed += BOVERFLOW_PSIZE;
else
needed += BKEYDATA_PSIZE(data_size);
break;
case DB_AFTER:
case DB_BEFORE:
case DB_CURRENT:
bigkey = 0;
if (op == DB_CURRENT) {
bk = GET_BKEYDATA(dbp, h,
indx + (TYPE(h) == P_LBTREE ? O_INDX : 0));
if (B_TYPE(bk->type) == B_KEYDATA)
have_bytes = BKEYDATA_PSIZE(bk->len);
else
have_bytes = BOVERFLOW_PSIZE;
need_bytes = 0;
} else {
have_bytes = 0;
need_bytes = sizeof(db_indx_t);
}
if (bigdata)
need_bytes += BOVERFLOW_PSIZE;
else
need_bytes += BKEYDATA_PSIZE(data_size);
if (have_bytes < need_bytes)
needed += need_bytes - have_bytes;
break;
default:
return (__db_unknown_flag(env, "DB->put", op));
}
if (P_FREESPACE(dbp, h) < needed)
return (DB_NEEDSPLIT);
if (F_ISSET(dbp, DB_AM_DUP) &&
TYPE(h) == P_LBTREE && op != DB_KEYFIRST &&
P_FREESPACE(dbp, h) - needed <= dbp->pgsize / 2 &&
__bam_dup_check(dbc, op, h, indx, needed, &cnt)) {
pages = 1;
dupadjust = 1;
} else
pages = 0;
if (dbc->txn == NULL && mpf->mfp->maxpgno != 0) {
pagespace = P_MAXSPACE(dbp, dbp->pgsize);
if (bigdata)
pages += ((data_size - 1) / pagespace) + 1;
if (bigkey)
pages += ((key->size - 1) / pagespace) + 1;
if (pages > (mpf->mfp->maxpgno - mpf->mfp->last_pgno))
return (__db_space_err(dbp));
}
if ((ret = __memp_dirty(mpf, &h,
dbc->thread_info, dbc->txn, dbc->priority, 0)) != 0)
return (ret);
if (cp->csp->page == cp->page)
cp->csp->page = h;
cp->page = h;
switch (op) {
case DB_KEYFIRST:
if (bigkey) {
if ((ret = __bam_ovput(dbc,
B_OVERFLOW, PGNO_INVALID, h, indx, key)) != 0)
return (ret);
} else
if ((ret = __db_pitem(dbc, h, indx,
BKEYDATA_SIZE(key->size), NULL, key)) != 0)
return (ret);
if ((ret = __bam_ca_di(dbc, PGNO(h), indx, 1)) != 0)
return (ret);
++indx;
break;
case DB_AFTER:
if (TYPE(h) == P_LBTREE) {
if ((ret =
__bam_adjindx(dbc, h, indx + P_INDX, indx, 1)) != 0)
return (ret);
if ((ret =
__bam_ca_di(dbc, PGNO(h), indx + P_INDX, 1)) != 0)
return (ret);
indx += 3;
cp->indx += 2;
} else {
++indx;
cp->indx += 1;
}
break;
case DB_BEFORE:
if (TYPE(h) == P_LBTREE) {
if ((ret = __bam_adjindx(dbc, h, indx, indx, 1)) != 0)
return (ret);
if ((ret = __bam_ca_di(dbc, PGNO(h), indx, 1)) != 0)
return (ret);
++indx;
}
break;
case DB_CURRENT:
if ((ret = __bam_ca_delete(dbp, PGNO(h), indx, 0, NULL)) != 0)
return (ret);
if (TYPE(h) == P_LBTREE)
++indx;
bk = GET_BKEYDATA(dbp, h, indx);
if (TYPE(h) == P_LBTREE || TYPE(h) == P_LDUP)
was_deleted = B_DISSET(bk->type);
if (bigdata || B_TYPE(bk->type) != B_KEYDATA) {
if ((ret = __bam_ditem(dbc, h, indx)) != 0)
return (ret);
del = 1;
break;
}
replace = 1;
break;
default:
return (__db_unknown_flag(env, "DB->put", op));
}
if (bigdata) {
DB_ASSERT(env, !LF_ISSET(BI_DELETED));
ret = __bam_ovput(dbc,
B_OVERFLOW, PGNO_INVALID, h, indx, data);
} else {
if (LF_ISSET(BI_DELETED)) {
B_TSET_DELETED(bk_tmp.type, B_KEYDATA);
bk_tmp.len = data->size;
bk_hdr.data = &bk_tmp;
bk_hdr.size = SSZA(BKEYDATA, data);
ret = __db_pitem(dbc, h, indx,
BKEYDATA_SIZE(data->size), &bk_hdr, data);
} else if (replace)
ret = __bam_ritem(dbc, h, indx, data);
else
ret = __db_pitem(dbc, h, indx,
BKEYDATA_SIZE(data->size), NULL, data);
}
if (ret != 0) {
if (del == 1 && (t_ret =
__bam_ca_di(dbc, PGNO(h), indx + 1, -1)) != 0) {
__db_err(env, t_ret,
"cursor adjustment after delete failed");
return (__env_panic(env, t_ret));
}
return (ret);
}
if (op != DB_CURRENT) {
if ((ret = __bam_ca_di(dbc, PGNO(h), indx, 1)) != 0)
return (ret);
cp->indx = TYPE(h) == P_LBTREE ? indx - O_INDX : indx;
}
if (F_ISSET(cp, C_RECNUM) && (op != DB_CURRENT || was_deleted))
if ((ret = __bam_adjust(dbc, 1)) != 0)
return (ret);
if (dupadjust &&
(ret = __bam_dup_convert(dbc, h, indx - O_INDX, cnt)) != 0)
return (ret);
if (dbc->dbtype == DB_RECNO)
t->re_modified = 1;
return (ret);
}
static u_int32_t
__bam_partsize(dbp, op, data, h, indx)
DB *dbp;
u_int32_t op, indx;
DBT *data;
PAGE *h;
{
BKEYDATA *bk;
u_int32_t nbytes;
if (op != DB_CURRENT)
return (data->doff + data->size);
bk = GET_BKEYDATA(dbp, h, indx + (TYPE(h) == P_LBTREE ? O_INDX : 0));
nbytes =
B_TYPE(bk->type) == B_OVERFLOW ? ((BOVERFLOW *)bk)->tlen : bk->len;
return (__db_partsize(nbytes, data));
}
static int
__bam_build(dbc, op, dbt, h, indx, nbytes)
DBC *dbc;
u_int32_t op, indx, nbytes;
DBT *dbt;
PAGE *h;
{
BKEYDATA *bk, tbk;
BOVERFLOW *bo;
BTREE *t;
DB *dbp;
DBT copy, *rdata;
u_int32_t len, tlen;
u_int8_t *p;
int ret;
COMPQUIET(bo, NULL);
dbp = dbc->dbp;
t = dbp->bt_internal;
rdata = &dbc->my_rdata;
if (rdata->ulen < nbytes) {
if ((ret = __os_realloc(dbp->env,
nbytes, &rdata->data)) != 0) {
rdata->ulen = 0;
rdata->data = NULL;
return (ret);
}
rdata->ulen = nbytes;
}
memset(rdata->data,
F_ISSET(dbp, DB_AM_FIXEDLEN) ? t->re_pad : 0, nbytes);
if (!F_ISSET(dbt, DB_DBT_PARTIAL) || op != DB_CURRENT) {
p = (u_int8_t *)rdata->data + dbt->doff;
tlen = dbt->doff;
goto user_copy;
}
if (indx < NUM_ENT(h)) {
bk = GET_BKEYDATA(dbp, h, indx + (TYPE(h) == P_LBTREE ?
O_INDX : 0));
bo = (BOVERFLOW *)bk;
} else {
bk = &tbk;
B_TSET(bk->type, B_KEYDATA);
bk->len = 0;
}
if (B_TYPE(bk->type) == B_OVERFLOW) {
memset(©, 0, sizeof(copy));
if ((ret = __db_goff(dbp, dbc->thread_info, dbc->txn, ©,
bo->tlen, bo->pgno, &rdata->data, &rdata->ulen)) != 0)
return (ret);
tlen = dbt->doff;
p = (u_int8_t *)rdata->data + dbt->doff;
if (bo->tlen > dbt->doff + dbt->dlen) {
len = bo->tlen - (dbt->doff + dbt->dlen);
if (dbt->dlen != dbt->size)
memmove(p + dbt->size, p + dbt->dlen, len);
tlen += len;
}
} else {
memcpy(rdata->data,
bk->data, dbt->doff > bk->len ? bk->len : dbt->doff);
tlen = dbt->doff;
p = (u_int8_t *)rdata->data + dbt->doff;
len = dbt->doff + dbt->dlen;
if (bk->len > len) {
memcpy(p + dbt->size, bk->data + len, bk->len - len);
tlen += bk->len - len;
}
}
user_copy:
memcpy(p, dbt->data, dbt->size);
tlen += dbt->size;
rdata->size = F_ISSET(dbp, DB_AM_FIXEDLEN) ? t->re_len : tlen;
rdata->dlen = 0;
rdata->doff = 0;
rdata->flags = 0;
*dbt = *rdata;
return (0);
}
int
__bam_ritem(dbc, h, indx, data)
DBC *dbc;
PAGE *h;
u_int32_t indx;
DBT *data;
{
BKEYDATA *bk;
DB *dbp;
DBT orig, repl;
db_indx_t cnt, lo, ln, min, off, prefix, suffix;
int32_t nbytes;
int ret;
db_indx_t *inp;
u_int8_t *p, *t;
dbp = dbc->dbp;
bk = GET_BKEYDATA(dbp, h, indx);
if (DBC_LOGGING(dbc)) {
min = data->size < bk->len ? data->size : bk->len;
for (prefix = 0,
p = bk->data, t = data->data;
prefix < min && *p == *t; ++prefix, ++p, ++t)
;
min -= prefix;
for (suffix = 0,
p = (u_int8_t *)bk->data + bk->len - 1,
t = (u_int8_t *)data->data + data->size - 1;
suffix < min && *p == *t; ++suffix, --p, --t)
;
orig.data = (u_int8_t *)bk->data + prefix;
orig.size = bk->len - (prefix + suffix);
repl.data = (u_int8_t *)data->data + prefix;
repl.size = data->size - (prefix + suffix);
if ((ret = __bam_repl_log(dbp, dbc->txn, &LSN(h), 0, PGNO(h),
&LSN(h), (u_int32_t)indx, (u_int32_t)B_DISSET(bk->type),
&orig, &repl, (u_int32_t)prefix, (u_int32_t)suffix)) != 0)
return (ret);
} else
LSN_NOT_LOGGED(LSN(h));
inp = P_INP(dbp, h);
p = (u_int8_t *)h + HOFFSET(h);
t = (u_int8_t *)bk;
lo = BKEYDATA_SIZE(bk->len);
ln = (db_indx_t)BKEYDATA_SIZE(data->size);
if (lo != ln) {
nbytes = lo - ln;
if (p == t)
inp[indx] += nbytes;
else {
memmove(p + nbytes, p, (size_t)(t - p));
off = inp[indx];
for (cnt = 0; cnt < NUM_ENT(h); ++cnt)
if (inp[cnt] <= off)
inp[cnt] += nbytes;
}
HOFFSET(h) += nbytes;
t += nbytes;
}
bk = (BKEYDATA *)t;
B_TSET(bk->type, B_KEYDATA);
bk->len = data->size;
memcpy(bk->data, data->data, data->size);
return (0);
}
static int
__bam_dup_check(dbc, op, h, indx, sz, cntp)
DBC *dbc;
u_int32_t op;
PAGE *h;
u_int32_t indx, sz;
db_indx_t *cntp;
{
BKEYDATA *bk;
DB *dbp;
db_indx_t cnt, first, *inp;
dbp = dbc->dbp;
inp = P_INP(dbp, h);
while (indx > 0 && inp[indx] == inp[indx - P_INDX])
indx -= P_INDX;
bk = GET_BKEYDATA(dbp, h, indx);
sz += B_TYPE(bk->type) == B_KEYDATA ?
BKEYDATA_PSIZE(bk->len) : BOVERFLOW_PSIZE;
first = indx;
cnt = op == DB_CURRENT ? 0 : 1;
for (first = indx;
indx < NUM_ENT(h) && inp[first] == inp[indx];
++cnt, indx += P_INDX) {
bk = GET_BKEYDATA(dbp, h, indx + O_INDX);
sz += B_TYPE(bk->type) == B_KEYDATA ?
BKEYDATA_PSIZE(bk->len) : BOVERFLOW_PSIZE;
}
if (cnt == 1)
return (0);
if (sz < dbp->pgsize / 4)
return (0);
*cntp = cnt;
return (1);
}
static int
__bam_dup_convert(dbc, h, indx, cnt)
DBC *dbc;
PAGE *h;
u_int32_t indx, cnt;
{
BKEYDATA *bk;
DB *dbp;
DBT hdr;
DB_MPOOLFILE *mpf;
PAGE *dp;
db_indx_t cpindx, dindx, first, *inp;
int ret, t_ret;
dbp = dbc->dbp;
mpf = dbp->mpf;
inp = P_INP(dbp, h);
while (indx > 0 && inp[indx] == inp[indx - P_INDX])
indx -= P_INDX;
if ((ret = __db_new(dbc,
dbp->dup_compare == NULL ? P_LRECNO : P_LDUP, &dp)) != 0)
return (ret);
P_INIT(dp, dbp->pgsize, dp->pgno,
PGNO_INVALID, PGNO_INVALID, LEAFLEVEL, TYPE(dp));
memset(&hdr, 0, sizeof(hdr));
first = indx;
dindx = indx;
cpindx = 0;
do {
if ((ret = __bam_ca_dup(dbc, first,
PGNO(h), indx, PGNO(dp), cpindx)) != 0)
goto err;
bk = GET_BKEYDATA(dbp, h, dindx + 1);
hdr.data = bk;
hdr.size = B_TYPE(bk->type) == B_KEYDATA ?
BKEYDATA_SIZE(bk->len) : BOVERFLOW_SIZE;
if (dbp->dup_compare == NULL && B_DISSET(bk->type)) {
if (B_TYPE(bk->type) == B_OVERFLOW &&
(ret = __db_doff(dbc,
(GET_BOVERFLOW(dbp, h, dindx + 1))->pgno)) != 0)
goto err;
} else {
if ((ret = __db_pitem(
dbc, dp, cpindx, hdr.size, &hdr, NULL)) != 0)
goto err;
++cpindx;
}
if (cnt != 1) {
if ((ret = __bam_adjindx(dbc,
h, dindx, first + 1, 0)) != 0)
goto err;
} else
dindx++;
if ((ret = __db_ditem(dbc, h, dindx, hdr.size)) != 0)
goto err;
indx += P_INDX;
} while (--cnt);
if ((ret = __bam_ovput(dbc,
B_DUPLICATE, dp->pgno, h, first + 1, NULL)) != 0)
goto err;
ret = __bam_ca_di(dbc,
PGNO(h), first + P_INDX, (int)(first + P_INDX - indx));
err: if ((t_ret = __memp_fput(mpf,
dbc->thread_info, dp, dbc->priority)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
static int
__bam_ovput(dbc, type, pgno, h, indx, item)
DBC *dbc;
u_int32_t type, indx;
db_pgno_t pgno;
PAGE *h;
DBT *item;
{
BOVERFLOW bo;
DBT hdr;
int ret;
UMRW_SET(bo.unused1);
B_TSET(bo.type, type);
UMRW_SET(bo.unused2);
if (type == B_OVERFLOW) {
if ((ret = __db_poff(dbc, item, &bo.pgno)) != 0)
return (ret);
bo.tlen = item->size;
} else {
bo.pgno = pgno;
bo.tlen = 0;
}
memset(&hdr, 0, sizeof(hdr));
hdr.data = &bo;
hdr.size = BOVERFLOW_SIZE;
return (__db_pitem(dbc, h, indx, BOVERFLOW_SIZE, &hdr, NULL));
}