forked from lix-project/lix
* Retry a transaction if SQLite returns SQLITE_BUSY. This can happen
even with a very long busy timeout, because SQLITE_BUSY is also returned to resolve deadlocks. This should get rid of random "database is locked" errors. This is kind of hard to test though. * Fix a horrible bug in deleteFromStore(): deletePathWrapped() should be called after committing the transaction, not before, because the commit might not succeed.
This commit is contained in:
parent
365f3028dd
commit
de79d23f76
|
@ -23,21 +23,30 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
class SQLiteError : public Error
|
MakeError(SQLiteError, Error);
|
||||||
|
MakeError(SQLiteBusy, SQLiteError);
|
||||||
|
|
||||||
|
|
||||||
|
static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||||
|
__attribute__ ((noreturn));
|
||||||
|
|
||||||
|
static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||||
{
|
{
|
||||||
public:
|
int err = sqlite3_errcode(db);
|
||||||
SQLiteError(sqlite3 * db, const format & f)
|
if (err == SQLITE_BUSY) {
|
||||||
: Error(format("%1%: %2%") % f.str() % sqlite3_errmsg(db))
|
printMsg(lvlError, "warning: SQLite database is busy");
|
||||||
{
|
throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
};
|
else
|
||||||
|
throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SQLite::~SQLite()
|
SQLite::~SQLite()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (db && sqlite3_close(db) != SQLITE_OK)
|
if (db && sqlite3_close(db) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "closing database");
|
throwSQLiteError(db, "closing database");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +58,7 @@ void SQLiteStmt::create(sqlite3 * db, const string & s)
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
assert(!stmt);
|
assert(!stmt);
|
||||||
if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "creating statement");
|
throwSQLiteError(db, "creating statement");
|
||||||
this->db = db;
|
this->db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +67,7 @@ void SQLiteStmt::reset()
|
||||||
{
|
{
|
||||||
assert(stmt);
|
assert(stmt);
|
||||||
if (sqlite3_reset(stmt) != SQLITE_OK)
|
if (sqlite3_reset(stmt) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "resetting statement");
|
throwSQLiteError(db, "resetting statement");
|
||||||
curArg = 1;
|
curArg = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +76,7 @@ SQLiteStmt::~SQLiteStmt()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "finalizing statement");
|
throwSQLiteError(db, "finalizing statement");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -77,28 +86,28 @@ SQLiteStmt::~SQLiteStmt()
|
||||||
void SQLiteStmt::bind(const string & value)
|
void SQLiteStmt::bind(const string & value)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throwSQLiteError(db, "binding argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLiteStmt::bind(int value)
|
void SQLiteStmt::bind(int value)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int(stmt, curArg++, value) != SQLITE_OK)
|
if (sqlite3_bind_int(stmt, curArg++, value) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throwSQLiteError(db, "binding argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLiteStmt::bind64(long long value)
|
void SQLiteStmt::bind64(long long value)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throwSQLiteError(db, "binding argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLiteStmt::bind()
|
void SQLiteStmt::bind()
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "binding argument");
|
throwSQLiteError(db, "binding argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,14 +141,14 @@ struct SQLiteTxn
|
||||||
SQLiteTxn(sqlite3 * db) : active(false) {
|
SQLiteTxn(sqlite3 * db) : active(false) {
|
||||||
this->db = db;
|
this->db = db;
|
||||||
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "starting transaction");
|
throwSQLiteError(db, "starting transaction");
|
||||||
active = true;
|
active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit()
|
void commit()
|
||||||
{
|
{
|
||||||
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "committing transaction");
|
throwSQLiteError(db, "committing transaction");
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +156,7 @@ struct SQLiteTxn
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "aborting transaction");
|
throwSQLiteError(db, "aborting transaction");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -289,10 +298,10 @@ void LocalStore::openDB(bool create)
|
||||||
throw Error("cannot open SQLite database");
|
throw Error("cannot open SQLite database");
|
||||||
|
|
||||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "setting timeout");
|
throwSQLiteError(db, "setting timeout");
|
||||||
|
|
||||||
if (sqlite3_exec(db, "pragma foreign_keys = 1;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "pragma foreign_keys = 1;", 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "enabling foreign keys");
|
throwSQLiteError(db, "enabling foreign keys");
|
||||||
|
|
||||||
/* !!! check whether sqlite has been built with foreign key
|
/* !!! check whether sqlite has been built with foreign key
|
||||||
support */
|
support */
|
||||||
|
@ -303,7 +312,7 @@ void LocalStore::openDB(bool create)
|
||||||
crashes. */
|
crashes. */
|
||||||
string syncMode = queryBoolSetting("fsync-metadata", true) ? "normal" : "off";
|
string syncMode = queryBoolSetting("fsync-metadata", true) ? "normal" : "off";
|
||||||
if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "setting synchronous mode");
|
throwSQLiteError(db, "setting synchronous mode");
|
||||||
|
|
||||||
/* Set the SQLite journal mode. WAL mode is fastest, but doesn't
|
/* Set the SQLite journal mode. WAL mode is fastest, but doesn't
|
||||||
seem entirely stable at the moment (Oct. 2010). Thus, use
|
seem entirely stable at the moment (Oct. 2010). Thus, use
|
||||||
|
@ -314,31 +323,31 @@ void LocalStore::openDB(bool create)
|
||||||
SQLiteStmt stmt;
|
SQLiteStmt stmt;
|
||||||
stmt.create(db, "pragma main.journal_mode;");
|
stmt.create(db, "pragma main.journal_mode;");
|
||||||
if (sqlite3_step(stmt) != SQLITE_ROW)
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
throw SQLiteError(db, "querying journal mode");
|
throwSQLiteError(db, "querying journal mode");
|
||||||
prevMode = string((const char *) sqlite3_column_text(stmt, 0));
|
prevMode = string((const char *) sqlite3_column_text(stmt, 0));
|
||||||
}
|
}
|
||||||
if (prevMode != mode &&
|
if (prevMode != mode &&
|
||||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "setting journal mode");
|
throwSQLiteError(db, "setting journal mode");
|
||||||
|
|
||||||
/* Increase the auto-checkpoint interval to 8192 pages. This
|
/* Increase the auto-checkpoint interval to 8192 pages. This
|
||||||
seems enough to ensure that instantiating the NixOS system
|
seems enough to ensure that instantiating the NixOS system
|
||||||
derivation is done in a single fsync(). */
|
derivation is done in a single fsync(). */
|
||||||
if (sqlite3_exec(db, "pragma wal_autocheckpoint = 8192;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "pragma wal_autocheckpoint = 8192;", 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "setting autocheckpoint interval");
|
throwSQLiteError(db, "setting autocheckpoint interval");
|
||||||
|
|
||||||
/* Initialise the database schema, if necessary. */
|
/* Initialise the database schema, if necessary. */
|
||||||
if (create) {
|
if (create) {
|
||||||
#include "schema.sql.hh"
|
#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");
|
throwSQLiteError(db, "initialising database schema");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Backwards compatibility with old (pre-release) databases. Can
|
/* Backwards compatibility with old (pre-release) databases. Can
|
||||||
remove this eventually. */
|
remove this eventually. */
|
||||||
if (sqlite3_table_column_metadata(db, 0, "ValidPaths", "narSize", 0, 0, 0, 0, 0) != SQLITE_OK) {
|
if (sqlite3_table_column_metadata(db, 0, "ValidPaths", "narSize", 0, 0, 0, 0, 0) != SQLITE_OK) {
|
||||||
if (sqlite3_exec(db, "alter table ValidPaths add column narSize integer" , 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "alter table ValidPaths add column narSize integer" , 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "adding column narSize");
|
throwSQLiteError(db, "adding column narSize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare SQL statements. */
|
/* Prepare SQL statements. */
|
||||||
|
@ -460,7 +469,7 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info)
|
||||||
else
|
else
|
||||||
stmtRegisterValidPath.bind(); // null
|
stmtRegisterValidPath.bind(); // null
|
||||||
if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
|
if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("registering valid path `%1%' in database") % info.path);
|
throwSQLiteError(db, format("registering valid path `%1%' in database") % info.path);
|
||||||
unsigned long long id = sqlite3_last_insert_rowid(db);
|
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
|
||||||
|
@ -475,7 +484,7 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info)
|
||||||
stmtAddDerivationOutput.bind(i->first);
|
stmtAddDerivationOutput.bind(i->first);
|
||||||
stmtAddDerivationOutput.bind(i->second.path);
|
stmtAddDerivationOutput.bind(i->second.path);
|
||||||
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("adding derivation output for `%1%' in database") % info.path);
|
throwSQLiteError(db, format("adding derivation output for `%1%' in database") % info.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +498,7 @@ void LocalStore::addReference(unsigned long long referrer, unsigned long long re
|
||||||
stmtAddReference.bind(referrer);
|
stmtAddReference.bind(referrer);
|
||||||
stmtAddReference.bind(reference);
|
stmtAddReference.bind(reference);
|
||||||
if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
|
if (sqlite3_step(stmtAddReference) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, "adding reference to database");
|
throwSQLiteError(db, "adding reference to database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,14 +508,22 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
|
||||||
ValidPathInfo info2(info);
|
ValidPathInfo info2(info);
|
||||||
if (info2.registrationTime == 0) info2.registrationTime = time(0);
|
if (info2.registrationTime == 0) info2.registrationTime = time(0);
|
||||||
|
|
||||||
SQLiteTxn txn(db);
|
while (1) {
|
||||||
|
try {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
unsigned long long id = addValidPath(info2);
|
unsigned long long id = addValidPath(info2);
|
||||||
|
|
||||||
foreach (PathSet::const_iterator, i, info2.references)
|
foreach (PathSet::const_iterator, i, info2.references)
|
||||||
addReference(id, queryValidPathId(*i));
|
addReference(id, queryValidPathId(*i));
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
break;
|
||||||
|
} catch (SQLiteBusy & e) {
|
||||||
|
/* Retry; the `txn' destructor will roll back the current
|
||||||
|
transaction. */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -517,7 +534,7 @@ void LocalStore::registerFailedPath(const Path & path)
|
||||||
stmtRegisterFailedPath.bind(path);
|
stmtRegisterFailedPath.bind(path);
|
||||||
stmtRegisterFailedPath.bind(time(0));
|
stmtRegisterFailedPath.bind(time(0));
|
||||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("registering failed path `%1%'") % path);
|
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -527,7 +544,7 @@ bool LocalStore::hasPathFailed(const Path & path)
|
||||||
stmtHasPathFailed.bind(path);
|
stmtHasPathFailed.bind(path);
|
||||||
int res = sqlite3_step(stmtHasPathFailed);
|
int res = sqlite3_step(stmtHasPathFailed);
|
||||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||||
throw SQLiteError(db, "querying whether path failed");
|
throwSQLiteError(db, "querying whether path failed");
|
||||||
return res == SQLITE_ROW;
|
return res == SQLITE_ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,7 +562,7 @@ PathSet LocalStore::queryFailedPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, "error querying failed paths");
|
throwSQLiteError(db, "error querying failed paths");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -559,7 +576,7 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||||
SQLiteStmtUse use(stmtClearFailedPath);
|
SQLiteStmtUse use(stmtClearFailedPath);
|
||||||
stmtClearFailedPath.bind(*i);
|
stmtClearFailedPath.bind(*i);
|
||||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
@ -594,7 +611,7 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
|
|
||||||
int r = sqlite3_step(stmtQueryPathInfo);
|
int r = sqlite3_step(stmtQueryPathInfo);
|
||||||
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) throwSQLiteError(db, "querying path in database");
|
||||||
|
|
||||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||||
|
|
||||||
|
@ -622,7 +639,7 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("error getting references of `%1%'") % path);
|
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -635,7 +652,7 @@ unsigned long long LocalStore::queryValidPathId(const Path & path)
|
||||||
int res = sqlite3_step(stmtQueryPathInfo);
|
int res = sqlite3_step(stmtQueryPathInfo);
|
||||||
if (res == SQLITE_ROW) return sqlite3_column_int(stmtQueryPathInfo, 0);
|
if (res == SQLITE_ROW) return sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||||
if (res == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
if (res == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||||
throw SQLiteError(db, "querying path in database");
|
throwSQLiteError(db, "querying path in database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -645,7 +662,7 @@ bool LocalStore::isValidPath(const Path & path)
|
||||||
stmtQueryPathInfo.bind(path);
|
stmtQueryPathInfo.bind(path);
|
||||||
int res = sqlite3_step(stmtQueryPathInfo);
|
int res = sqlite3_step(stmtQueryPathInfo);
|
||||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||||
throw SQLiteError(db, "querying path in database");
|
throwSQLiteError(db, "querying path in database");
|
||||||
return res == SQLITE_ROW;
|
return res == SQLITE_ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +682,7 @@ PathSet LocalStore::queryValidPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, "error getting valid paths");
|
throwSQLiteError(db, "error getting valid paths");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -695,7 +712,7 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("error getting references of `%1%'") % path);
|
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -721,7 +738,7 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
||||||
|
|
||||||
return derivers;
|
return derivers;
|
||||||
}
|
}
|
||||||
|
@ -743,7 +760,7 @@ PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("error getting outputs of `%1%'") % path);
|
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
||||||
|
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
@ -890,7 +907,7 @@ void LocalStore::invalidatePath(const Path & path)
|
||||||
stmtInvalidatePath.bind(path);
|
stmtInvalidatePath.bind(path);
|
||||||
|
|
||||||
if (sqlite3_step(stmtInvalidatePath) != SQLITE_DONE)
|
if (sqlite3_step(stmtInvalidatePath) != SQLITE_DONE)
|
||||||
throw SQLiteError(db, format("invalidating path `%1%' in database") % path);
|
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'. */
|
||||||
|
@ -1248,20 +1265,25 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
|
||||||
|
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
SQLiteTxn txn(db);
|
while (1) {
|
||||||
|
try {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
if (isValidPath(path)) {
|
if (isValidPath(path)) {
|
||||||
PathSet referrers; queryReferrers(path, referrers);
|
PathSet referrers; queryReferrers(path, referrers);
|
||||||
referrers.erase(path); /* ignore self-references */
|
referrers.erase(path); /* ignore self-references */
|
||||||
if (!referrers.empty())
|
if (!referrers.empty())
|
||||||
throw PathInUse(format("cannot delete path `%1%' because it is in use by `%2%'")
|
throw PathInUse(format("cannot delete path `%1%' because it is in use by `%2%'")
|
||||||
% path % showPaths(referrers));
|
% path % showPaths(referrers));
|
||||||
invalidatePath(path);
|
invalidatePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
break;
|
||||||
|
} catch (SQLiteBusy & e) { };
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePathWrapped(path, bytesFreed, blocksFreed);
|
deletePathWrapped(path, bytesFreed, blocksFreed);
|
||||||
|
|
||||||
txn.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue