Improve the SQLite wrapper API

In particular, this eliminates a bunch of boilerplate code.
This commit is contained in:
Eelco Dolstra 2016-03-30 15:50:45 +02:00
parent d9c5e3bbf0
commit 3d119f0a3b
5 changed files with 169 additions and 236 deletions

View file

@ -332,6 +332,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");
} }
@ -526,23 +527,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.narHash)); ("sha256:" + printHash(info.narHash))
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
@ -559,12 +553,11 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
if (checkOutputs) checkDerivationOutputs(info.path, drv); if (checkOutputs) checkDerivationOutputs(info.path, drv);
for (auto & 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);
} }
} }
@ -572,24 +565,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);
}); });
} }
@ -597,12 +582,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;
}); });
} }
@ -610,18 +590,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;
}); });
@ -633,12 +606,8 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
retrySQLite<void>([&]() { retrySQLite<void>([&]() {
SQLiteTxn txn(db); SQLiteTxn txn(db);
for (auto & 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();
}); });
@ -669,41 +638,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.narHash = parseHashField(path, useQueryPathInfo.getStr(1));
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1); info.registrationTime = useQueryPathInfo.getInt(2);
assert(s);
info.narHash = 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;
}); });
@ -714,37 +670,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.narHash))
else (info.path)
stmtUpdatePathInfo.bind(); // null .exec();
stmtUpdatePathInfo.bind("sha256:" + printHash(info.narHash));
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;
} }
@ -770,20 +715,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;
}); });
} }
@ -791,19 +725,10 @@ PathSet LocalStore::queryAllValidPaths()
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);
} }
@ -827,19 +752,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;
}); });
@ -849,19 +766,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;
}); });
@ -871,19 +780,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;
}); });
@ -897,12 +798,9 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
Path prefix = settings.nixStore + "/" + hashPart; Path prefix = settings.nixStore + "/" + hashPart;
return retrySQLite<Path>([&]() { return retrySQLite<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 : "";
@ -1143,7 +1041,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
} }
for (auto & i : infos) { for (auto & i : infos) {
unsigned long long referrer = queryValidPathId(i.path); auto referrer = queryValidPathId(i.path);
for (auto & j : i.references) for (auto & j : i.references)
addReference(referrer, queryValidPathId(j)); addReference(referrer, queryValidPathId(j));
} }
@ -1178,12 +1076,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'. */

View file

@ -208,6 +208,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;
@ -230,11 +231,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);

View file

@ -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,74 @@ 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 & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
{ {
try { if (notNull) {
stmt.reset(); if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
} catch (...) { throwSQLiteError(stmt.db, "binding argument");
ignoreException(); } 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)

View file

@ -22,29 +22,46 @@ 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); /* Helper for binding / executing statements. */
void bind64(long long value); class Use
void bind(); {
friend struct SQLiteStmt;
private:
SQLiteStmt & stmt;
unsigned int curArg = 1;
Use(SQLiteStmt & stmt);
public:
/* 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);
}; };
/* Helper class to ensure that prepared statements are reset when Use use()
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; return Use(*this);
SQLiteStmtUse(SQLiteStmt & stmt); }
~SQLiteStmtUse();
}; };
/* RAII helper that ensures transactions are aborted unless explicitly /* RAII helper that ensures transactions are aborted unless explicitly

View file

@ -96,8 +96,8 @@ struct ValidPathInfo
Hash narHash; Hash narHash;
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
/* Whether the path is ultimately trusted, that is, it was built /* Whether the path is ultimately trusted, that is, it was built
locally or is content-addressable (e.g. added via addToStore() locally or is content-addressable (e.g. added via addToStore()