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);
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; unsigned int curArg = 1;
SQLiteStmtUse(SQLiteStmt & stmt); Use(SQLiteStmt & stmt);
~SQLiteStmtUse();
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);
};
Use use()
{
return Use(*this);
}
}; };
/* 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()