about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/local-store.cc201
-rw-r--r--src/libstore/local-store.hh6
-rw-r--r--src/libstore/store-api.hh1
3 files changed, 96 insertions, 112 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 7d6fc5e33d65..af240b61cbe5 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -74,6 +74,24 @@ SQLiteStmt::~SQLiteStmt()
 }
 
 
+/* Helper class to ensure that prepared statements are reset when
+   leaving the scope that uses them.  Unfinished prepared statements
+   prevent transactions from being aborted, and can cause locks to be
+   kept when they should be released. */
+struct SQLiteStmtUse
+{
+    SQLiteStmt & stmt;
+    SQLiteStmtUse(SQLiteStmt & stmt) : stmt(stmt)
+    {
+        stmt.reset();
+    }
+    ~SQLiteStmtUse()
+    {
+        stmt.reset();
+    }
+};
+
+
 struct SQLiteTxn 
 {
     bool active;
@@ -223,10 +241,10 @@ int LocalStore::getSchema()
 }
 
 
-#include "schema.sql.hh"
-
 void LocalStore::initSchema()
 {
+#include "schema.sql.hh"
+
     if (sqlite3_exec(db, (const char *) schema, 0, 0, 0) != SQLITE_OK)
         throw SQLiteError(db, "initialising database schema");
 
@@ -237,9 +255,9 @@ void LocalStore::initSchema()
 void LocalStore::prepareStatements()
 {
     stmtRegisterValidPath.create(db,
-        "insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);");
+        "insert or replace into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);");
     stmtAddReference.create(db,
-        "insert into Refs (referrer, reference) values (?, ?);");
+        "insert or replace into Refs (referrer, reference) values (?, ?);");
     stmtQueryPathInfo.create(db,
         "select id, hash, registrationTime, deriver from ValidPaths where path = ?;");
     stmtQueryReferences.create(db,
@@ -336,75 +354,69 @@ void LocalStore::registerValidPath(const Path & path,
 }
 
 
-void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidity)
+unsigned long long LocalStore::addValidPath(const ValidPathInfo & info)
 {
-    abort();
-#if 0    
-    Path infoFile = infoFileFor(info.path);
-
-    ValidPathInfo oldInfo;
-    if (pathExists(infoFile)) oldInfo = queryPathInfo(info.path);
-
-    /* Note that it's possible for infoFile to already exist. */
-
-    /* Acquire a lock on each referrer file.  This prevents those
-       paths from being invalidated.  (It would be a violation of the
-       store invariants if we registered info.path as valid while some
-       of its references are invalid.)  NB: there can be no deadlock
-       here since we're acquiring the locks in sorted order. */
-    PathSet lockNames;
-    foreach (PathSet::const_iterator, i, info.references)
-        if (*i != info.path) lockNames.insert(referrersFileFor(*i));
-    PathLocks referrerLocks(lockNames);
-    referrerLocks.setDeletion(true);
-        
-    string refs;
-    foreach (PathSet::const_iterator, i, info.references) {
-        if (!refs.empty()) refs += " ";
-        refs += *i;
-
-        if (!ignoreValidity && *i != info.path && !isValidPath(*i))
-            throw Error(format("cannot register `%1%' as valid, because its reference `%2%' isn't valid")
-                % info.path % *i);
-
-        /* Update the referrer mapping for *i.  This must be done
-           before the info file is written to maintain the invariant
-           that if `path' is a valid path, then all its references
-           have referrer mappings back to `path'.  A " " is prefixed
-           to separate it from the previous entry.  It's not suffixed
-           to deal with interrupted partial writes to this file. */
-        if (oldInfo.references.find(*i) == oldInfo.references.end())
-            appendReferrer(*i, info.path, false);
+    SQLiteStmtUse use(stmtRegisterValidPath);
+    if (sqlite3_bind_text(stmtRegisterValidPath, 1, info.path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+        throw SQLiteError(db, "binding argument");
+    string h = "sha256:" + printHash(info.hash);
+    if (sqlite3_bind_text(stmtRegisterValidPath, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+        throw SQLiteError(db, "binding argument");
+    if (sqlite3_bind_int(stmtRegisterValidPath, 3, info.registrationTime) != SQLITE_OK)
+        throw SQLiteError(db, "binding argument");
+    if (info.deriver != "") {
+        if (sqlite3_bind_text(stmtRegisterValidPath, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+            throw SQLiteError(db, "binding argument");
+    } else {
+        if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK)
+            throw SQLiteError(db, "binding argument");
     }
+    if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
+        throw SQLiteError(db, format("registering valid path `%1%' in database") % info.path);
+    return sqlite3_last_insert_rowid(db);
+}
+
 
+void LocalStore::addReference(unsigned long long referrer, unsigned long long reference)
+{
+    SQLiteStmtUse use(stmtAddReference);
+    if (sqlite3_bind_int(stmtAddReference, 1, referrer) != SQLITE_OK)
+        throw SQLiteError(db, "binding argument");
+    if (sqlite3_bind_int(stmtAddReference, 2, reference) != SQLITE_OK)
+        throw SQLiteError(db, "binding argument");
+    if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
+        throw SQLiteError(db, "adding reference to database");
+}
+
+
+void LocalStore::registerValidPath(const ValidPathInfo & info)
+{
     assert(info.hash.type == htSHA256);
+    ValidPathInfo info2(info);
+    if (info2.registrationTime == 0) info2.registrationTime = time(0);
+    
+    SQLiteTxn txn(db);
+    
+    unsigned long long id = addValidPath(info2);
 
-    string s = (format(
-        "Hash: sha256:%1%\n"
-        "References: %2%\n"
-        "Deriver: %3%\n"
-        "Registered-At: %4%\n")
-        % printHash(info.hash) % refs % info.deriver %
-        (oldInfo.registrationTime ? oldInfo.registrationTime : time(0))).str();
-
-    /* Atomically rewrite the info file. */
-    Path tmpFile = tmpFileForAtomicUpdate(infoFile);
-    writeFile(tmpFile, s, doFsync);
-    if (rename(tmpFile.c_str(), infoFile.c_str()) == -1)
-        throw SysError(format("cannot rename `%1%' to `%2%'") % tmpFile % infoFile);
-#endif
+    foreach (PathSet::const_iterator, i, info2.references) {
+        ValidPathInfo ref = queryPathInfo(*i);
+        addReference(id, ref.id);
+    }
+        
+    txn.commit();
 }
 
 
 void LocalStore::registerFailedPath(const Path & path)
 {
-    abort();
+    throw Error("not implemented");
 }
 
 
 bool LocalStore::hasPathFailed(const Path & path)
 {
-    abort();
+    throw Error("not implemented");
 }
 
 
@@ -424,13 +436,13 @@ Hash parseHashField(const Path & path, const string & s)
 
 ValidPathInfo LocalStore::queryPathInfo(const Path & path)
 {
-    ValidPathInfo res;
-    res.path = path;
+    ValidPathInfo info;
+    info.path = path;
 
     assertStorePath(path);
 
     /* Get the path info. */
-    stmtQueryPathInfo.reset();
+    SQLiteStmtUse use1(stmtQueryPathInfo);
     
     if (sqlite3_bind_text(stmtQueryPathInfo, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
         throw SQLiteError(db, "binding argument");
@@ -439,39 +451,39 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
     if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
     if (r != SQLITE_ROW) throw SQLiteError(db, "querying path in database");
 
-    unsigned int id = sqlite3_column_int(stmtQueryPathInfo, 0);
+    info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
 
     const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
     assert(s);
-    res.hash = parseHashField(path, s);
+    info.hash = parseHashField(path, s);
     
-    res.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
+    info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
 
     s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
-    if (s) res.deriver = s;
+    if (s) info.deriver = s;
 
     /* Get the references. */
-    stmtQueryReferences.reset();
+    SQLiteStmtUse use2(stmtQueryReferences);
 
-    if (sqlite3_bind_int(stmtQueryReferences, 1, id) != SQLITE_OK)
+    if (sqlite3_bind_int(stmtQueryReferences, 1, info.id) != SQLITE_OK)
         throw SQLiteError(db, "binding argument");
 
     while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
         s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
         assert(s);
-        res.references.insert(s);
+        info.references.insert(s);
     }
 
     if (r != SQLITE_DONE)
         throw SQLiteError(db, format("error getting references of `%1%'") % path);
 
-    return res;
+    return info;
 }
 
 
 bool LocalStore::isValidPath(const Path & path)
 {
-    stmtQueryPathInfo.reset();
+    SQLiteStmtUse use(stmtQueryPathInfo);
     if (sqlite3_bind_text(stmtQueryPathInfo, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
         throw SQLiteError(db, "binding argument");
     int res = sqlite3_step(stmtQueryPathInfo);
@@ -514,7 +526,7 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
 {
     assertStorePath(path);
 
-    stmtQueryReferrers.reset();
+    SQLiteStmtUse use(stmtQueryReferrers);
 
     if (sqlite3_bind_text(stmtQueryReferrers, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
         throw SQLiteError(db, "binding argument");
@@ -687,7 +699,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
    there are no referrers. */
 void LocalStore::invalidatePath(const Path & path)
 {
-    abort();
+    throw Error("not implemented");
 #if 0    
     debug(format("invalidating path `%1%'") % path);
 
@@ -1010,7 +1022,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed,
     unsigned long long & blocksFreed)
 {
-    abort();
+    throw Error("not implemented");
+
 #if 0
     bytesFreed = 0;
 
@@ -1077,13 +1090,6 @@ void LocalStore::verifyStore(bool checkContents)
 
 /* Functions for upgrading from the pre-SQLite database. */
 
-static Path infoFileFor(const Path & path)
-{
-    string baseName = baseNameOf(path);
-    return (format("%1%/info/%2%") % nixDBPath % baseName).str();
-}
-
-
 PathSet LocalStore::queryValidPathsOld()
 {
     PathSet paths;
@@ -1100,7 +1106,8 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
     res.path = path;
 
     /* Read the info file. */
-    Path infoFile = infoFileFor(path);
+    string baseName = baseNameOf(path);
+    Path infoFile = (format("%1%/info/%2%") % nixDBPath % baseName).str();
     if (!pathExists(infoFile))
         throw Error(format("path `%1%' is not valid") % path);
     string info = readFile(infoFile);
@@ -1152,27 +1159,7 @@ void LocalStore::upgradeStore6()
     
     foreach (PathSet::iterator, i, validPaths) {
         ValidPathInfo info = queryPathInfoOld(*i);
-        
-        stmtRegisterValidPath.reset();
-        if (sqlite3_bind_text(stmtRegisterValidPath, 1, i->c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
-            throw SQLiteError(db, "binding argument");
-        string h = "sha256:" + printHash(info.hash);
-        if (sqlite3_bind_text(stmtRegisterValidPath, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
-            throw SQLiteError(db, "binding argument");
-        if (sqlite3_bind_int(stmtRegisterValidPath, 3, info.registrationTime) != SQLITE_OK)
-            throw SQLiteError(db, "binding argument");
-        if (info.deriver != "") {
-            if (sqlite3_bind_text(stmtRegisterValidPath, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
-                throw SQLiteError(db, "binding argument");
-        } else {
-            if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK)
-                throw SQLiteError(db, "binding argument");
-        }
-        if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
-            throw SQLiteError(db, "registering valid path in database");
-
-        pathToId[*i] = sqlite3_last_insert_rowid(db);
-
+        pathToId[*i] = addValidPath(info);
         std::cerr << ".";
     }
 
@@ -1180,19 +1167,11 @@ void LocalStore::upgradeStore6()
     
     foreach (PathSet::iterator, i, validPaths) {
         ValidPathInfo info = queryPathInfoOld(*i);
-        
         foreach (PathSet::iterator, j, info.references) {
-            stmtAddReference.reset();
-            if (sqlite3_bind_int(stmtAddReference, 1, pathToId[*i]) != SQLITE_OK)
-                throw SQLiteError(db, "binding argument");
             if (pathToId.find(*j) == pathToId.end())
                 throw Error(format("path `%1%' referenced by `%2%' is invalid") % *j % *i);
-            if (sqlite3_bind_int(stmtAddReference, 2, pathToId[*j]) != SQLITE_OK)
-                throw SQLiteError(db, "binding argument");
-            if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
-                throw SQLiteError(db, "adding reference to database");
+            addReference(pathToId[*i], pathToId[*j]);
         }
-
         std::cerr << ".";
     }
 
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 9eae443d1247..cfe26039efc3 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -198,7 +198,11 @@ private:
 
     void prepareStatements();
 
-    void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
+    unsigned long long addValidPath(const ValidPathInfo & info);
+        
+    void addReference(unsigned long long referrer, unsigned long long reference);
+    
+    void registerValidPath(const ValidPathInfo & info);
 
     ValidPathInfo queryPathInfo(const Path & path);
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 8506d47e36a7..23a3ab25e470 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -333,6 +333,7 @@ struct ValidPathInfo
     Hash hash;
     PathSet references;
     time_t registrationTime;
+    unsigned long long id; // internal use only
     ValidPathInfo() : registrationTime(0) { }
 };