* Implement registerValidPath().
This commit is contained in:
parent
268f9aaf28
commit
762cee72cc
3 changed files with 96 additions and 112 deletions
|
@ -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
|
struct SQLiteTxn
|
||||||
{
|
{
|
||||||
bool active;
|
bool active;
|
||||||
|
@ -223,10 +241,10 @@ int LocalStore::getSchema()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "schema.sql.hh"
|
|
||||||
|
|
||||||
void LocalStore::initSchema()
|
void LocalStore::initSchema()
|
||||||
{
|
{
|
||||||
|
#include "schema.sql.hh"
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
|
@ -237,9 +255,9 @@ void LocalStore::initSchema()
|
||||||
void LocalStore::prepareStatements()
|
void LocalStore::prepareStatements()
|
||||||
{
|
{
|
||||||
stmtRegisterValidPath.create(db,
|
stmtRegisterValidPath.create(db,
|
||||||
"insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);");
|
"insert or replace into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);");
|
||||||
stmtAddReference.create(db,
|
stmtAddReference.create(db,
|
||||||
"insert into Refs (referrer, reference) values (?, ?);");
|
"insert or replace into Refs (referrer, reference) values (?, ?);");
|
||||||
stmtQueryPathInfo.create(db,
|
stmtQueryPathInfo.create(db,
|
||||||
"select id, hash, registrationTime, deriver from ValidPaths where path = ?;");
|
"select id, hash, registrationTime, deriver from ValidPaths where path = ?;");
|
||||||
stmtQueryReferences.create(db,
|
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();
|
SQLiteStmtUse use(stmtRegisterValidPath);
|
||||||
#if 0
|
if (sqlite3_bind_text(stmtRegisterValidPath, 1, info.path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
Path infoFile = infoFileFor(info.path);
|
throw SQLiteError(db, "binding argument");
|
||||||
|
string h = "sha256:" + printHash(info.hash);
|
||||||
ValidPathInfo oldInfo;
|
if (sqlite3_bind_text(stmtRegisterValidPath, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
if (pathExists(infoFile)) oldInfo = queryPathInfo(info.path);
|
throw SQLiteError(db, "binding argument");
|
||||||
|
if (sqlite3_bind_int(stmtRegisterValidPath, 3, info.registrationTime) != SQLITE_OK)
|
||||||
/* Note that it's possible for infoFile to already exist. */
|
throw SQLiteError(db, "binding argument");
|
||||||
|
if (info.deriver != "") {
|
||||||
/* Acquire a lock on each referrer file. This prevents those
|
if (sqlite3_bind_text(stmtRegisterValidPath, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
paths from being invalidated. (It would be a violation of the
|
throw SQLiteError(db, "binding argument");
|
||||||
store invariants if we registered info.path as valid while some
|
} else {
|
||||||
of its references are invalid.) NB: there can be no deadlock
|
if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK)
|
||||||
here since we're acquiring the locks in sorted order. */
|
throw SQLiteError(db, "binding argument");
|
||||||
PathSet lockNames;
|
}
|
||||||
foreach (PathSet::const_iterator, i, info.references)
|
if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
|
||||||
if (*i != info.path) lockNames.insert(referrersFileFor(*i));
|
throw SQLiteError(db, format("registering valid path `%1%' in database") % info.path);
|
||||||
PathLocks referrerLocks(lockNames);
|
return sqlite3_last_insert_rowid(db);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
assert(info.hash.type == htSHA256);
|
||||||
|
ValidPathInfo info2(info);
|
||||||
|
if (info2.registrationTime == 0) info2.registrationTime = time(0);
|
||||||
|
|
||||||
string s = (format(
|
SQLiteTxn txn(db);
|
||||||
"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. */
|
unsigned long long id = addValidPath(info2);
|
||||||
Path tmpFile = tmpFileForAtomicUpdate(infoFile);
|
|
||||||
writeFile(tmpFile, s, doFsync);
|
foreach (PathSet::const_iterator, i, info2.references) {
|
||||||
if (rename(tmpFile.c_str(), infoFile.c_str()) == -1)
|
ValidPathInfo ref = queryPathInfo(*i);
|
||||||
throw SysError(format("cannot rename `%1%' to `%2%'") % tmpFile % infoFile);
|
addReference(id, ref.id);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::registerFailedPath(const Path & path)
|
void LocalStore::registerFailedPath(const Path & path)
|
||||||
{
|
{
|
||||||
abort();
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::hasPathFailed(const Path & path)
|
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 LocalStore::queryPathInfo(const Path & path)
|
||||||
{
|
{
|
||||||
ValidPathInfo res;
|
ValidPathInfo info;
|
||||||
res.path = path;
|
info.path = path;
|
||||||
|
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
/* Get the path info. */
|
/* Get the path info. */
|
||||||
stmtQueryPathInfo.reset();
|
SQLiteStmtUse use1(stmtQueryPathInfo);
|
||||||
|
|
||||||
if (sqlite3_bind_text(stmtQueryPathInfo, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_text(stmtQueryPathInfo, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
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_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||||
if (r != SQLITE_ROW) throw SQLiteError(db, "querying path in database");
|
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);
|
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
||||||
assert(s);
|
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);
|
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||||
if (s) res.deriver = s;
|
if (s) info.deriver = s;
|
||||||
|
|
||||||
/* Get the references. */
|
/* 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");
|
throw SQLiteError(db, "binding argument");
|
||||||
|
|
||||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
||||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
||||||
assert(s);
|
assert(s);
|
||||||
res.references.insert(s);
|
info.references.insert(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("error getting references of `%1%'") % path);
|
throw SQLiteError(db, format("error getting references of `%1%'") % path);
|
||||||
|
|
||||||
return res;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::isValidPath(const Path & path)
|
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)
|
if (sqlite3_bind_text(stmtQueryPathInfo, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throw SQLiteError(db, "binding argument");
|
||||||
int res = sqlite3_step(stmtQueryPathInfo);
|
int res = sqlite3_step(stmtQueryPathInfo);
|
||||||
|
@ -514,7 +526,7 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||||
{
|
{
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
stmtQueryReferrers.reset();
|
SQLiteStmtUse use(stmtQueryReferrers);
|
||||||
|
|
||||||
if (sqlite3_bind_text(stmtQueryReferrers, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_text(stmtQueryReferrers, 1, path.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throw SQLiteError(db, "binding argument");
|
||||||
|
@ -687,7 +699,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
there are no referrers. */
|
there are no referrers. */
|
||||||
void LocalStore::invalidatePath(const Path & path)
|
void LocalStore::invalidatePath(const Path & path)
|
||||||
{
|
{
|
||||||
abort();
|
throw Error("not implemented");
|
||||||
#if 0
|
#if 0
|
||||||
debug(format("invalidating path `%1%'") % path);
|
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,
|
void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed,
|
||||||
unsigned long long & blocksFreed)
|
unsigned long long & blocksFreed)
|
||||||
{
|
{
|
||||||
abort();
|
throw Error("not implemented");
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
bytesFreed = 0;
|
bytesFreed = 0;
|
||||||
|
|
||||||
|
@ -1077,13 +1090,6 @@ void LocalStore::verifyStore(bool checkContents)
|
||||||
|
|
||||||
/* Functions for upgrading from the pre-SQLite database. */
|
/* 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 LocalStore::queryValidPathsOld()
|
||||||
{
|
{
|
||||||
PathSet paths;
|
PathSet paths;
|
||||||
|
@ -1100,7 +1106,8 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
|
||||||
res.path = path;
|
res.path = path;
|
||||||
|
|
||||||
/* Read the info file. */
|
/* Read the info file. */
|
||||||
Path infoFile = infoFileFor(path);
|
string baseName = baseNameOf(path);
|
||||||
|
Path infoFile = (format("%1%/info/%2%") % nixDBPath % baseName).str();
|
||||||
if (!pathExists(infoFile))
|
if (!pathExists(infoFile))
|
||||||
throw Error(format("path `%1%' is not valid") % path);
|
throw Error(format("path `%1%' is not valid") % path);
|
||||||
string info = readFile(infoFile);
|
string info = readFile(infoFile);
|
||||||
|
@ -1152,27 +1159,7 @@ void LocalStore::upgradeStore6()
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths) {
|
foreach (PathSet::iterator, i, validPaths) {
|
||||||
ValidPathInfo info = queryPathInfoOld(*i);
|
ValidPathInfo info = queryPathInfoOld(*i);
|
||||||
|
pathToId[*i] = addValidPath(info);
|
||||||
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);
|
|
||||||
|
|
||||||
std::cerr << ".";
|
std::cerr << ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,19 +1167,11 @@ void LocalStore::upgradeStore6()
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths) {
|
foreach (PathSet::iterator, i, validPaths) {
|
||||||
ValidPathInfo info = queryPathInfoOld(*i);
|
ValidPathInfo info = queryPathInfoOld(*i);
|
||||||
|
|
||||||
foreach (PathSet::iterator, j, info.references) {
|
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())
|
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(stmtAddReference, 2, pathToId[*j]) != SQLITE_OK)
|
addReference(pathToId[*i], pathToId[*j]);
|
||||||
throw SQLiteError(db, "binding argument");
|
|
||||||
if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
|
|
||||||
throw SQLiteError(db, "adding reference to database");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << ".";
|
std::cerr << ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,11 @@ private:
|
||||||
|
|
||||||
void prepareStatements();
|
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);
|
ValidPathInfo queryPathInfo(const Path & path);
|
||||||
|
|
||||||
|
|
|
@ -333,6 +333,7 @@ struct ValidPathInfo
|
||||||
Hash hash;
|
Hash hash;
|
||||||
PathSet references;
|
PathSet references;
|
||||||
time_t registrationTime;
|
time_t registrationTime;
|
||||||
|
unsigned long long id; // internal use only
|
||||||
ValidPathInfo() : registrationTime(0) { }
|
ValidPathInfo() : registrationTime(0) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue