LTP GCOV extension - code coverage report
Current view: directory - src/libstore - db.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 220
Code covered: 0.9 % Executed lines: 2

       1                 : #include "config.h"
       2                 : 
       3                 : #ifdef OLD_DB_COMPAT
       4                 : 
       5                 : #include "db.hh"
       6                 : #include "util.hh"
       7                 : #include "pathlocks.hh"
       8                 : 
       9                 : #include <sys/types.h>
      10                 : #include <sys/stat.h>
      11                 : #include <fcntl.h>
      12                 : #include <errno.h>
      13                 : 
      14                 : #include <memory>
      15                 : 
      16                 : #include <db_cxx.h>
      17                 : 
      18                 : 
      19                 : namespace nix {
      20                 : 
      21                 : 
      22                 : /* Wrapper class to ensure proper destruction. */
      23                 : class DestroyDbc 
      24                 : {
      25                 :     Dbc * dbc;
      26                 : public:
      27               0 :     DestroyDbc(Dbc * _dbc) : dbc(_dbc) { }
      28               0 :     ~DestroyDbc() { dbc->close(); /* close() frees dbc */ }
      29                 : };
      30                 : 
      31                 : 
      32                 : class DestroyDbEnv
      33                 : {
      34                 :     DbEnv * dbenv;
      35                 : public:
      36               0 :     DestroyDbEnv(DbEnv * _dbenv) : dbenv(_dbenv) { }
      37               0 :     ~DestroyDbEnv() {
      38               0 :         if (dbenv) {
      39               0 :             if (dbenv->get_DB_ENV()) dbenv->close(0);
      40               0 :             delete dbenv;
      41                 :         }
      42               0 :     }
      43               0 :     void release() { dbenv = 0; };
      44                 : };
      45                 : 
      46                 : 
      47               0 : static void rethrow(DbException & e)
      48                 : {
      49               0 :     throw Error(e.what());
      50                 : }
      51                 : 
      52                 : 
      53               0 : Transaction::Transaction()
      54               0 :     : txn(0)
      55                 : {
      56               0 : }
      57                 : 
      58                 : 
      59               0 : Transaction::Transaction(Database & db)
      60               0 :     : txn(0)
      61                 : {
      62               0 :     begin(db);
      63               0 : }
      64                 : 
      65                 : 
      66               0 : Transaction::~Transaction()
      67                 : {
      68               0 :     if (txn) abort();
      69               0 : }
      70                 : 
      71                 : 
      72               0 : void Transaction::begin(Database & db)
      73                 : {
      74               0 :     assert(txn == 0);
      75               0 :     db.requireEnv();
      76                 :     try {
      77               0 :         db.env->txn_begin(0, &txn, 0);
      78               0 :     } catch (DbException e) { rethrow(e); }
      79               0 : }
      80                 : 
      81                 : 
      82               0 : void Transaction::commit()
      83                 : {
      84               0 :     if (!txn) throw Error("commit called on null transaction");
      85               0 :     debug(format("committing transaction %1%") % (void *) txn);
      86               0 :     DbTxn * txn2 = txn;
      87               0 :     txn = 0;
      88                 :     try {
      89               0 :         txn2->commit(0);
      90               0 :     } catch (DbException e) { rethrow(e); }
      91               0 : }
      92                 : 
      93                 : 
      94               0 : void Transaction::abort()
      95                 : {
      96               0 :     if (!txn) throw Error("abort called on null transaction");
      97               0 :     debug(format("aborting transaction %1%") % (void *) txn);
      98               0 :     DbTxn * txn2 = txn;
      99               0 :     txn = 0;
     100                 :     try {
     101               0 :         txn2->abort();
     102               0 :     } catch (DbException e) { rethrow(e); }
     103               0 : }
     104                 : 
     105                 : 
     106               0 : void Transaction::moveTo(Transaction & t)
     107                 : {
     108               0 :     if (t.txn) throw Error("target txn already exists");
     109               0 :     t.txn = txn;
     110               0 :     txn = 0;
     111               0 : }
     112                 : 
     113                 : 
     114               0 : void Database::requireEnv()
     115                 : {
     116               0 :     checkInterrupt();
     117               0 :     if (!env) throw Error("database environment is not open "
     118               0 :         "(maybe you don't have sufficient permission?)");
     119               0 : }
     120                 : 
     121                 : 
     122               0 : Db * Database::getDb(TableId table)
     123                 : {
     124               0 :     if (table == 0)
     125                 :         throw Error("database table is not open "
     126               0 :             "(maybe you don't have sufficient permission?)");
     127               0 :     std::map<TableId, Db *>::iterator i = tables.find(table);
     128               0 :     if (i == tables.end())
     129               0 :         throw Error("unknown table id");
     130               0 :     return i->second;
     131                 : }
     132                 : 
     133                 : 
     134               0 : Database::Database()
     135                 :     : env(0)
     136               0 :     , nextId(1)
     137                 : {
     138               0 : }
     139                 : 
     140                 : 
     141               0 : Database::~Database()
     142                 : {
     143               0 :     close();
     144               0 : }
     145                 : 
     146                 : 
     147               0 : void openEnv(DbEnv * & env, const string & path, u_int32_t flags)
     148                 : {
     149                 :     try {
     150               0 :         createDirs(path);
     151               0 :     } catch (SysError & e) {
     152               0 :         if (e.errNo == EPERM || e.errNo == EACCES)
     153               0 :             throw DbNoPermission(format("cannot create the Nix database in `%1%'") % path);
     154                 :         else
     155               0 :             throw;
     156                 :     }
     157                 :         
     158                 :     try {
     159                 :         env->open(path.c_str(),
     160                 :             DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
     161                 :             DB_CREATE | flags,
     162               0 :             0666);
     163               0 :     } catch (DbException & e) {
     164               0 :         printMsg(lvlError, format("environment open failed: %1%") % e.what());
     165               0 :         throw;
     166                 :     }
     167               0 : }
     168                 : 
     169                 : 
     170               0 : static int my_fsync(int fd)
     171                 : {
     172               0 :     return 0;
     173                 : }
     174                 : 
     175                 : 
     176               0 : static void errorPrinter(const DbEnv * env, const char * errpfx, const char * msg)
     177                 : {
     178               0 :     printMsg(lvlError, format("Berkeley DB error: %1%") % msg);
     179               0 : }
     180                 : 
     181                 : 
     182               0 : static void messagePrinter(const DbEnv * env, const char * msg)
     183                 : {
     184               0 :     printMsg(lvlError, format("Berkeley DB message: %1%") % msg);
     185               0 : }
     186                 : 
     187                 : 
     188               0 : void Database::open2(const string & path, bool removeOldEnv)
     189                 : {
     190               0 :     if (env) throw Error(format("environment already open"));
     191                 : 
     192               0 :     debug(format("opening database environment"));
     193                 : 
     194                 : 
     195                 :     /* Create the database environment object. */
     196               0 :     DbEnv * env = new DbEnv(0);
     197               0 :     DestroyDbEnv deleteEnv(env);
     198                 : 
     199               0 :     env->set_errcall(errorPrinter);
     200               0 :     env->set_msgcall(messagePrinter);
     201               0 :     if (getEnv("NIX_DEBUG_DB_REGISTER") == "1")
     202               0 :         env->set_verbose(DB_VERB_REGISTER, 1);
     203               0 :     env->set_verbose(DB_VERB_RECOVERY, 1);
     204                 :     
     205                 :     /* Smaller log files. */
     206               0 :     env->set_lg_bsize(32 * 1024); /* default */
     207               0 :     env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
     208                 :     
     209                 :     /* Write the log, but don't sync.  This protects transactions
     210                 :        against application crashes, but if the system crashes, some
     211                 :        transactions may be undone.  An acceptable risk, I think. */
     212               0 :     env->set_flags(DB_TXN_WRITE_NOSYNC | DB_LOG_AUTOREMOVE, 1);
     213                 :     
     214                 :     /* Increase the locking limits.  If you ever get `Dbc::get: Cannot
     215                 :        allocate memory' or similar, especially while running
     216                 :        `nix-store --verify', just increase the following number, then
     217                 :        run db_recover on the database to remove the existing DB
     218                 :        environment (since changes only take effect on new
     219                 :        environments). */
     220               0 :     env->set_lk_max_locks(10000);
     221               0 :     env->set_lk_max_lockers(10000);
     222               0 :     env->set_lk_max_objects(10000);
     223               0 :     env->set_lk_detect(DB_LOCK_DEFAULT);
     224                 :     
     225                 :     /* Dangerous, probably, but from the docs it *seems* that BDB
     226                 :        shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it still
     227                 :        fsync()s sometimes. */
     228               0 :     db_env_set_func_fsync(my_fsync);
     229                 : 
     230                 :     
     231               0 :     if (removeOldEnv) {
     232               0 :         printMsg(lvlError, "removing old Berkeley DB database environment...");
     233               0 :         env->remove(path.c_str(), DB_FORCE);
     234               0 :         return;
     235                 :     }
     236                 : 
     237               0 :     openEnv(env, path, DB_REGISTER | DB_RECOVER);
     238                 : 
     239               0 :     deleteEnv.release();
     240               0 :     this->env = env;
     241                 : }
     242                 : 
     243                 : 
     244               0 : void Database::open(const string & path)
     245                 : {
     246                 :     try {
     247                 : 
     248               0 :         open2(path, false);
     249                 :         
     250               0 :     } catch (DbException e) {
     251                 :         
     252               0 :         if (e.get_errno() == DB_VERSION_MISMATCH) {
     253                 :             /* Remove the environment while we are holding the global
     254                 :                lock.  If things go wrong there, we bail out.
     255                 :                !!! argh, we abolished the global lock :-( */
     256               0 :             open2(path, true);
     257                 : 
     258                 :             /* Try again. */
     259               0 :             open2(path, false);
     260                 : 
     261                 :             /* Force a checkpoint, as per the BDB docs. */
     262               0 :             env->txn_checkpoint(DB_FORCE, 0, 0);
     263                 : 
     264               0 :             printMsg(lvlError, "database succesfully upgraded to new version");
     265                 :         }
     266                 : 
     267                 : #if 0        
     268                 :         else if (e.get_errno() == DB_RUNRECOVERY) {
     269                 :             /* If recovery is needed, do it. */
     270                 :             printMsg(lvlError, "running recovery...");
     271                 :             open2(path, false, true);
     272                 :         }
     273                 : #endif        
     274                 :         
     275                 :         else
     276               0 :             rethrow(e);
     277                 :     }
     278               0 : }
     279                 : 
     280                 : 
     281               0 : void Database::close()
     282                 : {
     283               0 :     if (!env) return;
     284                 : 
     285                 :     /* Close the database environment. */
     286               0 :     debug(format("closing database environment"));
     287                 : 
     288                 :     try {
     289                 : 
     290               0 :         for (std::map<TableId, Db *>::iterator i = tables.begin();
     291                 :              i != tables.end(); )
     292                 :         {
     293               0 :             std::map<TableId, Db *>::iterator j = i;
     294               0 :             ++j;
     295               0 :             closeTable(i->first);
     296               0 :             i = j;
     297                 :         }
     298                 : 
     299                 :         /* Do a checkpoint every 128 kilobytes, or every 5 minutes. */
     300               0 :         env->txn_checkpoint(128, 5, 0);
     301                 :         
     302               0 :         env->close(0);
     303                 : 
     304               0 :     } catch (DbException e) { rethrow(e); }
     305                 : 
     306               0 :     delete env;
     307                 : 
     308               0 :     env = 0;
     309                 : }
     310                 : 
     311                 : 
     312               0 : TableId Database::openTable(const string & tableName, bool sorted)
     313                 : {
     314               0 :     requireEnv();
     315               0 :     TableId table = nextId++;
     316                 : 
     317                 :     try {
     318                 : 
     319               0 :         Db * db = new Db(env, 0);
     320                 : 
     321                 :         try {
     322                 :             db->open(0, tableName.c_str(), 0, 
     323                 :                 sorted ? DB_BTREE : DB_HASH,
     324               0 :                 DB_CREATE | DB_AUTO_COMMIT, 0666);
     325               0 :         } catch (...) {
     326               0 :             delete db;
     327               0 :             throw;
     328                 :         }
     329                 : 
     330               0 :         tables[table] = db;
     331                 : 
     332               0 :     } catch (DbException e) { rethrow(e); }
     333                 : 
     334               0 :     return table;
     335                 : }
     336                 : 
     337                 : 
     338               0 : void Database::closeTable(TableId table)
     339                 : {
     340                 :     try {
     341               0 :         Db * db = getDb(table);
     342               0 :         db->close(DB_NOSYNC);
     343               0 :         delete db;
     344               0 :         tables.erase(table);
     345               0 :     } catch (DbException e) { rethrow(e); }
     346               0 : }
     347                 : 
     348                 : 
     349               0 : void Database::deleteTable(const string & table)
     350                 : {
     351                 :     try {
     352               0 :         env->dbremove(0, table.c_str(), 0, DB_AUTO_COMMIT);
     353               0 :     } catch (DbException e) { rethrow(e); }
     354               0 : }
     355                 : 
     356                 : 
     357                 : bool Database::queryString(const Transaction & txn, TableId table, 
     358               0 :     const string & key, string & data)
     359                 : {
     360               0 :     checkInterrupt();
     361                 : 
     362                 :     try {
     363               0 :         Db * db = getDb(table);
     364                 : 
     365               0 :         Dbt kt((void *) key.c_str(), key.length());
     366               0 :         Dbt dt;
     367                 : 
     368               0 :         int err = db->get(txn.txn, &kt, &dt, 0);
     369               0 :         if (err) return false;
     370                 : 
     371               0 :         if (!dt.get_data())
     372               0 :             data = "";
     373                 :         else
     374               0 :             data = string((char *) dt.get_data(), dt.get_size());
     375                 :     
     376               0 :     } catch (DbException e) { rethrow(e); }
     377                 : 
     378               0 :     return true;
     379                 : }
     380                 : 
     381                 : 
     382                 : bool Database::queryStrings(const Transaction & txn, TableId table, 
     383               0 :     const string & key, Strings & data)
     384                 : {
     385               0 :     string d;
     386               0 :     if (!queryString(txn, table, key, d))
     387               0 :         return false;
     388               0 :     data = unpackStrings(d);
     389               0 :     return true;
     390                 : }
     391                 : 
     392                 : 
     393                 : void Database::setString(const Transaction & txn, TableId table,
     394               0 :     const string & key, const string & data)
     395                 : {
     396               0 :     checkInterrupt();
     397                 :     try {
     398               0 :         Db * db = getDb(table);
     399               0 :         Dbt kt((void *) key.c_str(), key.length());
     400               0 :         Dbt dt((void *) data.c_str(), data.length());
     401               0 :         db->put(txn.txn, &kt, &dt, 0);
     402               0 :     } catch (DbException e) { rethrow(e); }
     403               0 : }
     404                 : 
     405                 : 
     406                 : void Database::setStrings(const Transaction & txn, TableId table,
     407               0 :     const string & key, const Strings & data, bool deleteEmpty)
     408                 : {
     409               0 :     if (deleteEmpty && data.size() == 0)
     410               0 :         delPair(txn, table, key);
     411                 :     else
     412               0 :         setString(txn, table, key, packStrings(data));
     413               0 : }
     414                 : 
     415                 : 
     416                 : void Database::delPair(const Transaction & txn, TableId table,
     417               0 :     const string & key)
     418                 : {
     419               0 :     checkInterrupt();
     420                 :     try {
     421               0 :         Db * db = getDb(table);
     422               0 :         Dbt kt((void *) key.c_str(), key.length());
     423               0 :         db->del(txn.txn, &kt, 0);
     424                 :         /* Non-existence of a pair with the given key is not an
     425                 :            error. */
     426               0 :     } catch (DbException e) { rethrow(e); }
     427               0 : }
     428                 : 
     429                 : 
     430                 : void Database::enumTable(const Transaction & txn, TableId table,
     431               0 :     Strings & keys, const string & keyPrefix)
     432                 : {
     433                 :     try {
     434               0 :         Db * db = getDb(table);
     435                 : 
     436                 :         Dbc * dbc;
     437               0 :         db->cursor(txn.txn, &dbc, 0);
     438               0 :         DestroyDbc destroyDbc(dbc);
     439                 : 
     440               0 :         Dbt kt, dt;
     441               0 :         u_int32_t flags = DB_NEXT;
     442                 : 
     443               0 :         if (!keyPrefix.empty()) {
     444               0 :             flags = DB_SET_RANGE;
     445               0 :             kt = Dbt((void *) keyPrefix.c_str(), keyPrefix.size());
     446                 :         }
     447                 : 
     448               0 :         while (dbc->get(&kt, &dt, flags) != DB_NOTFOUND) {
     449               0 :             checkInterrupt();
     450               0 :             string data((char *) kt.get_data(), kt.get_size());
     451               0 :             if (!keyPrefix.empty() &&
     452                 :                 string(data, 0, keyPrefix.size()) != keyPrefix)
     453               0 :                 break;
     454               0 :             keys.push_back(data);
     455               0 :             flags = DB_NEXT;
     456               0 :         }
     457                 : 
     458               0 :     } catch (DbException e) { rethrow(e); }
     459               0 : }
     460                 : 
     461                 :  
     462               0 : void Database::clearTable(const Transaction & txn, TableId table)
     463                 : {
     464                 :     try {
     465               0 :         Db * db = getDb(table);
     466                 :         u_int32_t count;
     467               0 :         db->truncate(txn.txn, &count, 0);
     468               0 :     } catch (DbException e) { rethrow(e); }
     469               0 : }
     470                 : 
     471               0 : 
     472            1106 : }
     473             553 : 
     474                 : #endif

Generated by: LTP GCOV extension version 1.6