mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-24 21:38:07 -05:00
daemon: Improve the SQLite wrapper API.
In particular, this eliminates a bunch of boilerplate code. Also integrates these Nix commits: 80da7a6 Probably fix SQLITE_BUSY errors 37a337b throwSQLiteError(): Check for SIGINT so we don't loop forever Co-authored-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
parent
7bed5d91de
commit
b1fd0ab734
5 changed files with 180 additions and 240 deletions
|
@ -329,6 +329,7 @@ void LocalStore::openDB(bool create)
|
||||||
// ensure efficient lookup.
|
// ensure efficient lookup.
|
||||||
stmtQueryPathFromHashPart.create(db,
|
stmtQueryPathFromHashPart.create(db,
|
||||||
"select path from ValidPaths where path >= ? limit 1;");
|
"select path from ValidPaths where path >= ? limit 1;");
|
||||||
|
stmtQueryValidPaths.create(db, "select path from ValidPaths");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -523,23 +524,16 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtRegisterValidPath);
|
stmtRegisterValidPath.use()
|
||||||
stmtRegisterValidPath.bind(info.path);
|
(info.path)
|
||||||
stmtRegisterValidPath.bind("sha256:" + printHash(info.hash));
|
("sha256:" + printHash(info.hash))
|
||||||
stmtRegisterValidPath.bind(info.registrationTime == 0 ? time(0) : info.registrationTime);
|
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
||||||
if (info.deriver != "")
|
(info.deriver, info.deriver != "")
|
||||||
stmtRegisterValidPath.bind(info.deriver);
|
(info.narSize, info.narSize != 0)
|
||||||
else
|
.exec();
|
||||||
stmtRegisterValidPath.bind(); // null
|
uint64_t id = sqlite3_last_insert_rowid(db);
|
||||||
if (info.narSize != 0)
|
|
||||||
stmtRegisterValidPath.bind64(info.narSize);
|
|
||||||
else
|
|
||||||
stmtRegisterValidPath.bind(); // null
|
|
||||||
if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("registering valid path `%1%' in database") % info.path);
|
|
||||||
unsigned long long id = sqlite3_last_insert_rowid(db);
|
|
||||||
|
|
||||||
/* If this is a derivation, then store the derivation outputs in
|
/* If this is a derivation, then store the derivation outputs in
|
||||||
the database. This is useful for the garbage collector: it can
|
the database. This is useful for the garbage collector: it can
|
||||||
|
@ -555,13 +549,12 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
|
||||||
registration above is undone. */
|
registration above is undone. */
|
||||||
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
||||||
|
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
SQLiteStmtUse use(stmtAddDerivationOutput);
|
stmtAddDerivationOutput.use()
|
||||||
stmtAddDerivationOutput.bind(id);
|
(id)
|
||||||
stmtAddDerivationOutput.bind(i->first);
|
(i.first)
|
||||||
stmtAddDerivationOutput.bind(i->second.path);
|
(i.second.path)
|
||||||
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
.exec();
|
||||||
throwSQLiteError(db, format("adding derivation output for `%1%' in database") % info.path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,24 +562,16 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::addReference(unsigned long long referrer, unsigned long long reference)
|
void LocalStore::addReference(uint64_t referrer, uint64_t reference)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtAddReference);
|
stmtAddReference.use()(referrer)(reference).exec();
|
||||||
stmtAddReference.bind(referrer);
|
|
||||||
stmtAddReference.bind(reference);
|
|
||||||
if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, "adding reference to database");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::registerFailedPath(const Path & path)
|
void LocalStore::registerFailedPath(const Path & path)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
SQLiteStmtUse use(stmtRegisterFailedPath);
|
stmtRegisterFailedPath.use()(path)(time(0)).step();
|
||||||
stmtRegisterFailedPath.bind(path);
|
|
||||||
stmtRegisterFailedPath.bind(time(0));
|
|
||||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,12 +579,7 @@ void LocalStore::registerFailedPath(const Path & path)
|
||||||
bool LocalStore::hasPathFailed(const Path & path)
|
bool LocalStore::hasPathFailed(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<bool>([&]() {
|
return retrySQLite<bool>([&]() {
|
||||||
SQLiteStmtUse use(stmtHasPathFailed);
|
return stmtHasPathFailed.use()(path).next();
|
||||||
stmtHasPathFailed.bind(path);
|
|
||||||
int res = sqlite3_step(stmtHasPathFailed);
|
|
||||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
|
||||||
throwSQLiteError(db, "querying whether path failed");
|
|
||||||
return res == SQLITE_ROW;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,18 +587,11 @@ bool LocalStore::hasPathFailed(const Path & path)
|
||||||
PathSet LocalStore::queryFailedPaths()
|
PathSet LocalStore::queryFailedPaths()
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
SQLiteStmtUse use(stmtQueryFailedPaths);
|
auto useQueryFailedPaths(stmtQueryFailedPaths.use());
|
||||||
|
|
||||||
PathSet res;
|
PathSet res;
|
||||||
int r;
|
while (useQueryFailedPaths.next())
|
||||||
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
|
res.insert(useQueryFailedPaths.getStr(0));
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
|
|
||||||
assert(s);
|
|
||||||
res.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, "error querying failed paths");
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
@ -630,12 +603,8 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
SQLiteTxn txn(db);
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
foreach (PathSet::const_iterator, i, paths) {
|
for (auto & path : paths)
|
||||||
SQLiteStmtUse use(stmtClearFailedPath);
|
stmtClearFailedPath.use()(path).exec();
|
||||||
stmtClearFailedPath.bind(*i);
|
|
||||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
|
||||||
}
|
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
});
|
});
|
||||||
|
@ -666,41 +635,28 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
return retrySQLite<ValidPathInfo>([&]() {
|
return retrySQLite<ValidPathInfo>([&]() {
|
||||||
|
|
||||||
/* Get the path info. */
|
/* Get the path info. */
|
||||||
SQLiteStmtUse use1(stmtQueryPathInfo);
|
auto useQueryPathInfo(stmtQueryPathInfo.use()(path));
|
||||||
|
|
||||||
stmtQueryPathInfo.bind(path);
|
if (!useQueryPathInfo.next())
|
||||||
|
throw Error(format("path `%1%' is not valid") % path);
|
||||||
|
|
||||||
int r = sqlite3_step(stmtQueryPathInfo);
|
info.id = useQueryPathInfo.getInt(0);
|
||||||
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
|
||||||
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
|
|
||||||
|
|
||||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
info.hash = parseHashField(path, useQueryPathInfo.getStr(1));
|
||||||
|
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
info.registrationTime = useQueryPathInfo.getInt(2);
|
||||||
assert(s);
|
|
||||||
info.hash = parseHashField(path, s);
|
|
||||||
|
|
||||||
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
|
auto s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||||
|
|
||||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
|
||||||
if (s) info.deriver = s;
|
if (s) info.deriver = s;
|
||||||
|
|
||||||
/* Note that narSize = NULL yields 0. */
|
/* Note that narSize = NULL yields 0. */
|
||||||
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
|
info.narSize = useQueryPathInfo.getInt(4);
|
||||||
|
|
||||||
/* Get the references. */
|
/* Get the references. */
|
||||||
SQLiteStmtUse use2(stmtQueryReferences);
|
auto useQueryReferences(stmtQueryReferences.use()(info.id));
|
||||||
|
|
||||||
stmtQueryReferences.bind(info.id);
|
while (useQueryReferences.next())
|
||||||
|
info.references.insert(useQueryReferences.getStr(0));
|
||||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
|
||||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
|
||||||
assert(s);
|
|
||||||
info.references.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
|
@ -711,37 +667,26 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
narSize field. */
|
narSize field. */
|
||||||
void LocalStore::updatePathInfo(const ValidPathInfo & info)
|
void LocalStore::updatePathInfo(const ValidPathInfo & info)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtUpdatePathInfo);
|
stmtUpdatePathInfo.use()
|
||||||
if (info.narSize != 0)
|
(info.narSize, info.narSize != 0)
|
||||||
stmtUpdatePathInfo.bind64(info.narSize);
|
("sha256:" + printHash(info.hash))
|
||||||
else
|
(info.path)
|
||||||
stmtUpdatePathInfo.bind(); // null
|
.exec();
|
||||||
stmtUpdatePathInfo.bind("sha256:" + printHash(info.hash));
|
|
||||||
stmtUpdatePathInfo.bind(info.path);
|
|
||||||
if (sqlite3_step(stmtUpdatePathInfo) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("updating info of path `%1%' in database") % info.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned long long LocalStore::queryValidPathId(const Path & path)
|
uint64_t LocalStore::queryValidPathId(const Path & path)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtQueryPathInfo);
|
auto use(stmtQueryPathInfo.use()(path));
|
||||||
stmtQueryPathInfo.bind(path);
|
if (!use.next())
|
||||||
int res = sqlite3_step(stmtQueryPathInfo);
|
throw Error(format("path ‘%1%’ is not valid") % path);
|
||||||
if (res == SQLITE_ROW) return sqlite3_column_int(stmtQueryPathInfo, 0);
|
return use.getInt(0);
|
||||||
if (res == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
|
||||||
throwSQLiteError(db, "querying path in database");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::isValidPath_(const Path & path)
|
bool LocalStore::isValidPath_(const Path & path)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtQueryPathInfo);
|
return stmtQueryPathInfo.use()(path).next();
|
||||||
stmtQueryPathInfo.bind(path);
|
|
||||||
int res = sqlite3_step(stmtQueryPathInfo);
|
|
||||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
|
||||||
throwSQLiteError(db, "querying path in database");
|
|
||||||
return res == SQLITE_ROW;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -767,20 +712,9 @@ PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
||||||
PathSet LocalStore::queryAllValidPaths()
|
PathSet LocalStore::queryAllValidPaths()
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
SQLiteStmt stmt;
|
auto use(stmtQueryValidPaths.use());
|
||||||
stmt.create(db, "select path from ValidPaths");
|
|
||||||
|
|
||||||
PathSet res;
|
PathSet res;
|
||||||
int r;
|
while (use.next()) res.insert(use.getStr(0));
|
||||||
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
|
|
||||||
const char * s = (const char *) sqlite3_column_text(stmt, 0);
|
|
||||||
assert(s);
|
|
||||||
res.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, "error getting valid paths");
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -796,19 +730,10 @@ void LocalStore::queryReferences(const Path & path,
|
||||||
|
|
||||||
void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
|
void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
|
||||||
{
|
{
|
||||||
SQLiteStmtUse use(stmtQueryReferrers);
|
auto useQueryReferrers(stmtQueryReferrers.use()(path));
|
||||||
|
|
||||||
stmtQueryReferrers.bind(path);
|
while (useQueryReferrers.next())
|
||||||
|
referrers.insert(useQueryReferrers.getStr(0));
|
||||||
int r;
|
|
||||||
while ((r = sqlite3_step(stmtQueryReferrers)) == SQLITE_ROW) {
|
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryReferrers, 0);
|
|
||||||
assert(s);
|
|
||||||
referrers.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -832,19 +757,11 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
SQLiteStmtUse use(stmtQueryValidDerivers);
|
auto useQueryValidDerivers(stmtQueryValidDerivers.use()(path));
|
||||||
stmtQueryValidDerivers.bind(path);
|
|
||||||
|
|
||||||
PathSet derivers;
|
PathSet derivers;
|
||||||
int r;
|
while (useQueryValidDerivers.next())
|
||||||
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
|
derivers.insert(useQueryValidDerivers.getStr(1));
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
|
|
||||||
assert(s);
|
|
||||||
derivers.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
|
||||||
|
|
||||||
return derivers;
|
return derivers;
|
||||||
});
|
});
|
||||||
|
@ -854,19 +771,11 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||||
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
|
||||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
|
||||||
|
|
||||||
PathSet outputs;
|
PathSet outputs;
|
||||||
int r;
|
while (useQueryDerivationOutputs.next())
|
||||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
outputs.insert(useQueryDerivationOutputs.getStr(1));
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
|
|
||||||
assert(s);
|
|
||||||
outputs.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
|
||||||
|
|
||||||
return outputs;
|
return outputs;
|
||||||
});
|
});
|
||||||
|
@ -876,19 +785,11 @@ PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||||
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<StringSet>([&]() {
|
return retrySQLite<StringSet>([&]() {
|
||||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
|
||||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
|
||||||
|
|
||||||
StringSet outputNames;
|
StringSet outputNames;
|
||||||
int r;
|
while (useQueryDerivationOutputs.next())
|
||||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
outputNames.insert(useQueryDerivationOutputs.getStr(0));
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
|
||||||
assert(s);
|
|
||||||
outputNames.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
|
||||||
|
|
||||||
return outputNames;
|
return outputNames;
|
||||||
});
|
});
|
||||||
|
@ -902,12 +803,9 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
|
||||||
Path prefix = settings.nixStore + "/" + hashPart;
|
Path prefix = settings.nixStore + "/" + hashPart;
|
||||||
|
|
||||||
return retrySQLite<Path>([&]() -> Path {
|
return retrySQLite<Path>([&]() -> Path {
|
||||||
SQLiteStmtUse use(stmtQueryPathFromHashPart);
|
auto useQueryPathFromHashPart(stmtQueryPathFromHashPart.use()(prefix));
|
||||||
stmtQueryPathFromHashPart.bind(prefix);
|
|
||||||
|
|
||||||
int res = sqlite3_step(stmtQueryPathFromHashPart);
|
if (!useQueryPathFromHashPart.next()) return "";
|
||||||
if (res == SQLITE_DONE) return "";
|
|
||||||
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
|
|
||||||
|
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
||||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||||
|
@ -1154,10 +1052,10 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
paths.insert(i->path);
|
paths.insert(i->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
for (auto & i : infos) {
|
||||||
unsigned long long referrer = queryValidPathId(i->path);
|
auto referrer = queryValidPathId(i.path);
|
||||||
foreach (PathSet::iterator, j, i->references)
|
for (auto & j : i.references)
|
||||||
addReference(referrer, queryValidPathId(*j));
|
addReference(referrer, queryValidPathId(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the derivation outputs are correct. We can't do
|
/* Check that the derivation outputs are correct. We can't do
|
||||||
|
@ -1190,12 +1088,7 @@ void LocalStore::invalidatePath(const Path & path)
|
||||||
|
|
||||||
drvHashes.erase(path);
|
drvHashes.erase(path);
|
||||||
|
|
||||||
SQLiteStmtUse use(stmtInvalidatePath);
|
stmtInvalidatePath.use()(path).exec();
|
||||||
|
|
||||||
stmtInvalidatePath.bind(path);
|
|
||||||
|
|
||||||
if (sqlite3_step(stmtInvalidatePath) != SQLITE_DONE)
|
|
||||||
throwSQLiteError(db, format("invalidating path `%1%' in database") % path);
|
|
||||||
|
|
||||||
/* Note that the foreign key constraints on the Refs table take
|
/* Note that the foreign key constraints on the Refs table take
|
||||||
care of deleting the references entries for `path'. */
|
care of deleting the references entries for `path'. */
|
||||||
|
|
|
@ -207,6 +207,7 @@ private:
|
||||||
SQLiteStmt stmtQueryValidDerivers;
|
SQLiteStmt stmtQueryValidDerivers;
|
||||||
SQLiteStmt stmtQueryDerivationOutputs;
|
SQLiteStmt stmtQueryDerivationOutputs;
|
||||||
SQLiteStmt stmtQueryPathFromHashPart;
|
SQLiteStmt stmtQueryPathFromHashPart;
|
||||||
|
SQLiteStmt stmtQueryValidPaths;
|
||||||
|
|
||||||
/* Cache for pathContentsGood(). */
|
/* Cache for pathContentsGood(). */
|
||||||
std::map<Path, bool> pathContentsGoodCache;
|
std::map<Path, bool> pathContentsGoodCache;
|
||||||
|
@ -223,11 +224,11 @@ private:
|
||||||
|
|
||||||
void makeStoreWritable();
|
void makeStoreWritable();
|
||||||
|
|
||||||
unsigned long long queryValidPathId(const Path & path);
|
uint64_t queryValidPathId(const Path & path);
|
||||||
|
|
||||||
unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
|
uint64_t addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
|
||||||
|
|
||||||
void addReference(unsigned long long referrer, unsigned long long reference);
|
void addReference(uint64_t referrer, uint64_t reference);
|
||||||
|
|
||||||
void appendReferrer(const Path & from, const Path & to, bool lock);
|
void appendReferrer(const Path & from, const Path & to, bool lock);
|
||||||
|
|
||||||
|
|
|
@ -53,15 +53,6 @@ void SQLiteStmt::create(sqlite3 * db, const string & s)
|
||||||
this->db = db;
|
this->db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteStmt::reset()
|
|
||||||
{
|
|
||||||
assert(stmt);
|
|
||||||
/* Note: sqlite3_reset() returns the error code for the most
|
|
||||||
recent call to sqlite3_step(). So ignore it. */
|
|
||||||
sqlite3_reset(stmt);
|
|
||||||
curArg = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteStmt::~SQLiteStmt()
|
SQLiteStmt::~SQLiteStmt()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -72,43 +63,79 @@ SQLiteStmt::~SQLiteStmt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteStmt::bind(const string & value)
|
SQLiteStmt::Use::Use(SQLiteStmt & stmt)
|
||||||
{
|
|
||||||
if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
|
||||||
throwSQLiteError(db, "binding argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SQLiteStmt::bind(int value)
|
|
||||||
{
|
|
||||||
if (sqlite3_bind_int(stmt, curArg++, value) != SQLITE_OK)
|
|
||||||
throwSQLiteError(db, "binding argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SQLiteStmt::bind64(long long value)
|
|
||||||
{
|
|
||||||
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
|
||||||
throwSQLiteError(db, "binding argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SQLiteStmt::bind()
|
|
||||||
{
|
|
||||||
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
|
||||||
throwSQLiteError(db, "binding argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteStmtUse::SQLiteStmtUse(SQLiteStmt & stmt)
|
|
||||||
: stmt(stmt)
|
: stmt(stmt)
|
||||||
{
|
{
|
||||||
stmt.reset();
|
assert(stmt.stmt);
|
||||||
|
/* Note: sqlite3_reset() returns the error code for the most
|
||||||
|
recent call to sqlite3_step(). So ignore it. */
|
||||||
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteStmtUse::~SQLiteStmtUse()
|
SQLiteStmt::Use::~Use()
|
||||||
{
|
{
|
||||||
try {
|
sqlite3_reset(stmt);
|
||||||
stmt.reset();
|
}
|
||||||
} catch (...) {
|
|
||||||
ignoreException();
|
SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
|
||||||
}
|
{
|
||||||
|
if (notNull) {
|
||||||
|
if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
|
throwSQLiteError(stmt.db, "binding argument");
|
||||||
|
} else
|
||||||
|
bind();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||||
|
{
|
||||||
|
if (notNull) {
|
||||||
|
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
||||||
|
throwSQLiteError(stmt.db, "binding argument");
|
||||||
|
} else
|
||||||
|
bind();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
||||||
|
throwSQLiteError(stmt.db, "binding argument");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SQLiteStmt::Use::step()
|
||||||
|
{
|
||||||
|
return sqlite3_step(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SQLiteStmt::Use::exec()
|
||||||
|
{
|
||||||
|
int r = step();
|
||||||
|
assert(r != SQLITE_ROW);
|
||||||
|
if (r != SQLITE_DONE)
|
||||||
|
throwSQLiteError(stmt.db, "executing SQLite statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SQLiteStmt::Use::next()
|
||||||
|
{
|
||||||
|
int r = step();
|
||||||
|
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
||||||
|
throwSQLiteError(stmt.db, "executing SQLite query");
|
||||||
|
return r == SQLITE_ROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SQLiteStmt::Use::getStr(int col)
|
||||||
|
{
|
||||||
|
auto s = (const char *) sqlite3_column_text(stmt, col);
|
||||||
|
assert(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t SQLiteStmt::Use::getInt(int col)
|
||||||
|
{
|
||||||
|
// FIXME: detect nulls?
|
||||||
|
return sqlite3_column_int64(stmt, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
||||||
|
|
|
@ -22,29 +22,48 @@ struct SQLite
|
||||||
/* RAII wrapper to create and destroy SQLite prepared statements. */
|
/* RAII wrapper to create and destroy SQLite prepared statements. */
|
||||||
struct SQLiteStmt
|
struct SQLiteStmt
|
||||||
{
|
{
|
||||||
sqlite3 * db;
|
sqlite3 * db = 0;
|
||||||
sqlite3_stmt * stmt;
|
sqlite3_stmt * stmt = 0;
|
||||||
unsigned int curArg;
|
SQLiteStmt() { }
|
||||||
SQLiteStmt() { stmt = 0; }
|
|
||||||
void create(sqlite3 * db, const std::string & s);
|
void create(sqlite3 * db, const std::string & s);
|
||||||
void reset();
|
|
||||||
~SQLiteStmt();
|
~SQLiteStmt();
|
||||||
operator sqlite3_stmt * () { return stmt; }
|
operator sqlite3_stmt * () { return stmt; }
|
||||||
void bind(const std::string & value);
|
|
||||||
void bind(int value);
|
|
||||||
void bind64(long long value);
|
|
||||||
void bind();
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Helper class to ensure that prepared statements are reset when
|
/* Helper for binding / executing statements. */
|
||||||
leaving the scope that uses them. Unfinished prepared statements
|
class Use
|
||||||
prevent transactions from being aborted, and can cause locks to be
|
{
|
||||||
kept when they should be released. */
|
friend struct SQLiteStmt;
|
||||||
struct SQLiteStmtUse
|
private:
|
||||||
{
|
|
||||||
SQLiteStmt & stmt;
|
SQLiteStmt & stmt;
|
||||||
SQLiteStmtUse(SQLiteStmt & stmt);
|
unsigned int curArg = 1;
|
||||||
~SQLiteStmtUse();
|
Use(SQLiteStmt & stmt);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
~Use();
|
||||||
|
|
||||||
|
/* Bind the next parameter. */
|
||||||
|
Use & operator () (const std::string & value, bool notNull = true);
|
||||||
|
Use & operator () (int64_t value, bool notNull = true);
|
||||||
|
Use & bind(); // null
|
||||||
|
|
||||||
|
int step();
|
||||||
|
|
||||||
|
/* Execute a statement that does not return rows. */
|
||||||
|
void exec();
|
||||||
|
|
||||||
|
/* For statements that return 0 or more rows. Returns true iff
|
||||||
|
a row is available. */
|
||||||
|
bool next();
|
||||||
|
|
||||||
|
std::string getStr(int col);
|
||||||
|
int64_t getInt(int col);
|
||||||
|
};
|
||||||
|
|
||||||
|
Use use()
|
||||||
|
{
|
||||||
|
return Use(*this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* RAII helper that ensures transactions are aborted unless explicitly
|
/* RAII helper that ensures transactions are aborted unless explicitly
|
||||||
|
|
|
@ -89,8 +89,8 @@ struct ValidPathInfo
|
||||||
Hash hash;
|
Hash hash;
|
||||||
PathSet references;
|
PathSet references;
|
||||||
time_t registrationTime = 0;
|
time_t registrationTime = 0;
|
||||||
unsigned long long narSize = 0; // 0 = unknown
|
uint64_t narSize = 0; // 0 = unknown
|
||||||
unsigned long long id; // internal use only
|
uint64_t id; // internal use only
|
||||||
|
|
||||||
bool operator == (const ValidPathInfo & i) const
|
bool operator == (const ValidPathInfo & i) const
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue