BerkeleyDBStore.cc

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2004-2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
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  * BerkeleyDBStore
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     // XXX/bowei need to expose options if needed later
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  |    // create new files
00152         DB_PRIVATE      // only one process can access the db
00153         ;
00154 
00155     if (cfg.db_lockdetect_ != 0) { // locking
00156         dbenv_opts |= DB_INIT_LOCK | DB_THREAD;
00157     }
00158 
00159     if (cfg.db_mpool_) { // memory pool
00160         dbenv_opts |= DB_INIT_MPOOL;
00161     }
00162 
00163     if (cfg.db_log_) { // logging
00164         dbenv_opts |= DB_INIT_LOG;
00165     }
00166 
00167     if (cfg.db_txn_) { // transactions / recovery
00168         dbenv_opts |= DB_INIT_TXN | DB_RECOVER;
00169     }
00170     
00171     err = dbenv_->open(dbenv_, dbdir.c_str(), dbenv_opts, 0 /* default mode */);
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, // every operation is a tx
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     // grab a new database handle
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     // calculate the db type and creation flags
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 // XXX/demmer force type to be specified??
00257         {
00258             db_type = DB_BTREE;
00259         }
00260 
00261     } else {
00262         db_type = DB_UNKNOWN;
00263     }
00264 
00265     if (deadlock_timer_) {
00266         // locking is enabled
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         // unfortunately, we can't use the standard serialization stuff
00384         // for the metatable, because the string stored in the metatable are
00385         // not null-terminated
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         // XXX/bowei -- TODO
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  * BerkeleyDBTable
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     // Note: If we are to multithread access to the same table, this
00587     // will have potential concurrency problems, because close can
00588     // only happen if no other instance of Db is around.
00589     store_->release_table(name());
00590 
00591     log_debug("closing db %s", name());
00592     db_->close(db_, 0); // XXX/bowei - not sure about comment above
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     // flatten and fill in the key
00718     DBTRef k(key_buf.buf(), key_buf_len);
00719 
00720     // if the caller does not want to create new entries, first do a
00721     // db get to see if the key already exists
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     // figure out the size of the data
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     // and the size of the type code (if multitype)
00742     size_t typecode_sz = 0;
00743     if (multitype_) {
00744         typecode_sz = MarshalSize::get_size(&typecode);
00745     }
00746 
00747     // XXX/demmer -- one little optimization would be to pass the
00748     // calculated size out to the caller (the generic DurableTable),
00749     // so we don't have to re-calculate it in the object cache code
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     // if we're a multitype table, marshal the type code
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  * BerkeleyDBIterator
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 } // namespace oasys

Generated on Sat Sep 8 08:43:24 2007 for DTN Reference Implementation by  doxygen 1.5.3