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