diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 35391f3aaf..870644c88b 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -18,6 +18,8 @@ #include #include +#include + 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, ®isterStmt, 0) != SQLITE_OK) - throw SQLiteError(db, "creating statement"); - std::map 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 f295656800..3637a5d07c 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 + +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);