* Some wrapper objects to ensure that SQLite objects are properly
destroyed.
This commit is contained in:
parent
a053d2d8e5
commit
e0305bb7a8
2 changed files with 109 additions and 49 deletions
|
@ -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, ®isterStmt, 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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue