about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-02-18T14·30+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-02-18T14·30+0000
commite0305bb7a8b24fe1ea8b36cc2e5fe77ab151ae2f (patch)
tree6b8e160ed4437f9c9ccdb34468c285183c63ca19
parenta053d2d8e53f2967c64ab2b204727e4c27f06c0e (diff)
* Some wrapper objects to ensure that SQLite objects are properly
  destroyed.

-rw-r--r--src/libstore/local-store.cc118
-rw-r--r--src/libstore/local-store.hh40
2 files changed, 109 insertions, 49 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 35391f3aaf77..870644c88ba9 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -18,6 +18,8 @@
 #include <errno.h>
 #include <stdio.h>
 
+#include <sqlite3.h>
+
 
 namespace nix {
 
@@ -32,6 +34,37 @@ public:
 };
 
 
+SQLite::~SQLite()
+{
+    try {
+        if (db && sqlite3_close(db) != SQLITE_OK)
+            throw SQLiteError(db, "closing database");
+    } catch (...) {
+        ignoreException();
+    }
+}
+
+
+void SQLiteStmt::create(sqlite3 * db, const string & s)
+{
+    assert(!stmt);
+    if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
+        throw SQLiteError(db, "creating statement");
+    this->db = db;
+}
+
+
+SQLiteStmt::~SQLiteStmt()
+{
+    try {
+        if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
+            throw SQLiteError(db, "finalizing statement");
+    } catch (...) {
+        ignoreException();
+    }
+}
+
+
 void checkStoreNotSymlink()
 {
     if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
@@ -52,7 +85,6 @@ void checkStoreNotSymlink()
 
 LocalStore::LocalStore()
 {
-    db = 0;
     substitutablePathsLoaded = false;
     
     schemaPath = nixDBPath + "/schema";
@@ -73,6 +105,8 @@ LocalStore::LocalStore()
   
     checkStoreNotSymlink();
 
+    /* Acquire the big fat lock in shared mode to make sure that no
+       schema upgrade is in progress. */
     try {
         Path globalLockPath = nixDBPath + "/big-lock";
         globalLock = openLockFile(globalLockPath.c_str(), true);
@@ -86,22 +120,34 @@ LocalStore::LocalStore()
         printMsg(lvlError, "waiting for the big Nix store lock...");
         lockFile(globalLock, ltRead, true);
     }
+
+    /* Open the Nix database. */
+    if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db.db,
+            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
+        throw Error("cannot open SQLite database");
+
+    if (sqlite3_busy_timeout(db, 60000) != SQLITE_OK)
+        throw SQLiteError(db, "setting timeout");
     
+    /* Check the current database schema and if necessary do an
+       upgrade. */
     int curSchema = getSchema();
     if (curSchema > nixSchemaVersion)
         throw Error(format("current Nix store schema is version %1%, but I only support %2%")
             % curSchema % nixSchemaVersion);
     if (curSchema == 0) { /* new store */
-        curSchema = nixSchemaVersion; 
+        curSchema = nixSchemaVersion;
+        initSchema();
         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
     }
-    if (curSchema == 1) throw Error("your Nix store is no longer supported");
-    if (curSchema < 5)
+    else if (curSchema == 1) throw Error("your Nix store is no longer supported");
+    else if (curSchema < 5)
         throw Error(
             "Your Nix store has a database in Berkeley DB format,\n"
             "which is no longer supported. To convert to the new format,\n"
             "please upgrade Nix to version 0.12 first.");
-    if (curSchema < 6) upgradeStore6();
+    else if (curSchema < 6) upgradeStore6();
+    else prepareStatements();
 
     doFsync = queryBoolSetting("fsync-metadata", false);
 }
@@ -112,9 +158,6 @@ LocalStore::~LocalStore()
     try {
         flushDelayedUpdates();
 
-        if (db && sqlite3_close(db) != SQLITE_OK)
-            throw SQLiteError(db, "closing database");
-
         foreach (RunningSubstituters::iterator, i, runningSubstituters) {
             i->second.to.close();
             i->second.from.close();
@@ -143,15 +186,19 @@ int LocalStore::getSchema()
 
 void LocalStore::initSchema()
 {
-    if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db,
-            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
-        throw Error("cannot open SQLite database");
-
-    if (sqlite3_busy_timeout(db, 60000) != SQLITE_OK)
-        throw SQLiteError(db, "sett");
-    
     if (sqlite3_exec(db, (const char *) schema, 0, 0, 0) != SQLITE_OK)
         throw SQLiteError(db, "initialising database schema");
+
+    prepareStatements();
+}
+
+
+void LocalStore::prepareStatements()
+{
+    stmtRegisterValidPath.create(db,
+        "insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);");
+    stmtAddReference.create(db,
+        "insert into Refs (referrer, reference) values (?, ?);");
 }
 
 
@@ -1220,33 +1267,28 @@ void LocalStore::upgradeStore6()
     if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
         throw SQLiteError(db, "running `begin' command");
 
-    sqlite3_stmt * registerStmt;
-    if (sqlite3_prepare_v2(db, "insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);",
-            -1, &registerStmt, 0) != SQLITE_OK)
-        throw SQLiteError(db, "creating statement");
-    
     std::map<Path, sqlite3_int64> pathToId;
     
     foreach (PathSet::iterator, i, validPaths) {
         ValidPathInfo info = queryPathInfo(*i, true);
         
-        if (sqlite3_reset(registerStmt) != SQLITE_OK)
+        if (sqlite3_reset(stmtRegisterValidPath) != SQLITE_OK)
             throw SQLiteError(db, "resetting statement");
-        if (sqlite3_bind_text(registerStmt, 1, i->c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+        if (sqlite3_bind_text(stmtRegisterValidPath, 1, i->c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
             throw SQLiteError(db, "binding argument 1");
         string h = "sha256:" + printHash(info.hash);
-        if (sqlite3_bind_text(registerStmt, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+        if (sqlite3_bind_text(stmtRegisterValidPath, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
             throw SQLiteError(db, "binding argument 2");
-        if (sqlite3_bind_int(registerStmt, 3, info.registrationTime) != SQLITE_OK)
+        if (sqlite3_bind_int(stmtRegisterValidPath, 3, info.registrationTime) != SQLITE_OK)
             throw SQLiteError(db, "binding argument 3");
         if (info.deriver != "") {
-            if (sqlite3_bind_text(registerStmt, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+            if (sqlite3_bind_text(stmtRegisterValidPath, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
                 throw SQLiteError(db, "binding argument 4");
         } else {
-            if (sqlite3_bind_null(registerStmt, 4) != SQLITE_OK)
+            if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK)
                 throw SQLiteError(db, "binding argument 4");
         }
-        if (sqlite3_step(registerStmt) != SQLITE_DONE)
+        if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
             throw SQLiteError(db, "registering valid path in database");
 
         pathToId[*i] = sqlite3_last_insert_rowid(db);
@@ -1254,29 +1296,21 @@ void LocalStore::upgradeStore6()
         std::cerr << ".";
     }
 
-    if (sqlite3_finalize(registerStmt) != SQLITE_OK)
-        throw SQLiteError(db, "finalizing statement");
-
     std::cerr << "|";
     
-    sqlite3_stmt * addRefStmt;
-    if (sqlite3_prepare_v2(db, "insert into Refs (referrer, reference) values (?, ?);",
-            -1, &addRefStmt, 0) != SQLITE_OK)
-        throw SQLiteError(db, "creating statement");
-    
     foreach (PathSet::iterator, i, validPaths) {
         ValidPathInfo info = queryPathInfo(*i, true);
         
         foreach (PathSet::iterator, j, info.references) {
-            if (sqlite3_reset(addRefStmt) != SQLITE_OK)
+            if (sqlite3_reset(stmtAddReference) != SQLITE_OK)
                 throw SQLiteError(db, "resetting statement");
-            if (sqlite3_bind_int(addRefStmt, 1, pathToId[*i]) != SQLITE_OK)
+            if (sqlite3_bind_int(stmtAddReference, 1, pathToId[*i]) != SQLITE_OK)
                 throw SQLiteError(db, "binding argument 1");
             if (pathToId.find(*j) == pathToId.end())
                 throw Error(format("path `%1%' referenced by `%2%' is invalid") % *j % *i);
-            if (sqlite3_bind_int(addRefStmt, 2, pathToId[*j]) != SQLITE_OK)
+            if (sqlite3_bind_int(stmtAddReference, 2, pathToId[*j]) != SQLITE_OK)
                 throw SQLiteError(db, "binding argument 2");
-            if (sqlite3_step(addRefStmt) != SQLITE_DONE)
+            if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
                 throw SQLiteError(db, "adding reference to database");
         }
 
@@ -1287,12 +1321,6 @@ void LocalStore::upgradeStore6()
 
     if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
         throw SQLiteError(db, "running `commit' command");
-    
-    if (sqlite3_finalize(addRefStmt) != SQLITE_OK)
-        throw SQLiteError(db, "finalizing statement");
-
-    throw Error("foo");
-
     writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
 
     lockFile(globalLock, ltRead, true);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index f2956568002f..3637a5d07cd1 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -6,7 +6,9 @@
 #include "store-api.hh"
 #include "util.hh"
 
-#include <sqlite3.h>
+
+class sqlite3;
+class sqlite3_stmt;
 
 
 namespace nix {
@@ -14,8 +16,9 @@ namespace nix {
 
 /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10.
-   Version 4 is Nix 0.11.  Version 5 is Nix 0.12*/
-const int nixSchemaVersion = 5;
+   Version 4 is Nix 0.11.  Version 5 is Nix 0.12-0.14.  Version 6 is
+   Nix 0.15. */
+const int nixSchemaVersion = 6;
 
 
 extern string drvsLogDir;
@@ -43,6 +46,28 @@ struct RunningSubstituter
 };
 
 
+/* Wrapper object to close the SQLite database automatically. */
+struct SQLite
+{
+    sqlite3 * db;
+    SQLite() { db = 0; }
+    ~SQLite();
+    operator sqlite3 * () { return db; }
+};
+
+
+/* Wrapper object to create and destroy SQLite prepared statements. */
+struct SQLiteStmt
+{
+    sqlite3 * db;
+    sqlite3_stmt * stmt;
+    SQLiteStmt() { stmt = 0; }
+    void create(sqlite3 * db, const string & s);
+    ~SQLiteStmt();
+    operator sqlite3_stmt * () { return stmt; }
+};
+    
+
 class LocalStore : public StoreAPI
 {
 private:
@@ -163,12 +188,19 @@ private:
     /* Whether to do an fsync() after writing Nix metadata. */
     bool doFsync;
 
-    sqlite3 * db;
+    /* The SQLite database object. */
+    SQLite db;
+
+    /* Some precompiled SQLite statements. */
+    SQLiteStmt stmtRegisterValidPath;
+    SQLiteStmt stmtAddReference;
 
     int getSchema();
 
     void initSchema();
 
+    void prepareStatements();
+
     void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
 
     ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false);