2
* See the file LICENSE for redistribution information.
4
* Copyright (c) 1996-2002
5
* Sleepycat Software. All rights reserved.
11
static const char revid[] = "$Id$";
14
#ifndef NO_SYSTEM_INCLUDES
15
#include <sys/types.h>
21
#include "dbinc/db_page.h"
22
#include "dbinc/db_shash.h"
23
#include "dbinc/btree.h"
24
#include "dbinc/lock.h"
25
#include "dbinc/log.h"
29
* Gather/print the btree statistics
31
* PUBLIC: int __bam_stat __P((DB *, void *, u_int32_t));
34
__bam_stat(dbp, spp, flags)
44
DB_LOCK lock, metalock;
48
int ret, t_ret, write_meta;
50
PANIC_CHECK(dbp->dbenv);
51
DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat");
63
/* Check for invalid flags. */
64
if ((ret = __db_statchk(dbp, flags)) != 0)
67
/* Acquire a cursor. */
68
if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
70
cp = (BTREE_CURSOR *)dbc->internal;
72
DEBUG_LWRITE(dbc, NULL, "bam_stat", NULL, NULL, flags);
74
/* Allocate and clear the structure. */
75
if ((ret = __os_umalloc(dbp->dbenv, sizeof(*sp), &sp)) != 0)
77
memset(sp, 0, sizeof(*sp));
79
/* Get the metadata page for the entire database. */
81
if ((ret = __db_lget(dbc, 0, pgno, DB_LOCK_READ, 0, &metalock)) != 0)
83
if ((ret = mpf->get(mpf, &pgno, 0, (PAGE **)&meta)) != 0)
86
if (flags == DB_RECORDCOUNT || flags == DB_CACHED_COUNTS)
88
if (flags == DB_FAST_STAT)
91
/* Walk the metadata free list, counting pages. */
92
for (sp->bt_free = 0, pgno = meta->dbmeta.free; pgno != PGNO_INVALID;) {
95
if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
99
if ((ret = mpf->put(mpf, h, 0)) != 0)
104
/* Get the root page. */
106
if ((ret = __db_lget(dbc, 0, pgno, DB_LOCK_READ, 0, &lock)) != 0)
108
if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
111
/* Get the levels from the root page. */
112
sp->bt_levels = h->level;
114
/* Discard the root page. */
115
if ((ret = mpf->put(mpf, h, 0)) != 0)
121
if ((ret = __bam_traverse(dbc,
122
DB_LOCK_READ, cp->root, __bam_stat_callback, sp)) != 0)
126
* Get the subdatabase metadata page if it's not the same as the
127
* one we already have.
129
write_meta = !F_ISSET(dbp, DB_AM_RDONLY);
131
if (t->bt_meta != PGNO_BASE_MD || write_meta != 0) {
132
if ((ret = mpf->put(mpf, meta, 0)) != 0)
135
__LPUT(dbc, metalock);
137
if ((ret = __db_lget(dbc,
138
0, t->bt_meta, write_meta == 0 ?
139
DB_LOCK_READ : DB_LOCK_WRITE, 0, &metalock)) != 0)
141
if ((ret = mpf->get(mpf, &t->bt_meta, 0, (PAGE **)&meta)) != 0)
144
if (flags == DB_FAST_STAT) {
145
if (dbp->type == DB_RECNO ||
146
(dbp->type == DB_BTREE && F_ISSET(dbp, DB_AM_RECNUM))) {
147
if ((ret = __db_lget(dbc, 0,
148
cp->root, DB_LOCK_READ, 0, &lock)) != 0)
151
mpf->get(mpf, &cp->root, 0, (PAGE **)&h)) != 0)
154
sp->bt_nkeys = RE_NREC(h);
156
sp->bt_nkeys = meta->dbmeta.key_count;
157
sp->bt_ndata = meta->dbmeta.record_count;
160
/* Get metadata page statistics. */
161
sp->bt_metaflags = meta->dbmeta.flags;
162
sp->bt_maxkey = meta->maxkey;
163
sp->bt_minkey = meta->minkey;
164
sp->bt_re_len = meta->re_len;
165
sp->bt_re_pad = meta->re_pad;
166
sp->bt_pagesize = meta->dbmeta.pagesize;
167
sp->bt_magic = meta->dbmeta.magic;
168
sp->bt_version = meta->dbmeta.version;
170
if (write_meta != 0) {
171
meta->dbmeta.key_count = sp->bt_nkeys;
172
meta->dbmeta.record_count = sp->bt_ndata;
175
*(DB_BTREE_STAT **)spp = sp;
177
err: /* Discard the second page. */
179
if (h != NULL && (t_ret = mpf->put(mpf, h, 0)) != 0 && ret == 0)
182
/* Discard the metadata page. */
183
__LPUT(dbc, metalock);
184
if (meta != NULL && (t_ret = mpf->put(
185
mpf, meta, write_meta == 0 ? 0 : DB_MPOOL_DIRTY)) != 0 && ret == 0)
188
if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
191
if (ret != 0 && sp != NULL) {
192
__os_ufree(dbp->dbenv, sp);
193
*(DB_BTREE_STAT **)spp = NULL;
201
* Walk a Btree database.
203
* PUBLIC: int __bam_traverse __P((DBC *, db_lockmode_t,
204
* PUBLIC: db_pgno_t, int (*)(DB *, PAGE *, void *, int *), void *));
207
__bam_traverse(dbc, mode, root_pgno, callback, cookie)
211
int (*callback)__P((DB *, PAGE *, void *, int *));
222
int already_put, ret, t_ret;
228
if ((ret = __db_lget(dbc, 0, root_pgno, mode, 0, &lock)) != 0)
230
if ((ret = mpf->get(mpf, &root_pgno, 0, &h)) != 0) {
237
for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
238
bi = GET_BINTERNAL(dbp, h, indx);
239
if (B_TYPE(bi->type) == B_OVERFLOW &&
240
(ret = __db_traverse_big(dbp,
241
((BOVERFLOW *)bi->data)->pgno,
242
callback, cookie)) != 0)
244
if ((ret = __bam_traverse(
245
dbc, mode, bi->pgno, callback, cookie)) != 0)
250
for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
251
ri = GET_RINTERNAL(dbp, h, indx);
252
if ((ret = __bam_traverse(
253
dbc, mode, ri->pgno, callback, cookie)) != 0)
258
for (indx = 0; indx < NUM_ENT(h); indx += P_INDX) {
259
bk = GET_BKEYDATA(dbp, h, indx);
260
if (B_TYPE(bk->type) == B_OVERFLOW &&
261
(ret = __db_traverse_big(dbp,
262
GET_BOVERFLOW(dbp, h, indx)->pgno,
263
callback, cookie)) != 0)
265
bk = GET_BKEYDATA(dbp, h, indx + O_INDX);
266
if (B_TYPE(bk->type) == B_DUPLICATE &&
267
(ret = __bam_traverse(dbc, mode,
268
GET_BOVERFLOW(dbp, h, indx + O_INDX)->pgno,
269
callback, cookie)) != 0)
271
if (B_TYPE(bk->type) == B_OVERFLOW &&
272
(ret = __db_traverse_big(dbp,
273
GET_BOVERFLOW(dbp, h, indx + O_INDX)->pgno,
274
callback, cookie)) != 0)
280
for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
281
bk = GET_BKEYDATA(dbp, h, indx);
282
if (B_TYPE(bk->type) == B_OVERFLOW &&
283
(ret = __db_traverse_big(dbp,
284
GET_BOVERFLOW(dbp, h, indx)->pgno,
285
callback, cookie)) != 0)
291
ret = callback(dbp, h, cookie, &already_put);
293
err: if (!already_put && (t_ret = mpf->put(mpf, h, 0)) != 0 && ret != 0)
301
* __bam_stat_callback --
302
* Statistics callback.
304
* PUBLIC: int __bam_stat_callback __P((DB *, PAGE *, void *, int *));
307
__bam_stat_callback(dbp, h, cookie, putp)
314
db_indx_t indx, *inp, top;
326
sp->bt_int_pgfree += P_FREESPACE(dbp, h);
329
/* Correct for on-page duplicates and deleted items. */
330
for (indx = 0; indx < top; indx += P_INDX) {
331
if (indx + P_INDX >= top ||
332
inp[indx] != inp[indx + P_INDX])
335
type = GET_BKEYDATA(dbp, h, indx + O_INDX)->type;
336
if (!B_DISSET(type) && B_TYPE(type) != B_DUPLICATE)
341
sp->bt_leaf_pgfree += P_FREESPACE(dbp, h);
345
* If walking a recno tree, then each of these items is a key.
346
* Otherwise, we're walking an off-page duplicate set.
348
if (dbp->type == DB_RECNO) {
352
* Correct for deleted items in non-renumbering
355
if (F_ISSET(dbp, DB_AM_RENUMBER))
358
for (indx = 0; indx < top; indx += O_INDX) {
359
type = GET_BKEYDATA(dbp, h, indx)->type;
365
sp->bt_leaf_pgfree += P_FREESPACE(dbp, h);
370
sp->bt_dup_pgfree += P_FREESPACE(dbp, h);
374
/* Correct for deleted items. */
375
for (indx = 0; indx < top; indx += O_INDX)
376
if (!B_DISSET(GET_BKEYDATA(dbp, h, indx)->type))
380
sp->bt_dup_pgfree += P_FREESPACE(dbp, h);
384
sp->bt_over_pgfree += P_OVFLSPACE(dbp, dbp->pgsize, h);
387
return (__db_pgfmt(dbp->dbenv, h->pgno));
394
* Return proportion of keys relative to given key. The numbers are
395
* slightly skewed due to on page duplicates.
397
* PUBLIC: int __bam_key_range __P((DB *,
398
* PUBLIC: DB_TXN *, DBT *, DB_KEY_RANGE *, u_int32_t));
401
__bam_key_range(dbp, txn, dbt, kp, flags)
412
int exact, ret, t_ret;
414
PANIC_CHECK(dbp->dbenv);
415
DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->key_range");
418
return (__db_ferr(dbp->dbenv, "DB->key_range", 0));
420
/* Check for consistent transaction usage. */
421
if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 1)) != 0)
424
/* Acquire a cursor. */
425
if ((ret = dbp->cursor(dbp, txn, &dbc, 0)) != 0)
428
DEBUG_LWRITE(dbc, NULL, "bam_key_range", NULL, NULL, 0);
430
if ((ret = __bam_search(dbc, PGNO_INVALID,
431
dbt, S_STK_ONLY, 1, NULL, &exact)) != 0)
434
cp = (BTREE_CURSOR *)dbc->internal;
435
kp->less = kp->greater = 0.0;
438
/* Correct the leaf page. */
439
cp->csp->entries /= 2;
441
for (sp = cp->sp; sp <= cp->csp; ++sp) {
443
* At each level we know that pages greater than indx contain
444
* keys greater than what we are looking for and those less
445
* than indx are less than. The one pointed to by indx may
446
* have some less, some greater or even equal. If indx is
447
* equal to the number of entries, then the key is out of range
448
* and everything is less.
451
kp->greater += factor * (sp->entries - 1)/sp->entries;
452
else if (sp->indx == sp->entries)
455
kp->less += factor * sp->indx / sp->entries;
456
kp->greater += factor *
457
(sp->entries - sp->indx - 1) / sp->entries;
459
factor *= 1.0/sp->entries;
463
* If there was an exact match then assign 1 n'th to the key itself.
464
* Otherwise that factor belongs to those greater than the key, unless
465
* the key was out of range.
471
kp->greater += factor;
477
err: if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)