LTP GCOV extension - code coverage report
Current view: directory - src/libstore - db.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 196
Code covered: 76.0 % Executed lines: 149

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

Generated by: LTP GCOV extension version 1.1