bt_stat.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2002
00005  *      Sleepycat Software.  All rights reserved.
00006  */
00007 
00008 #include "db_config.h"
00009 
00010 #ifndef lint
00011 static const char revid[] = "$Id: bt_stat.c,v 11.52 2002/05/30 15:40:27 krinsky Exp $";
00012 #endif /* not lint */
00013 
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016 
00017 #include <string.h>
00018 #endif
00019 
00020 #include "db_int.h"
00021 #include "dbinc/db_page.h"
00022 #include "dbinc/db_shash.h"
00023 #include "dbinc/btree.h"
00024 #include "dbinc/lock.h"
00025 #include "dbinc/log.h"
00026 
00027 /*
00028  * __bam_stat --
00029  *      Gather/print the btree statistics
00030  *
00031  * PUBLIC: int __bam_stat __P((DB *, void *, u_int32_t));
00032  */
00033 int
00034 __bam_stat(dbp, spp, flags)
00035         DB *dbp;
00036         void *spp;
00037         u_int32_t flags;
00038 {
00039         BTMETA *meta;
00040         BTREE *t;
00041         BTREE_CURSOR *cp;
00042         DBC *dbc;
00043         DB_BTREE_STAT *sp;
00044         DB_LOCK lock, metalock;
00045         DB_MPOOLFILE *mpf;
00046         PAGE *h;
00047         db_pgno_t pgno;
00048         int ret, t_ret, write_meta;
00049 
00050         PANIC_CHECK(dbp->dbenv);
00051         DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat");
00052 
00053         meta = NULL;
00054         t = dbp->bt_internal;
00055         sp = NULL;
00056         LOCK_INIT(metalock);
00057         LOCK_INIT(lock);
00058         mpf = dbp->mpf;
00059         h = NULL;
00060         ret = 0;
00061         write_meta = 0;
00062 
00063         /* Check for invalid flags. */
00064         if ((ret = __db_statchk(dbp, flags)) != 0)
00065                 return (ret);
00066 
00067         /* Acquire a cursor. */
00068         if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
00069                 return (ret);
00070         cp = (BTREE_CURSOR *)dbc->internal;
00071 
00072         DEBUG_LWRITE(dbc, NULL, "bam_stat", NULL, NULL, flags);
00073 
00074         /* Allocate and clear the structure. */
00075         if ((ret = __os_umalloc(dbp->dbenv, sizeof(*sp), &sp)) != 0)
00076                 goto err;
00077         memset(sp, 0, sizeof(*sp));
00078 
00079         /* Get the metadata page for the entire database. */
00080         pgno = PGNO_BASE_MD;
00081         if ((ret = __db_lget(dbc, 0, pgno, DB_LOCK_READ, 0, &metalock)) != 0)
00082                 goto err;
00083         if ((ret = mpf->get(mpf, &pgno, 0, (PAGE **)&meta)) != 0)
00084                 goto err;
00085 
00086         if (flags == DB_RECORDCOUNT || flags == DB_CACHED_COUNTS)
00087                 flags = DB_FAST_STAT;
00088         if (flags == DB_FAST_STAT)
00089                 goto meta_only;
00090 
00091         /* Walk the metadata free list, counting pages. */
00092         for (sp->bt_free = 0, pgno = meta->dbmeta.free; pgno != PGNO_INVALID;) {
00093                 ++sp->bt_free;
00094 
00095                 if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
00096                         goto err;
00097 
00098                 pgno = h->next_pgno;
00099                 if ((ret = mpf->put(mpf, h, 0)) != 0)
00100                         goto err;
00101                 h = NULL;
00102         }
00103 
00104         /* Get the root page. */
00105         pgno = cp->root;
00106         if ((ret = __db_lget(dbc, 0, pgno, DB_LOCK_READ, 0, &lock)) != 0)
00107                 goto err;
00108         if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
00109                 goto err;
00110 
00111         /* Get the levels from the root page. */
00112         sp->bt_levels = h->level;
00113 
00114         /* Discard the root page. */
00115         if ((ret = mpf->put(mpf, h, 0)) != 0)
00116                 goto err;
00117         h = NULL;
00118         __LPUT(dbc, lock);
00119 
00120         /* Walk the tree. */
00121         if ((ret = __bam_traverse(dbc,
00122             DB_LOCK_READ, cp->root, __bam_stat_callback, sp)) != 0)
00123                 goto err;
00124 
00125         /*
00126          * Get the subdatabase metadata page if it's not the same as the
00127          * one we already have.
00128          */
00129         write_meta = !F_ISSET(dbp, DB_AM_RDONLY);
00130 meta_only:
00131         if (t->bt_meta != PGNO_BASE_MD || write_meta != 0) {
00132                 if ((ret = mpf->put(mpf, meta, 0)) != 0)
00133                         goto err;
00134                 meta = NULL;
00135                 __LPUT(dbc, metalock);
00136 
00137                 if ((ret = __db_lget(dbc,
00138                     0, t->bt_meta, write_meta == 0 ?
00139                     DB_LOCK_READ : DB_LOCK_WRITE, 0, &metalock)) != 0)
00140                         goto err;
00141                 if ((ret = mpf->get(mpf, &t->bt_meta, 0, (PAGE **)&meta)) != 0)
00142                         goto err;
00143         }
00144         if (flags == DB_FAST_STAT) {
00145                 if (dbp->type == DB_RECNO ||
00146                     (dbp->type == DB_BTREE && F_ISSET(dbp, DB_AM_RECNUM))) {
00147                         if ((ret = __db_lget(dbc, 0,
00148                             cp->root, DB_LOCK_READ, 0, &lock)) != 0)
00149                                 goto err;
00150                         if ((ret =
00151                             mpf->get(mpf, &cp->root, 0, (PAGE **)&h)) != 0)
00152                                 goto err;
00153 
00154                         sp->bt_nkeys = RE_NREC(h);
00155                 } else
00156                         sp->bt_nkeys = meta->dbmeta.key_count;
00157                 sp->bt_ndata = meta->dbmeta.record_count;
00158         }
00159 
00160         /* Get metadata page statistics. */
00161         sp->bt_metaflags = meta->dbmeta.flags;
00162         sp->bt_maxkey = meta->maxkey;
00163         sp->bt_minkey = meta->minkey;
00164         sp->bt_re_len = meta->re_len;
00165         sp->bt_re_pad = meta->re_pad;
00166         sp->bt_pagesize = meta->dbmeta.pagesize;
00167         sp->bt_magic = meta->dbmeta.magic;
00168         sp->bt_version = meta->dbmeta.version;
00169 
00170         if (write_meta != 0) {
00171                 meta->dbmeta.key_count = sp->bt_nkeys;
00172                 meta->dbmeta.record_count = sp->bt_ndata;
00173         }
00174 
00175         *(DB_BTREE_STAT **)spp = sp;
00176 
00177 err:    /* Discard the second page. */
00178         __LPUT(dbc, lock);
00179         if (h != NULL && (t_ret = mpf->put(mpf, h, 0)) != 0 && ret == 0)
00180                 ret = t_ret;
00181 
00182         /* Discard the metadata page. */
00183         __LPUT(dbc, metalock);
00184         if (meta != NULL && (t_ret = mpf->put(
00185             mpf, meta, write_meta == 0 ? 0 : DB_MPOOL_DIRTY)) != 0 && ret == 0)
00186                 ret = t_ret;
00187 
00188         if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00189                 ret = t_ret;
00190 
00191         if (ret != 0 && sp != NULL) {
00192                 __os_ufree(dbp->dbenv, sp);
00193                 *(DB_BTREE_STAT **)spp = NULL;
00194         }
00195 
00196         return (ret);
00197 }
00198 
00199 /*
00200  * __bam_traverse --
00201  *      Walk a Btree database.
00202  *
00203  * PUBLIC: int __bam_traverse __P((DBC *, db_lockmode_t,
00204  * PUBLIC:     db_pgno_t, int (*)(DB *, PAGE *, void *, int *), void *));
00205  */
00206 int
00207 __bam_traverse(dbc, mode, root_pgno, callback, cookie)
00208         DBC *dbc;
00209         db_lockmode_t mode;
00210         db_pgno_t root_pgno;
00211         int (*callback)__P((DB *, PAGE *, void *, int *));
00212         void *cookie;
00213 {
00214         BINTERNAL *bi;
00215         BKEYDATA *bk;
00216         DB *dbp;
00217         DB_LOCK lock;
00218         DB_MPOOLFILE *mpf;
00219         PAGE *h;
00220         RINTERNAL *ri;
00221         db_indx_t indx;
00222         int already_put, ret, t_ret;
00223 
00224         dbp = dbc->dbp;
00225         mpf = dbp->mpf;
00226         already_put = 0;
00227 
00228         if ((ret = __db_lget(dbc, 0, root_pgno, mode, 0, &lock)) != 0)
00229                 return (ret);
00230         if ((ret = mpf->get(mpf, &root_pgno, 0, &h)) != 0) {
00231                 __LPUT(dbc, lock);
00232                 return (ret);
00233         }
00234 
00235         switch (TYPE(h)) {
00236         case P_IBTREE:
00237                 for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
00238                         bi = GET_BINTERNAL(dbp, h, indx);
00239                         if (B_TYPE(bi->type) == B_OVERFLOW &&
00240                             (ret = __db_traverse_big(dbp,
00241                             ((BOVERFLOW *)bi->data)->pgno,
00242                             callback, cookie)) != 0)
00243                                 goto err;
00244                         if ((ret = __bam_traverse(
00245                             dbc, mode, bi->pgno, callback, cookie)) != 0)
00246                                 goto err;
00247                 }
00248                 break;
00249         case P_IRECNO:
00250                 for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
00251                         ri = GET_RINTERNAL(dbp, h, indx);
00252                         if ((ret = __bam_traverse(
00253                             dbc, mode, ri->pgno, callback, cookie)) != 0)
00254                                 goto err;
00255                 }
00256                 break;
00257         case P_LBTREE:
00258                 for (indx = 0; indx < NUM_ENT(h); indx += P_INDX) {
00259                         bk = GET_BKEYDATA(dbp, h, indx);
00260                         if (B_TYPE(bk->type) == B_OVERFLOW &&
00261                             (ret = __db_traverse_big(dbp,
00262                             GET_BOVERFLOW(dbp, h, indx)->pgno,
00263                             callback, cookie)) != 0)
00264                                 goto err;
00265                         bk = GET_BKEYDATA(dbp, h, indx + O_INDX);
00266                         if (B_TYPE(bk->type) == B_DUPLICATE &&
00267                             (ret = __bam_traverse(dbc, mode,
00268                             GET_BOVERFLOW(dbp, h, indx + O_INDX)->pgno,
00269                             callback, cookie)) != 0)
00270                                 goto err;
00271                         if (B_TYPE(bk->type) == B_OVERFLOW &&
00272                             (ret = __db_traverse_big(dbp,
00273                             GET_BOVERFLOW(dbp, h, indx + O_INDX)->pgno,
00274                             callback, cookie)) != 0)
00275                                 goto err;
00276                 }
00277                 break;
00278         case P_LDUP:
00279         case P_LRECNO:
00280                 for (indx = 0; indx < NUM_ENT(h); indx += O_INDX) {
00281                         bk = GET_BKEYDATA(dbp, h, indx);
00282                         if (B_TYPE(bk->type) == B_OVERFLOW &&
00283                             (ret = __db_traverse_big(dbp,
00284                             GET_BOVERFLOW(dbp, h, indx)->pgno,
00285                             callback, cookie)) != 0)
00286                                 goto err;
00287                 }
00288                 break;
00289         }
00290 
00291         ret = callback(dbp, h, cookie, &already_put);
00292 
00293 err:    if (!already_put && (t_ret = mpf->put(mpf, h, 0)) != 0 && ret != 0)
00294                 ret = t_ret;
00295         __LPUT(dbc, lock);
00296 
00297         return (ret);
00298 }
00299 
00300 /*
00301  * __bam_stat_callback --
00302  *      Statistics callback.
00303  *
00304  * PUBLIC: int __bam_stat_callback __P((DB *, PAGE *, void *, int *));
00305  */
00306 int
00307 __bam_stat_callback(dbp, h, cookie, putp)
00308         DB *dbp;
00309         PAGE *h;
00310         void *cookie;
00311         int *putp;
00312 {
00313         DB_BTREE_STAT *sp;
00314         db_indx_t indx, *inp, top;
00315         u_int8_t type;
00316 
00317         sp = cookie;
00318         *putp = 0;
00319         top = NUM_ENT(h);
00320         inp = P_INP(dbp, h);
00321 
00322         switch (TYPE(h)) {
00323         case P_IBTREE:
00324         case P_IRECNO:
00325                 ++sp->bt_int_pg;
00326                 sp->bt_int_pgfree += P_FREESPACE(dbp, h);
00327                 break;
00328         case P_LBTREE:
00329                 /* Correct for on-page duplicates and deleted items. */
00330                 for (indx = 0; indx < top; indx += P_INDX) {
00331                         if (indx + P_INDX >= top ||
00332                             inp[indx] != inp[indx + P_INDX])
00333                                 ++sp->bt_nkeys;
00334 
00335                         type = GET_BKEYDATA(dbp, h, indx + O_INDX)->type;
00336                         if (!B_DISSET(type) && B_TYPE(type) != B_DUPLICATE)
00337                                 ++sp->bt_ndata;
00338                 }
00339 
00340                 ++sp->bt_leaf_pg;
00341                 sp->bt_leaf_pgfree += P_FREESPACE(dbp, h);
00342                 break;
00343         case P_LRECNO:
00344                 /*
00345                  * If walking a recno tree, then each of these items is a key.
00346                  * Otherwise, we're walking an off-page duplicate set.
00347                  */
00348                 if (dbp->type == DB_RECNO) {
00349                         sp->bt_nkeys += top;
00350 
00351                         /*
00352                          * Correct for deleted items in non-renumbering
00353                          * Recno databases.
00354                          */
00355                         if (F_ISSET(dbp, DB_AM_RENUMBER))
00356                                 sp->bt_ndata += top;
00357                         else
00358                                 for (indx = 0; indx < top; indx += O_INDX) {
00359                                         type = GET_BKEYDATA(dbp, h, indx)->type;
00360                                         if (!B_DISSET(type))
00361                                                 ++sp->bt_ndata;
00362                                 }
00363 
00364                         ++sp->bt_leaf_pg;
00365                         sp->bt_leaf_pgfree += P_FREESPACE(dbp, h);
00366                 } else {
00367                         sp->bt_ndata += top;
00368 
00369                         ++sp->bt_dup_pg;
00370                         sp->bt_dup_pgfree += P_FREESPACE(dbp, h);
00371                 }
00372                 break;
00373         case P_LDUP:
00374                 /* Correct for deleted items. */
00375                 for (indx = 0; indx < top; indx += O_INDX)
00376                         if (!B_DISSET(GET_BKEYDATA(dbp, h, indx)->type))
00377                                 ++sp->bt_ndata;
00378 
00379                 ++sp->bt_dup_pg;
00380                 sp->bt_dup_pgfree += P_FREESPACE(dbp, h);
00381                 break;
00382         case P_OVERFLOW:
00383                 ++sp->bt_over_pg;
00384                 sp->bt_over_pgfree += P_OVFLSPACE(dbp, dbp->pgsize, h);
00385                 break;
00386         default:
00387                 return (__db_pgfmt(dbp->dbenv, h->pgno));
00388         }
00389         return (0);
00390 }
00391 
00392 /*
00393  * __bam_key_range --
00394  *      Return proportion of keys relative to given key.  The numbers are
00395  *      slightly skewed due to on page duplicates.
00396  *
00397  * PUBLIC: int __bam_key_range __P((DB *,
00398  * PUBLIC:     DB_TXN *, DBT *, DB_KEY_RANGE *, u_int32_t));
00399  */
00400 int
00401 __bam_key_range(dbp, txn, dbt, kp, flags)
00402         DB *dbp;
00403         DB_TXN *txn;
00404         DBT *dbt;
00405         DB_KEY_RANGE *kp;
00406         u_int32_t flags;
00407 {
00408         BTREE_CURSOR *cp;
00409         DBC *dbc;
00410         EPG *sp;
00411         double factor;
00412         int exact, ret, t_ret;
00413 
00414         PANIC_CHECK(dbp->dbenv);
00415         DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->key_range");
00416 
00417         if (flags != 0)
00418                 return (__db_ferr(dbp->dbenv, "DB->key_range", 0));
00419 
00420         /* Check for consistent transaction usage. */
00421         if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 1)) != 0)
00422                 return (ret);
00423 
00424         /* Acquire a cursor. */
00425         if ((ret = dbp->cursor(dbp, txn, &dbc, 0)) != 0)
00426                 return (ret);
00427 
00428         DEBUG_LWRITE(dbc, NULL, "bam_key_range", NULL, NULL, 0);
00429 
00430         if ((ret = __bam_search(dbc, PGNO_INVALID,
00431             dbt, S_STK_ONLY, 1, NULL, &exact)) != 0)
00432                 goto err;
00433 
00434         cp = (BTREE_CURSOR *)dbc->internal;
00435         kp->less = kp->greater = 0.0;
00436 
00437         factor = 1.0;
00438         /* Correct the leaf page. */
00439         cp->csp->entries /= 2;
00440         cp->csp->indx /= 2;
00441         for (sp = cp->sp; sp <= cp->csp; ++sp) {
00442                 /*
00443                  * At each level we know that pages greater than indx contain
00444                  * keys greater than what we are looking for and those less
00445                  * than indx are less than.  The one pointed to by indx may
00446                  * have some less, some greater or even equal.  If indx is
00447                  * equal to the number of entries, then the key is out of range
00448                  * and everything is less.
00449                  */
00450                 if (sp->indx == 0)
00451                         kp->greater += factor * (sp->entries - 1)/sp->entries;
00452                 else if (sp->indx == sp->entries)
00453                         kp->less += factor;
00454                 else {
00455                         kp->less += factor * sp->indx / sp->entries;
00456                         kp->greater += factor *
00457                             (sp->entries - sp->indx - 1) / sp->entries;
00458                 }
00459                 factor *= 1.0/sp->entries;
00460         }
00461 
00462         /*
00463          * If there was an exact match then assign 1 n'th to the key itself.
00464          * Otherwise that factor belongs to those greater than the key, unless
00465          * the key was out of range.
00466          */
00467         if (exact)
00468                 kp->equal = factor;
00469         else {
00470                 if (kp->less != 1)
00471                         kp->greater += factor;
00472                 kp->equal = 0;
00473         }
00474 
00475         BT_STK_CLR(cp);
00476 
00477 err:    if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00478                 ret = t_ret;
00479 
00480         return (ret);
00481 }

Generated on Wed Jul 20 21:03:01 2005 for MySQL 5.0.9 Beta by  doxygen 1.4.3