LTP GCOV extension - code coverage report
Current view: directory - src/libstore - db.cc
Test: app.info
Date: 2004-12-21 Instrumented lines: 200
Code covered: 73.0 % Executed lines: 146

       1                 : #include <sys/types.h>
       2                 : #include <sys/stat.h>
       3                 : #include <fcntl.h>
       4                 : #include <errno.h>
       5                 : 
       6                 : #include <memory>
       7                 : 
       8                 : #include "db.hh"
       9                 : #include "util.hh"
      10                 : #include "pathlocks.hh"
      11                 : 
      12                 : 
      13                 : /* Wrapper class to ensure proper destruction. */
      14                 : class DestroyDbc 
      15                 : {
      16                 :     Dbc * dbc;
      17                 : public:
      18              61 :     DestroyDbc(Dbc * _dbc) : dbc(_dbc) { }
      19             112 :     ~DestroyDbc() { dbc->close(); /* close() frees dbc */ }
      20               0 : };
      21                 : 
      22                 : 
      23                 : static void rethrow(DbException & e)
      24               0 : {
      25               0 :     throw Error(e.what());
      26                 : }
      27                 : 
      28                 : 
      29                 : Transaction::Transaction()
      30            1122 :     : txn(0)
      31            1122 : {
      32                 : }
      33                 : 
      34                 : 
      35                 : Transaction::Transaction(Database & db)
      36              94 : {
      37              94 :     db.requireEnv();
      38              94 :     try {
      39              94 :         db.env->txn_begin(0, &txn, 0);
      40               0 :     } catch (DbException e) { rethrow(e); }
      41                 : }
      42                 : 
      43                 : 
      44                 : Transaction::~Transaction()
      45            2432 : {
      46            1216 :     if (txn) abort();
      47                 : }
      48                 : 
      49                 : 
      50                 : void Transaction::commit()
      51              94 : {
      52              94 :     if (!txn) throw Error("commit called on null transaction");
      53              94 :     debug(format("committing transaction %1%") % (void *) txn);
      54              94 :     DbTxn * txn2 = txn;
      55              94 :     txn = 0;
      56              94 :     try {
      57              94 :         txn2->commit(0);
      58               0 :     } catch (DbException e) { rethrow(e); }
      59                 : }
      60                 : 
      61                 : 
      62                 : void Transaction::abort()
      63               0 : {
      64               0 :     if (!txn) throw Error("abort called on null transaction");
      65               0 :     debug(format("aborting transaction %1%") % (void *) txn);
      66               0 :     DbTxn * txn2 = txn;
      67               0 :     txn = 0;
      68               0 :     try {
      69               0 :         txn2->abort();
      70               0 :     } catch (DbException e) { rethrow(e); }
      71                 : }
      72                 : 
      73                 : 
      74                 : void Transaction::moveTo(Transaction & t)
      75              34 : {
      76              34 :     if (t.txn) throw Error("target txn already exists");
      77              34 :     t.txn = txn;
      78              34 :     txn = 0;
      79                 : }
      80                 : 
      81                 : 
      82                 : void Database::requireEnv()
      83             289 : {
      84             289 :     checkInterrupt();
      85             289 :     if (!env)throw Error("database environment is not open "
      86                 :         "(maybe you don't have sufficient permission?)");
      87                 : }
      88                 : 
      89                 : 
      90                 : Db * Database::getDb(TableId table)
      91            1354 : {
      92            1354 :     if (table == 0)
      93               0 :         throw Error("database table is not open "
      94                 :             "(maybe you don't have sufficient permission?)");
      95            1354 :     map<TableId, Db *>::iterator i = tables.find(table);
      96            1354 :     if (i == tables.end())
      97               0 :         throw Error("unknown table id");
      98            1354 :     return i->second;
      99                 : }
     100                 : 
     101                 : 
     102                 : Database::Database()
     103              51 :     : env(0)
     104                 :     , nextId(1)
     105              51 : {
     106                 : }
     107                 : 
     108                 : 
     109                 : Database::~Database()
     110               0 : {
     111               0 :     close();
     112                 : }
     113                 : 
     114                 : 
     115                 : int getAccessorCount(int fd)
     116              39 : {
     117              39 :     if (lseek(fd, 0, SEEK_SET) == -1)
     118               0 :         throw SysError("seeking accessor count");
     119              39 :     char buf[128];
     120              39 :     int len;
     121              39 :     if ((len = read(fd, buf, sizeof(buf) - 1)) == -1)
     122               0 :         throw SysError("reading accessor count");
     123              39 :     buf[len] = 0;
     124              39 :     int count;
     125              39 :     if (sscanf(buf, "%d", &count) != 1) {
     126               1 :         debug(format("accessor count is invalid: `%1%'") % buf);
     127               1 :         return -1;
     128                 :     }
     129              38 :     return count;
     130                 : }
     131                 : 
     132                 : 
     133                 : void setAccessorCount(int fd, int n)
     134              39 : {
     135              39 :     if (lseek(fd, 0, SEEK_SET) == -1)
     136               0 :         throw SysError("seeking accessor count");
     137              39 :     string s = (format("%1%") % n).str();
     138              39 :     const char * s2 = s.c_str();
     139              39 :     if (write(fd, s2, strlen(s2)) != (ssize_t) strlen(s2) ||
     140                 :         ftruncate(fd, strlen(s2)) != 0)
     141               0 :         throw SysError("writing accessor count");
     142                 : }
     143                 : 
     144                 : 
     145                 : void openEnv(DbEnv * env, const string & path, u_int32_t flags)
     146              39 : {
     147              39 :     env->open(path.c_str(),
     148                 :         DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
     149                 :         DB_CREATE | flags,
     150                 :         0666);
     151                 : }
     152                 : 
     153                 : 
     154                 : static int my_fsync(int fd)
     155              15 : {
     156              15 :     return 0;
     157                 : }
     158                 : 
     159                 : 
     160                 : void Database::open(const string & path)
     161              39 : {
     162              39 :     if (env) throw Error(format("environment already open"));
     163                 : 
     164              39 :     try {
     165                 : 
     166              39 :         debug(format("opening database environment"));
     167                 : 
     168                 : 
     169                 :         /* Create the database environment object. */
     170              39 :         DbEnv * env = 0; /* !!! close on error */
     171              39 :         env = new DbEnv(0);
     172                 : 
     173                 :         /* Smaller log files. */
     174              39 :         env->set_lg_bsize(32 * 1024); /* default */
     175              39 :         env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
     176                 : 
     177                 :         /* Write the log, but don't sync.  This protects transactions
     178                 :            against application crashes, but if the system crashes,
     179                 :            some transactions may be undone.  An acceptable risk, I
     180                 :            think. */
     181              39 :         env->set_flags(DB_TXN_WRITE_NOSYNC | DB_LOG_AUTOREMOVE, 1);
     182                 : 
     183                 :         /* Increase the locking limits.  If you ever get `Dbc::get:
     184                 :            Cannot allocate memory' or similar, especially while
     185                 :            running `nix-store --verify', just increase the following
     186                 :            number, then run db_recover on the database to remove the
     187                 :            existing DB environment (since changes only take effect on
     188                 :            new environments). */
     189              39 :         env->set_lk_max_locks(4000);
     190              39 :         env->set_lk_max_lockers(4000);
     191              39 :         env->set_lk_max_objects(4000);
     192              39 :         env->set_lk_detect(DB_LOCK_DEFAULT);
     193                 : 
     194                 :         /* Dangerous, probably, but from the docs it *seems* that BDB
     195                 :            shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it
     196                 :            still fsync()s sometimes. */
     197              39 :         db_env_set_func_fsync(my_fsync);
     198                 :         
     199                 : 
     200                 :         /* The following code provides automatic recovery of the
     201                 :            database environment.  Recovery is necessary when a process
     202                 :            dies while it has the database open.  To detect this,
     203                 :            processes atomically increment a counter when they open the
     204                 :            database, and decrement it when they close it.  If we see
     205                 :            that counter is > 0 but no processes are accessing the
     206                 :            database---determined by attempting to obtain a write lock
     207                 :            on a lock file on which all accessors have a read lock---we
     208                 :            must run recovery.  Note that this also ensures that we
     209                 :            only run recovery when there are no other accessors (which
     210                 :            could cause database corruption). */
     211                 : 
     212                 :         /* !!! close fdAccessors / fdLock on exception */
     213                 : 
     214                 :         /* Open the accessor count file. */
     215              39 :         string accessorsPath = path + "/accessor_count";
     216              39 :         fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666);
     217              39 :         if (fdAccessors == -1)
     218               0 :             if (errno == EACCES)
     219               0 :                 throw DbNoPermission(
     220               0 :                     format("permission denied to database in `%1%'") % accessorsPath);
     221                 :             else
     222               0 :                 throw SysError(format("opening file `%1%'") % accessorsPath);
     223                 : 
     224                 :         /* Open the lock file. */
     225              39 :         string lockPath = path + "/access_lock";
     226              39 :         fdLock = ::open(lockPath.c_str(), O_RDWR | O_CREAT, 0666);
     227              39 :         if (fdLock == -1)
     228               0 :             throw SysError(format("opening lock file `%1%'") % lockPath);
     229                 : 
     230                 :         /* Try to acquire a write lock. */
     231              39 :         debug(format("attempting write lock on `%1%'") % lockPath);
     232              39 :         if (lockFile(fdLock, ltWrite, false)) { /* don't wait */
     233                 : 
     234              34 :             debug(format("write lock granted"));
     235                 : 
     236                 :             /* We have a write lock, which means that there are no
     237                 :                other readers or writers. */
     238                 : 
     239              34 :             int n = getAccessorCount(fdAccessors);
     240                 : 
     241              34 :             if (n != 0) {
     242               1 :                 printMsg(lvlTalkative,
     243                 :                     format("accessor count is %1%, running recovery") % n);
     244                 : 
     245                 :                 /* Open the environment after running recovery. */
     246               1 :                 openEnv(env, path, DB_RECOVER);
     247                 :             }
     248                 :             
     249                 :             else 
     250                 :                 /* Open the environment normally. */
     251              33 :                 openEnv(env, path, 0);
     252                 : 
     253              34 :             setAccessorCount(fdAccessors, 1);
     254                 : 
     255                 :             /* Downgrade to a read lock. */
     256              34 :             debug(format("downgrading to read lock on `%1%'") % lockPath);
     257              34 :             lockFile(fdLock, ltRead, true);
     258                 : 
     259                 :         } else {
     260                 :             /* There are other accessors. */ 
     261               5 :             debug(format("write lock refused"));
     262                 : 
     263                 :             /* Acquire a read lock. */
     264               5 :             debug(format("acquiring read lock on `%1%'") % lockPath);
     265               5 :             lockFile(fdLock, ltRead, true); /* wait indefinitely */
     266                 : 
     267                 :             /* Increment the accessor count. */
     268               5 :             lockFile(fdAccessors, ltWrite, true);
     269               5 :             int n = getAccessorCount(fdAccessors) + 1;
     270               5 :             setAccessorCount(fdAccessors, n);
     271               5 :             debug(format("incremented accessor count to %1%") % n);
     272               5 :             lockFile(fdAccessors, ltNone, true);
     273                 : 
     274                 :             /* Open the environment normally. */
     275               5 :             openEnv(env, path, 0);
     276                 :         }
     277                 : 
     278              39 :         this->env = env;
     279                 :         
     280               0 :     } catch (DbException e) { rethrow(e); }
     281                 : }
     282                 : 
     283                 : 
     284                 : void Database::close()
     285               0 : {
     286               0 :     if (!env) return;
     287                 : 
     288                 :     /* Close the database environment. */
     289               0 :     debug(format("closing database environment"));
     290                 : 
     291               0 :     try {
     292                 : 
     293               0 :         for (map<TableId, Db *>::iterator i = tables.begin();
     294                 :              i != tables.end(); i++)
     295                 :         {
     296               0 :             Db * db = i->second;
     297               0 :             db->close(DB_NOSYNC);
     298               0 :             delete db;
     299                 :         }
     300                 : 
     301                 :         /* Do a checkpoint every 128 kilobytes, or every 5 minutes. */
     302               0 :         env->txn_checkpoint(128, 5, 0);
     303                 :         
     304               0 :         env->close(0);
     305                 : 
     306               0 :     } catch (DbException e) { rethrow(e); }
     307                 : 
     308               0 :     delete env;
     309                 : 
     310                 :     /* Decrement the accessor count. */
     311               0 :     lockFile(fdAccessors, ltWrite, true);
     312               0 :     int n = getAccessorCount(fdAccessors) - 1;
     313               0 :     setAccessorCount(fdAccessors, n);
     314               0 :     debug(format("decremented accessor count to %1%") % n);
     315               0 :     lockFile(fdAccessors, ltNone, true);
     316                 : 
     317               0 :     ::close(fdAccessors);
     318               0 :     ::close(fdLock);
     319                 : }
     320                 : 
     321                 : 
     322                 : TableId Database::openTable(const string & tableName)
     323             195 : {
     324             195 :     requireEnv();
     325             195 :     TableId table = nextId++;
     326                 : 
     327             195 :     try {
     328                 : 
     329             195 :         Db * db = new Db(env, 0);
     330                 : 
     331             195 :         try {
     332             195 :             db->open(0, tableName.c_str(), 0, 
     333                 :                 DB_HASH, DB_CREATE | DB_AUTO_COMMIT, 0666);
     334               0 :         } catch (...) {
     335               0 :             delete db;
     336               0 :             throw;
     337                 :         }
     338                 : 
     339             195 :         tables[table] = db;
     340                 : 
     341               0 :     } catch (DbException e) { rethrow(e); }
     342                 : 
     343             195 :     return table;
     344                 : }
     345                 : 
     346                 : 
     347                 : bool Database::queryString(const Transaction & txn, TableId table, 
     348                 :     const string & key, string & data)
     349            1203 : {
     350            1203 :     checkInterrupt();
     351                 : 
     352            1203 :     try {
     353            1203 :         Db * db = getDb(table);
     354                 : 
     355            1203 :         Dbt kt((void *) key.c_str(), key.length());
     356            1203 :         Dbt dt;
     357                 : 
     358            1203 :         int err = db->get(txn.txn, &kt, &dt, 0);
     359            1203 :         if (err) return false;
     360                 : 
     361             748 :         if (!dt.get_data())
     362             587 :             data = "";
     363                 :         else
     364             161 :             data = string((char *) dt.get_data(), dt.get_size());
     365                 :     
     366               0 :     } catch (DbException e) { rethrow(e); }
     367                 : 
     368             748 :     return true;
     369                 : }
     370                 : 
     371                 : 
     372                 : bool Database::queryStrings(const Transaction & txn, TableId table, 
     373                 :     const string & key, Strings & data)
     374              52 : {
     375              52 :     string d;
     376              52 :     if (!queryString(txn, table, key, d))
     377              32 :         return false;
     378              20 :     data = unpackStrings(d);
     379              20 :     return true;
     380                 : }
     381                 : 
     382                 : 
     383                 : void Database::setString(const Transaction & txn, TableId table,
     384                 :     const string & key, const string & data)
     385             142 : {
     386             142 :     checkInterrupt();
     387             142 :     try {
     388             142 :         Db * db = getDb(table);
     389             142 :         Dbt kt((void *) key.c_str(), key.length());
     390             142 :         Dbt dt((void *) data.c_str(), data.length());
     391             142 :         db->put(txn.txn, &kt, &dt, 0);
     392               0 :     } catch (DbException e) { rethrow(e); }
     393                 : }
     394                 : 
     395                 : 
     396                 : void Database::setStrings(const Transaction & txn, TableId table,
     397                 :     const string & key, const Strings & data, bool deleteEmpty)
     398              39 : {
     399              39 :     if (deleteEmpty && data.size() == 0)
     400               2 :         delPair(txn, table, key);
     401                 :     else
     402              37 :         setString(txn, table, key, packStrings(data));
     403                 : }
     404                 : 
     405                 : 
     406                 : void Database::delPair(const Transaction & txn, TableId table,
     407                 :     const string & key)
     408               4 : {
     409               4 :     checkInterrupt();
     410               4 :     try {
     411               4 :         Db * db = getDb(table);
     412               4 :         Dbt kt((void *) key.c_str(), key.length());
     413               4 :         db->del(txn.txn, &kt, 0);
     414                 :         /* Non-existence of a pair with the given key is not an
     415                 :            error. */
     416               0 :     } catch (DbException e) { rethrow(e); }
     417                 : }
     418                 : 
     419                 : 
     420                 : void Database::enumTable(const Transaction & txn, TableId table,
     421                 :     Strings & keys)
     422               5 : {
     423               5 :     try {
     424               5 :         Db * db = getDb(table);
     425                 : 
     426               5 :         Dbc * dbc;
     427               5 :         db->cursor(txn.txn, &dbc, 0);
     428               5 :         DestroyDbc destroyDbc(dbc);
     429                 : 
     430               5 :         Dbt kt, dt;
     431             139 :         while (dbc->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND) {
     432             134 :             checkInterrupt();
     433             134 :             keys.push_back(
     434                 :                 string((char *) kt.get_data(), kt.get_size()));
     435                 :         }
     436                 : 
     437               0 :     } catch (DbException e) { rethrow(e); }
     438                 : }

Generated by: LTP GCOV extension version 1.1