00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <sys/types.h>
00019 #include <errno.h>
00020 #include <unistd.h>
00021
00022 #include <debug/DebugUtils.h>
00023 #include <io/FileUtils.h>
00024
00025 #include <util/StringBuffer.h>
00026 #include <util/Pointers.h>
00027 #include <util/ScratchBuffer.h>
00028
00029 #include <serialize/MarshalSerialize.h>
00030 #include <serialize/TypeShims.h>
00031
00032 #include "BerkeleyDBStore.h"
00033 #include "StorageConfig.h"
00034 #include "util/InitSequencer.h"
00035
00036 #define NO_TX 0 // for easily going back and changing TX id's later
00037
00038 namespace oasys {
00039
00040
00041
00042
00043
00044 const std::string BerkeleyDBStore::META_TABLE_NAME("___META_TABLE___");
00045
00046 BerkeleyDBStore::BerkeleyDBStore(const char* logpath)
00047 : DurableStoreImpl("BerkeleyDBStore", logpath),
00048 init_(false)
00049 {}
00050
00051
00052 BerkeleyDBStore::~BerkeleyDBStore()
00053 {
00054 StringBuffer err_str;
00055
00056 err_str.append("Tables still open at deletion time: ");
00057 bool busy = false;
00058
00059 for (RefCountMap::iterator iter = ref_count_.begin();
00060 iter != ref_count_.end(); ++iter)
00061 {
00062 if (iter->second != 0)
00063 {
00064 err_str.appendf("%s ", iter->first.c_str());
00065 busy = true;
00066 }
00067 }
00068
00069 if (busy)
00070 {
00071 log_err(err_str.c_str());
00072 }
00073
00074 if (deadlock_timer_) {
00075 deadlock_timer_->cancel();
00076 }
00077
00078 dbenv_->close(dbenv_, 0);
00079 dbenv_ = 0;
00080 log_info("db closed");
00081 }
00082
00083
00084 int
00085 BerkeleyDBStore::init(const StorageConfig& cfg)
00086 {
00087 std::string dbdir = cfg.dbdir_;
00088 FileUtils::abspath(&dbdir);
00089
00090 db_name_ = cfg.dbname_;
00091 sharefile_ = cfg.db_sharefile_;
00092
00093
00094 if (cfg.tidy_) {
00095 prune_db_dir(dbdir.c_str(), cfg.tidy_wait_);
00096 }
00097
00098 bool db_dir_exists;
00099 int err = check_db_dir(dbdir.c_str(), &db_dir_exists);
00100 if (err != 0)
00101 {
00102 return DS_ERR;
00103 }
00104 if (!db_dir_exists)
00105 {
00106 if (cfg.init_) {
00107 if (create_db_dir(dbdir.c_str()) != 0) {
00108 return DS_ERR;
00109 }
00110 } else {
00111 log_crit("DB dir %s does not exist and not told to create!",
00112 dbdir.c_str());
00113 return DS_ERR;
00114 }
00115 }
00116
00117 db_env_create(&dbenv_, 0);
00118 if (dbenv_ == 0)
00119 {
00120 log_crit("Can't create db env");
00121 return DS_ERR;
00122 }
00123
00124 dbenv_->set_errcall(dbenv_, BerkeleyDBStore::db_errcall);
00125
00126 log_info("initializing db name=%s (%s), dir=%s",
00127 db_name_.c_str(), sharefile_ ? "shared" : "not shared",
00128 dbdir.c_str());
00129
00130 #define SET_DBENV_OPTION(_opt, _fn) \
00131 if (cfg._opt != 0) { \
00132 err = dbenv_->_fn(dbenv_, cfg._opt); \
00133 \
00134 if (err != 0) \
00135 { \
00136 log_crit("DB: %s, cannot %s to %d", \
00137 db_strerror(err), #_fn, cfg._opt); \
00138 return DS_ERR; \
00139 } \
00140 }
00141
00142 SET_DBENV_OPTION(db_max_tx_, set_tx_max);
00143 SET_DBENV_OPTION(db_max_locks_, set_lk_max_locks);
00144 SET_DBENV_OPTION(db_max_lockers_, set_lk_max_lockers);
00145 SET_DBENV_OPTION(db_max_lockedobjs_, set_lk_max_objects);
00146 SET_DBENV_OPTION(db_max_logregion_, set_lg_regionmax);
00147
00148 #undef SET_DBENV_OPTION
00149
00150 int dbenv_opts =
00151 DB_CREATE |
00152 DB_PRIVATE
00153 ;
00154
00155 if (cfg.db_lockdetect_ != 0) {
00156 dbenv_opts |= DB_INIT_LOCK | DB_THREAD;
00157 }
00158
00159 if (cfg.db_mpool_) {
00160 dbenv_opts |= DB_INIT_MPOOL;
00161 }
00162
00163 if (cfg.db_log_) {
00164 dbenv_opts |= DB_INIT_LOG;
00165 }
00166
00167 if (cfg.db_txn_) {
00168 dbenv_opts |= DB_INIT_TXN | DB_RECOVER;
00169 }
00170
00171 err = dbenv_->open(dbenv_, dbdir.c_str(), dbenv_opts, 0 );
00172
00173 if (err != 0)
00174 {
00175 log_crit("DB: %s, cannot open database", db_strerror(err));
00176 return DS_ERR;
00177 }
00178
00179 if (cfg.db_txn_) {
00180 err = dbenv_->set_flags(dbenv_,
00181 DB_AUTO_COMMIT |
00182 DB_LOG_AUTOREMOVE,
00183 1);
00184 if (err != 0)
00185 {
00186 log_crit("DB: %s, cannot set flags", db_strerror(err));
00187 return DS_ERR;
00188 }
00189 }
00190
00191 err = dbenv_->set_paniccall(dbenv_, BerkeleyDBStore::db_panic);
00192
00193 if (err != 0)
00194 {
00195 log_crit("DB: %s, cannot set panic call", db_strerror(err));
00196 return DS_ERR;
00197 }
00198
00199 if (cfg.db_lockdetect_ != 0) {
00200 deadlock_timer_ = new DeadlockTimer(logpath_, dbenv_, cfg.db_lockdetect_);
00201 deadlock_timer_->reschedule();
00202 } else {
00203 deadlock_timer_ = NULL;
00204 }
00205
00206 init_ = true;
00207
00208 return 0;
00209 }
00210
00211
00212 int
00213 BerkeleyDBStore::get_table(DurableTableImpl** table,
00214 const std::string& name,
00215 int flags,
00216 PrototypeVector& prototypes)
00217 {
00218 (void)prototypes;
00219
00220 DB* db;
00221 int err;
00222 DBTYPE db_type = DB_BTREE;
00223 u_int32_t db_flags;
00224
00225 ASSERT(init_);
00226
00227
00228 err = db_create(&db, dbenv_, 0);
00229 if (err != 0) {
00230 log_err("error creating database handle: %s", db_strerror(err));
00231 return DS_ERR;
00232 }
00233
00234
00235 db_flags = 0;
00236
00237 if (flags & DS_CREATE) {
00238 db_flags |= DB_CREATE;
00239
00240 if (flags & DS_EXCL) {
00241 db_flags |= DB_EXCL;
00242 }
00243
00244 if (((flags & DS_BTREE) != 0) && ((flags & DS_HASH) != 0)) {
00245 PANIC("both DS_HASH and DS_BTREE were specified");
00246 }
00247
00248 if (flags & DS_HASH)
00249 {
00250 db_type = DB_HASH;
00251 }
00252 else if (flags & DS_BTREE)
00253 {
00254 db_type = DB_BTREE;
00255 }
00256 else
00257 {
00258 db_type = DB_BTREE;
00259 }
00260
00261 } else {
00262 db_type = DB_UNKNOWN;
00263 }
00264
00265 if (deadlock_timer_) {
00266
00267 db_flags |= DB_THREAD;
00268 }
00269
00270 retry:
00271 if (sharefile_) {
00272 oasys::StaticStringBuffer<128> dbfile("%s.db", db_name_.c_str());
00273 err = db->open(db, NO_TX, dbfile.c_str(), name.c_str(),
00274 db_type, db_flags, 0);
00275 } else {
00276 oasys::StaticStringBuffer<128> dbname("%s-%s.db",
00277 db_name_.c_str(), name.c_str());
00278 err = db->open(db, NO_TX, dbname.c_str(), NULL,
00279 db_type, db_flags, 0);
00280 }
00281
00282 if (err == ENOENT)
00283 {
00284 log_debug("get_table -- notfound database %s", name.c_str());
00285 db->close(db, 0);
00286 return DS_NOTFOUND;
00287 }
00288 else if (err == EEXIST)
00289 {
00290 log_debug("get_table -- already existing database %s", name.c_str());
00291 db->close(db, 0);
00292 return DS_EXISTS;
00293 }
00294 else if (err == DB_LOCK_DEADLOCK)
00295 {
00296 log_warn("deadlock in get_table, retrying operation");
00297 goto retry;
00298 }
00299 else if (err != 0)
00300 {
00301 log_err("DB internal error in get_table: %s", db_strerror(err));
00302 db->close(db, 0);
00303 return DS_ERR;
00304 }
00305
00306 if (db_type == DB_UNKNOWN) {
00307 err = db->get_type(db, &db_type);
00308 if (err != 0) {
00309 log_err("DB internal error in get_type: %s", db_strerror(err));
00310 db->close(db, 0);
00311 return DS_ERR;
00312 }
00313 }
00314
00315 log_debug("get_table -- opened table %s type %d", name.c_str(), db_type);
00316
00317 *table = new BerkeleyDBTable(logpath_, this, name, (flags & DS_MULTITYPE),
00318 db, db_type);
00319
00320 return 0;
00321 }
00322
00323
00324 int
00325 BerkeleyDBStore::del_table(const std::string& name)
00326 {
00327 int err;
00328
00329 ASSERT(init_);
00330
00331 if (ref_count_[name] != 0)
00332 {
00333 log_info("Trying to delete table %s with %d refs still on it",
00334 name.c_str(), ref_count_[name]);
00335
00336 return DS_BUSY;
00337 }
00338
00339 log_info("deleting table %s", name.c_str());
00340
00341 if (sharefile_) {
00342 oasys::StaticStringBuffer<128> dbfile("%s.db", db_name_.c_str());
00343 err = dbenv_->dbremove(dbenv_, NO_TX, dbfile.c_str(), name.c_str(), 0);
00344 } else {
00345 oasys::StaticStringBuffer<128> dbfile("%s-%s.db",
00346 db_name_.c_str(), name.c_str());
00347 err = dbenv_->dbremove(dbenv_, NO_TX, dbfile.c_str(), NULL, 0);
00348 }
00349
00350 if (err != 0) {
00351 log_err("del_table %s", db_strerror(err));
00352
00353 if (err == ENOENT)
00354 {
00355 return DS_NOTFOUND;
00356 }
00357 else
00358 {
00359 return DS_ERR;
00360 }
00361 }
00362
00363 ref_count_.erase(name);
00364
00365 return 0;
00366 }
00367
00368
00369 int
00370 BerkeleyDBStore::get_table_names(StringVector* names)
00371 {
00372 names->clear();
00373
00374 if (sharefile_)
00375 {
00376 BerkeleyDBTable* metatable;
00377 int err = get_meta_table(&metatable);
00378
00379 if (err != DS_OK) {
00380 return err;
00381 }
00382
00383
00384
00385
00386 DBC* cursor = 0;
00387 err = metatable->db_->cursor(metatable->db_, NO_TX, &cursor, 0);
00388 if (err != 0)
00389 {
00390 log_err("cannot create iterator for metatable, err=%s",
00391 db_strerror(err));
00392 return DS_ERR;
00393 }
00394
00395 for (;;)
00396 {
00397 DBTRef key, data;
00398 err = cursor->c_get(cursor, key.dbt(), data.dbt(), DB_NEXT);
00399 if (err == DB_NOTFOUND)
00400 {
00401 break;
00402 }
00403 else if (err != 0)
00404 {
00405 log_err("error getting next item with iterator, err=%s",
00406 db_strerror(err));
00407 return DS_ERR;
00408 }
00409 names->push_back(std::string(static_cast<char*>(key->data),
00410 key->size));
00411 }
00412
00413 if (cursor)
00414 {
00415 err = cursor->c_close(cursor);
00416 if (err != 0)
00417 {
00418 log_err("DB: cannot close cursor, %s", db_strerror(err));
00419 return DS_ERR;
00420 }
00421 }
00422 delete_z(metatable);
00423 }
00424 else
00425 {
00426
00427 NOTIMPLEMENTED;
00428 }
00429
00430 return 0;
00431 }
00432
00433
00434 std::string
00435 BerkeleyDBStore::get_info() const
00436 {
00437 StringBuffer desc;
00438
00439 return "BerkeleyDB";
00440 }
00441
00442
00443 int
00444 BerkeleyDBStore::get_meta_table(BerkeleyDBTable** table)
00445 {
00446 DB* db;
00447 int err;
00448
00449 ASSERT(init_);
00450
00451 if (! sharefile_) {
00452 log_err("unable to open metatable for an unshared berkeley db");
00453 return DS_ERR;
00454 }
00455
00456 err = db_create(&db, dbenv_, 0);
00457 if (err != 0) {
00458 log_err("Can't create db pointer");
00459 return DS_ERR;
00460 }
00461
00462 oasys::StaticStringBuffer<128> dbfile("%s.db", db_name_.c_str());
00463 err = db->open(db, NO_TX, dbfile.c_str(),
00464 NULL, DB_UNKNOWN, DB_RDONLY, 0);
00465 if (err != 0) {
00466 log_err("unable to open metatable - DB: %s", db_strerror(err));
00467 return DS_ERR;
00468 }
00469
00470 DBTYPE type;
00471 err = db->get_type(db, &type);
00472 if (err != 0) {
00473 log_err("unable to get metatable type - DB: %s", db_strerror(err));
00474 return DS_ERR;
00475 }
00476
00477 *table = new BerkeleyDBTable(logpath_, this, META_TABLE_NAME, false, db, type);
00478
00479 return 0;
00480 }
00481
00482
00483 int
00484 BerkeleyDBStore::acquire_table(const std::string& table)
00485 {
00486 ASSERT(init_);
00487
00488 ++ref_count_[table];
00489 ASSERT(ref_count_[table] >= 0);
00490
00491 log_debug("table %s, +refcount=%d", table.c_str(), ref_count_[table]);
00492
00493 return ref_count_[table];
00494 }
00495
00496
00497 int
00498 BerkeleyDBStore::release_table(const std::string& table)
00499 {
00500 ASSERT(init_);
00501
00502 --ref_count_[table];
00503 ASSERT(ref_count_[table] >= 0);
00504
00505 log_debug("table %s, -refcount=%d", table.c_str(), ref_count_[table]);
00506
00507 return ref_count_[table];
00508 }
00509
00510
00511 #if DB_VERSION_MINOR >= 3
00512 void
00513 BerkeleyDBStore::db_errcall(const DB_ENV* dbenv,
00514 const char* errpfx,
00515 const char* msg)
00516 {
00517 (void)dbenv;
00518 (void)errpfx;
00519 log_err_p("/storage/berkeleydb", "DB internal error: %s", msg);
00520 }
00521
00522 #else
00523
00524
00525 void
00526 BerkeleyDBStore::db_errcall(const char* errpfx, char* msg)
00527 {
00528 (void)errpfx;
00529 log_err_p("/storage/berkeleydb", "DB internal error: %s", msg);
00530 }
00531
00532 #endif
00533
00534
00535 void
00536 BerkeleyDBStore::db_panic(DB_ENV* dbenv, int errval)
00537 {
00538 (void)dbenv;
00539 PANIC("fatal berkeley DB internal error: %s", db_strerror(errval));
00540 }
00541
00542
00543 void
00544 BerkeleyDBStore::DeadlockTimer::reschedule()
00545 {
00546 log_debug("rescheduling in %d msecs", frequency_);
00547 schedule_in(frequency_);
00548 }
00549
00550
00551 void
00552 BerkeleyDBStore::DeadlockTimer::timeout(const struct timeval& now)
00553 {
00554 (void)now;
00555 int aborted = 0;
00556 log_debug("running deadlock detection");
00557 dbenv_->lock_detect(dbenv_, 0, DB_LOCK_YOUNGEST, &aborted);
00558
00559 if (aborted != 0) {
00560 log_warn("deadlock detection found %d aborted transactions", aborted);
00561 }
00562
00563 reschedule();
00564 }
00565
00566
00567
00568
00569
00570
00571 BerkeleyDBTable::BerkeleyDBTable(const char* logpath,
00572 BerkeleyDBStore* store,
00573 const std::string& table_name,
00574 bool multitype,
00575 DB* db, DBTYPE db_type)
00576 : DurableTableImpl(table_name, multitype),
00577 Logger("BerkeleyDBTable", "%s/%s", logpath, table_name.c_str()),
00578 db_(db), db_type_(db_type), store_(store)
00579 {
00580 store_->acquire_table(table_name);
00581 }
00582
00583
00584 BerkeleyDBTable::~BerkeleyDBTable()
00585 {
00586
00587
00588
00589 store_->release_table(name());
00590
00591 log_debug("closing db %s", name());
00592 db_->close(db_, 0);
00593 db_ = NULL;
00594 }
00595
00596
00597 int
00598 BerkeleyDBTable::get(const SerializableObject& key,
00599 SerializableObject* data)
00600 {
00601 ASSERTF(!multitype_, "single-type get called for multi-type table");
00602
00603 ScratchBuffer<u_char*, 256> key_buf;
00604 size_t key_buf_len = flatten(key, &key_buf);
00605 ASSERT(key_buf_len != 0);
00606
00607 DBTRef k(key_buf.buf(), key_buf_len);
00608 DBTRef d;
00609
00610 int err = db_->get(db_, NO_TX, k.dbt(), d.dbt(), 0);
00611
00612 if (err == DB_NOTFOUND)
00613 {
00614 return DS_NOTFOUND;
00615 }
00616 else if (err != 0)
00617 {
00618 log_err("DB: %s", db_strerror(err));
00619 return DS_ERR;
00620 }
00621
00622 u_char* bp = (u_char*)d->data;
00623 size_t sz = d->size;
00624
00625 Unmarshal unmarshaller(Serialize::CONTEXT_LOCAL, bp, sz);
00626
00627 if (unmarshaller.action(data) != 0) {
00628 log_err("DB: error unserializing data object");
00629 return DS_ERR;
00630 }
00631
00632 return 0;
00633 }
00634
00635
00636 int
00637 BerkeleyDBTable::get(const SerializableObject& key,
00638 SerializableObject** data,
00639 TypeCollection::Allocator_t allocator)
00640 {
00641 ASSERTF(multitype_, "multi-type get called for single-type table");
00642
00643 ScratchBuffer<u_char*, 256> key_buf;
00644 size_t key_buf_len = flatten(key, &key_buf);
00645 if (key_buf_len == 0)
00646 {
00647 log_err("zero or too long key length");
00648 return DS_ERR;
00649 }
00650
00651 DBTRef k(key_buf.buf(), key_buf_len);
00652 DBTRef d;
00653
00654 int err = db_->get(db_, NO_TX, k.dbt(), d.dbt(), 0);
00655
00656 if (err == DB_NOTFOUND)
00657 {
00658 return DS_NOTFOUND;
00659 }
00660 else if (err != 0)
00661 {
00662 log_err("DB: %s", db_strerror(err));
00663 return DS_ERR;
00664 }
00665
00666 u_char* bp = (u_char*)d->data;
00667 size_t sz = d->size;
00668
00669 TypeCollection::TypeCode_t typecode;
00670 size_t typecode_sz = MarshalSize::get_size(&typecode);
00671
00672 Builder b;
00673 UIntShim type_shim(b);
00674 Unmarshal type_unmarshaller(Serialize::CONTEXT_LOCAL, bp, typecode_sz);
00675
00676 if (type_unmarshaller.action(&type_shim) != 0) {
00677 log_err("DB: error unserializing type code");
00678 return DS_ERR;
00679 }
00680
00681 typecode = type_shim.value();
00682
00683 bp += typecode_sz;
00684 sz -= typecode_sz;
00685
00686 err = allocator(typecode, data);
00687 if (err != 0) {
00688 *data = NULL;
00689 return DS_ERR;
00690 }
00691
00692 ASSERT(*data != NULL);
00693
00694 Unmarshal unmarshaller(Serialize::CONTEXT_LOCAL, bp, sz);
00695
00696 if (unmarshaller.action(*data) != 0) {
00697 log_err("DB: error unserializing data object");
00698 delete *data;
00699 *data = NULL;
00700 return DS_ERR;
00701 }
00702
00703 return DS_OK;
00704 }
00705
00706
00707 int
00708 BerkeleyDBTable::put(const SerializableObject& key,
00709 TypeCollection::TypeCode_t typecode,
00710 const SerializableObject* data,
00711 int flags)
00712 {
00713 ScratchBuffer<u_char*, 256> key_buf;
00714 size_t key_buf_len = flatten(key, &key_buf);
00715 int err;
00716
00717
00718 DBTRef k(key_buf.buf(), key_buf_len);
00719
00720
00721
00722 if ((flags & DS_CREATE) == 0) {
00723 DBTRef d;
00724 err = db_->get(db_, NO_TX, k.dbt(), d.dbt(), 0);
00725 if (err == DB_NOTFOUND) {
00726 return DS_NOTFOUND;
00727 } else if (err != 0) {
00728 log_err("put -- DB internal error: %s", db_strerror(err));
00729 return DS_ERR;
00730 }
00731 }
00732
00733
00734 MarshalSize sizer(Serialize::CONTEXT_LOCAL);
00735 if (sizer.action(data) != 0) {
00736 log_err("error sizing data object");
00737 return DS_ERR;
00738 }
00739 size_t object_sz = sizer.size();
00740
00741
00742 size_t typecode_sz = 0;
00743 if (multitype_) {
00744 typecode_sz = MarshalSize::get_size(&typecode);
00745 }
00746
00747
00748
00749
00750
00751 log_debug("put: serializing %zu byte object (plus %zu byte typecode)",
00752 object_sz, typecode_sz);
00753
00754 ScratchBuffer<u_char*, 1024> scratch;
00755 u_char* buf = scratch.buf(typecode_sz + object_sz);
00756 DBTRef d(buf, typecode_sz + object_sz);
00757
00758
00759 if (multitype_)
00760 {
00761 Marshal typemarshal(Serialize::CONTEXT_LOCAL, buf, typecode_sz);
00762 UIntShim type_shim(typecode);
00763
00764 if (typemarshal.action(&type_shim) != 0) {
00765 log_err("error serializing type code");
00766 return DS_ERR;
00767 }
00768 }
00769
00770 Marshal m(Serialize::CONTEXT_LOCAL, buf + typecode_sz, object_sz);
00771 if (m.action(data) != 0) {
00772 log_err("error serializing data object");
00773 return DS_ERR;
00774 }
00775
00776 int db_flags = 0;
00777 if (flags & DS_EXCL) {
00778 db_flags |= DB_NOOVERWRITE;
00779 }
00780
00781 err = db_->put(db_, NO_TX, k.dbt(), d.dbt(), db_flags);
00782
00783 if (err == DB_KEYEXIST) {
00784 return DS_EXISTS;
00785 } else if (err != 0) {
00786 log_err("DB internal error: %s", db_strerror(err));
00787 return DS_ERR;
00788 }
00789
00790 return 0;
00791 }
00792
00793
00794 int
00795 BerkeleyDBTable::del(const SerializableObject& key)
00796 {
00797 u_char key_buf[256];
00798 size_t key_buf_len;
00799
00800 key_buf_len = flatten(key, key_buf, 256);
00801 if (key_buf_len == 0)
00802 {
00803 log_err("zero or too long key length");
00804 return DS_ERR;
00805 }
00806
00807 DBTRef k(key_buf, key_buf_len);
00808
00809 int err = db_->del(db_, NO_TX, k.dbt(), 0);
00810
00811 if (err == DB_NOTFOUND)
00812 {
00813 return DS_NOTFOUND;
00814 }
00815 else if (err != 0)
00816 {
00817 log_err("DB internal error: %s", db_strerror(err));
00818 return DS_ERR;
00819 }
00820
00821 return 0;
00822 }
00823
00824
00825 size_t
00826 BerkeleyDBTable::size() const
00827 {
00828 int err;
00829 int flags = 0;
00830
00831 union {
00832 void* ptr;
00833 struct __db_bt_stat* btree_stats;
00834 struct __db_h_stat* hash_stats;
00835 } stats;
00836
00837 stats.ptr = 0;
00838
00839 #if ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR == 2))
00840 err = db_->stat(db_, &stats.ptr, flags);
00841 #else
00842 err = db_->stat(db_, NO_TX, &stats.ptr, flags);
00843 #endif
00844 if (err != 0) {
00845 log_crit("error in DB::stat: %d", errno);
00846 ASSERT(stats.ptr == 0);
00847 return 0;
00848 }
00849
00850 ASSERT(stats.ptr != 0);
00851
00852 size_t ret;
00853
00854 switch(db_type_) {
00855 case DB_BTREE:
00856 ret = stats.btree_stats->bt_nkeys;
00857 break;
00858 case DB_HASH:
00859 ret = stats.hash_stats->hash_nkeys;
00860 break;
00861 default:
00862 PANIC("illegal value for db_type %d", db_type_);
00863 }
00864
00865 free(stats.ptr);
00866
00867 return ret;
00868 }
00869
00870
00871 DurableIterator*
00872 BerkeleyDBTable::itr()
00873 {
00874 return new BerkeleyDBIterator(this);
00875 }
00876
00877
00878 int
00879 BerkeleyDBTable::key_exists(const void* key, size_t key_len)
00880 {
00881 DBTRef k(const_cast<void*>(key), key_len);
00882 DBTRef d;
00883
00884 int err = db_->get(db_, NO_TX, k.dbt(), d.dbt(), 0);
00885 if (err == DB_NOTFOUND)
00886 {
00887 return DS_NOTFOUND;
00888 }
00889 else if (err != 0)
00890 {
00891 log_err("DB: %s", db_strerror(err));
00892 return DS_ERR;
00893 }
00894
00895 return 0;
00896 }
00897
00898
00899
00900
00901
00902
00903 BerkeleyDBIterator::BerkeleyDBIterator(BerkeleyDBTable* t)
00904 : Logger("BerkeleyDBIterator", "%s/iter", t->logpath()),
00905 cur_(0), valid_(false)
00906 {
00907 int err = t->db_->cursor(t->db_, NO_TX, &cur_, 0);
00908 if (err != 0) {
00909 log_err("DB: cannot create a DB iterator, err=%s", db_strerror(err));
00910 cur_ = 0;
00911 }
00912
00913 if (cur_)
00914 {
00915 valid_ = true;
00916 }
00917 }
00918
00919
00920 BerkeleyDBIterator::~BerkeleyDBIterator()
00921 {
00922 valid_ = false;
00923 if (cur_)
00924 {
00925 int err = cur_->c_close(cur_);
00926
00927 if (err != 0) {
00928 log_err("Unable to close cursor, %s", db_strerror(err));
00929 }
00930 }
00931 }
00932
00933
00934 int
00935 BerkeleyDBIterator::next()
00936 {
00937 ASSERT(valid_);
00938
00939 bzero(&key_, sizeof(key_));
00940 bzero(&data_, sizeof(data_));
00941
00942 int err = cur_->c_get(cur_, key_.dbt(), data_.dbt(), DB_NEXT);
00943
00944 if (err == DB_NOTFOUND) {
00945 valid_ = false;
00946 return DS_NOTFOUND;
00947 }
00948 else if (err != 0) {
00949 log_err("next() DB: %s", db_strerror(err));
00950 valid_ = false;
00951 return DS_ERR;
00952 }
00953
00954 return 0;
00955 }
00956
00957
00958 int
00959 BerkeleyDBIterator::get_key(SerializableObject* key)
00960 {
00961 ASSERT(key != NULL);
00962 oasys::Unmarshal un(oasys::Serialize::CONTEXT_LOCAL,
00963 static_cast<u_char*>(key_->data), key_->size);
00964
00965 if (un.action(key) != 0) {
00966 log_err("error unmarshalling");
00967 return DS_ERR;
00968 }
00969
00970 return 0;
00971 }
00972
00973
00974 int
00975 BerkeleyDBIterator::raw_key(void** key, size_t* len)
00976 {
00977 if (!valid_) return DS_ERR;
00978
00979 *key = key_->data;
00980 *len = key_->size;
00981
00982 return 0;
00983 }
00984
00985
00986 int
00987 BerkeleyDBIterator::raw_data(void** data, size_t* len)
00988 {
00989 if (!valid_) return DS_ERR;
00990
00991 *data = data_->data;
00992 *len = data_->size;
00993
00994 return 0;
00995 }
00996
00997 }