BerkeleyDBStore.cc

Go to the documentation of this file.
00001 /*
00002  * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By
00003  * downloading, copying, installing or using the software you agree to
00004  * this license. If you do not agree to this license, do not download,
00005  * install, copy or use the software.
00006  * 
00007  * Intel Open Source License 
00008  * 
00009  * Copyright (c) 2004 Intel Corporation. All rights reserved. 
00010  * 
00011  * Redistribution and use in source and binary forms, with or without
00012  * modification, are permitted provided that the following conditions are
00013  * met:
00014  * 
00015  *   Redistributions of source code must retain the above copyright
00016  *   notice, this list of conditions and the following disclaimer.
00017  * 
00018  *   Redistributions in binary form must reproduce the above copyright
00019  *   notice, this list of conditions and the following disclaimer in the
00020  *   documentation and/or other materials provided with the distribution.
00021  * 
00022  *   Neither the name of the Intel Corporation nor the names of its
00023  *   contributors may be used to endorse or promote products derived from
00024  *   this software without specific prior written permission.
00025  *  
00026  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00027  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00028  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00029  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
00030  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00031  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00032  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00033  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00034  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00035  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00036  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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  * BerkeleyDBStore
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     // XXX/bowei need to expose options if needed later
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  |    // create new files
00173         DB_PRIVATE      // only one process can access the db
00174         ;
00175 
00176     if (cfg.db_lockdetect_ != 0) { // locking
00177         dbenv_opts |= DB_INIT_LOCK | DB_THREAD;
00178     }
00179 
00180     if (cfg.db_mpool_) { // memory pool
00181         dbenv_opts |= DB_INIT_MPOOL;
00182     }
00183 
00184     if (cfg.db_log_) { // logging
00185         dbenv_opts |= DB_INIT_LOG;
00186     }
00187 
00188     if (cfg.db_txn_) { // transactions / recovery
00189         dbenv_opts |= DB_INIT_TXN | DB_RECOVER;
00190     }
00191     
00192     err = dbenv_->open(dbenv_, dbdir.c_str(), dbenv_opts, 0 /* default mode */);
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, // every operation is a tx
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     // grab a new database handle
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     // calculate the db type and creation flags
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 // XXX/demmer force type to be specified??
00278         {
00279             db_type = DB_BTREE;
00280         }
00281 
00282     } else {
00283         db_type = DB_UNKNOWN;
00284     }
00285 
00286     if (deadlock_timer_) {
00287         // locking is enabled
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         // unfortunately, we can't use the standard serialization stuff
00405         // for the metatable, because the string stored in the metatable are
00406         // not null-terminated
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         // XXX/bowei -- TODO
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  * BerkeleyDBTable
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     // Note: If we are to multithread access to the same table, this
00599     // will have potential concurrency problems, because close can
00600     // only happen if no other instance of Db is around.
00601     store_->release_table(name());
00602 
00603     log_debug("closing db %s", name());
00604     db_->close(db_, 0); // XXX/bowei - not sure about comment above
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     // flatten and fill in the key
00730     DBTRef k(key_buf.buf(), key_buf_len);
00731 
00732     // if the caller does not want to create new entries, first do a
00733     // db get to see if the key already exists
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     // figure out the size of the data
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     // and the size of the type code (if multitype)
00754     size_t typecode_sz = 0;
00755     if (multitype_) {
00756         typecode_sz = MarshalSize::get_size(&typecode);
00757     }
00758 
00759     // XXX/demmer -- one little optimization would be to pass the
00760     // calculated size out to the caller (the generic DurableTable),
00761     // so we don't have to re-calculate it in the object cache code
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     // if we're a multitype table, marshal the type code
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  * BerkeleyDBIterator
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 } // namespace oasys

Generated on Fri Dec 22 14:47:57 2006 for DTN Reference Implementation by  doxygen 1.5.1