* Some wrapper objects to ensure that SQLite objects are properly

destroyed.
This commit is contained in:
Eelco Dolstra 2010-02-18 14:30:42 +00:00
parent a053d2d8e5
commit e0305bb7a8
2 changed files with 109 additions and 49 deletions

View file

@ -18,6 +18,8 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <sqlite3.h>
namespace nix { 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() void checkStoreNotSymlink()
{ {
if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return; if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
@ -52,7 +85,6 @@ void checkStoreNotSymlink()
LocalStore::LocalStore() LocalStore::LocalStore()
{ {
db = 0;
substitutablePathsLoaded = false; substitutablePathsLoaded = false;
schemaPath = nixDBPath + "/schema"; schemaPath = nixDBPath + "/schema";
@ -73,6 +105,8 @@ LocalStore::LocalStore()
checkStoreNotSymlink(); checkStoreNotSymlink();
/* Acquire the big fat lock in shared mode to make sure that no
schema upgrade is in progress. */
try { try {
Path globalLockPath = nixDBPath + "/big-lock"; Path globalLockPath = nixDBPath + "/big-lock";
globalLock = openLockFile(globalLockPath.c_str(), true); globalLock = openLockFile(globalLockPath.c_str(), true);
@ -87,21 +121,33 @@ LocalStore::LocalStore()
lockFile(globalLock, ltRead, true); 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(); int curSchema = getSchema();
if (curSchema > nixSchemaVersion) if (curSchema > nixSchemaVersion)
throw Error(format("current Nix store schema is version %1%, but I only support %2%") throw Error(format("current Nix store schema is version %1%, but I only support %2%")
% curSchema % nixSchemaVersion); % curSchema % nixSchemaVersion);
if (curSchema == 0) { /* new store */ if (curSchema == 0) { /* new store */
curSchema = nixSchemaVersion; curSchema = nixSchemaVersion;
initSchema();
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
} }
if (curSchema == 1) throw Error("your Nix store is no longer supported"); else if (curSchema == 1) throw Error("your Nix store is no longer supported");
if (curSchema < 5) else if (curSchema < 5)
throw Error( throw Error(
"Your Nix store has a database in Berkeley DB format,\n" "Your Nix store has a database in Berkeley DB format,\n"
"which is no longer supported. To convert to the new format,\n" "which is no longer supported. To convert to the new format,\n"
"please upgrade Nix to version 0.12 first."); "please upgrade Nix to version 0.12 first.");
if (curSchema < 6) upgradeStore6(); else if (curSchema < 6) upgradeStore6();
else prepareStatements();
doFsync = queryBoolSetting("fsync-metadata", false); doFsync = queryBoolSetting("fsync-metadata", false);
} }
@ -112,9 +158,6 @@ LocalStore::~LocalStore()
try { try {
flushDelayedUpdates(); flushDelayedUpdates();
if (db && sqlite3_close(db) != SQLITE_OK)
throw SQLiteError(db, "closing database");
foreach (RunningSubstituters::iterator, i, runningSubstituters) { foreach (RunningSubstituters::iterator, i, runningSubstituters) {
i->second.to.close(); i->second.to.close();
i->second.from.close(); i->second.from.close();
@ -143,15 +186,19 @@ int LocalStore::getSchema()
void LocalStore::initSchema() 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) if (sqlite3_exec(db, (const char *) schema, 0, 0, 0) != SQLITE_OK)
throw SQLiteError(db, "initialising database schema"); 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) if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
throw SQLiteError(db, "running `begin' command"); 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; std::map<Path, sqlite3_int64> pathToId;
foreach (PathSet::iterator, i, validPaths) { foreach (PathSet::iterator, i, validPaths) {
ValidPathInfo info = queryPathInfo(*i, true); ValidPathInfo info = queryPathInfo(*i, true);
if (sqlite3_reset(registerStmt) != SQLITE_OK) if (sqlite3_reset(stmtRegisterValidPath) != SQLITE_OK)
throw SQLiteError(db, "resetting statement"); 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"); throw SQLiteError(db, "binding argument 1");
string h = "sha256:" + printHash(info.hash); 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"); 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"); throw SQLiteError(db, "binding argument 3");
if (info.deriver != "") { 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"); throw SQLiteError(db, "binding argument 4");
} else { } else {
if (sqlite3_bind_null(registerStmt, 4) != SQLITE_OK) if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK)
throw SQLiteError(db, "binding argument 4"); 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"); throw SQLiteError(db, "registering valid path in database");
pathToId[*i] = sqlite3_last_insert_rowid(db); pathToId[*i] = sqlite3_last_insert_rowid(db);
@ -1254,29 +1296,21 @@ void LocalStore::upgradeStore6()
std::cerr << "."; std::cerr << ".";
} }
if (sqlite3_finalize(registerStmt) != SQLITE_OK)
throw SQLiteError(db, "finalizing statement");
std::cerr << "|"; 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) { foreach (PathSet::iterator, i, validPaths) {
ValidPathInfo info = queryPathInfo(*i, true); ValidPathInfo info = queryPathInfo(*i, true);
foreach (PathSet::iterator, j, info.references) { foreach (PathSet::iterator, j, info.references) {
if (sqlite3_reset(addRefStmt) != SQLITE_OK) if (sqlite3_reset(stmtAddReference) != SQLITE_OK)
throw SQLiteError(db, "resetting statement"); 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"); throw SQLiteError(db, "binding argument 1");
if (pathToId.find(*j) == pathToId.end()) if (pathToId.find(*j) == pathToId.end())
throw Error(format("path `%1%' referenced by `%2%' is invalid") % *j % *i); 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"); 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"); throw SQLiteError(db, "adding reference to database");
} }
@ -1287,12 +1321,6 @@ void LocalStore::upgradeStore6()
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK) if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
throw SQLiteError(db, "running `commit' command"); 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()); writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
lockFile(globalLock, ltRead, true); lockFile(globalLock, ltRead, true);

View file

@ -6,7 +6,9 @@
#include "store-api.hh" #include "store-api.hh"
#include "util.hh" #include "util.hh"
#include <sqlite3.h>
class sqlite3;
class sqlite3_stmt;
namespace nix { namespace nix {
@ -14,8 +16,9 @@ namespace nix {
/* Nix store and database schema version. Version 1 (or 0) was 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. 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*/ Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.14. Version 6 is
const int nixSchemaVersion = 5; Nix 0.15. */
const int nixSchemaVersion = 6;
extern string drvsLogDir; 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 class LocalStore : public StoreAPI
{ {
private: private:
@ -163,12 +188,19 @@ private:
/* Whether to do an fsync() after writing Nix metadata. */ /* Whether to do an fsync() after writing Nix metadata. */
bool doFsync; bool doFsync;
sqlite3 * db; /* The SQLite database object. */
SQLite db;
/* Some precompiled SQLite statements. */
SQLiteStmt stmtRegisterValidPath;
SQLiteStmt stmtAddReference;
int getSchema(); int getSchema();
void initSchema(); void initSchema();
void prepareStatements();
void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false); void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false); ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false);