Improve the SQLite wrapper API
In particular, this eliminates a bunch of boilerplate code.
This commit is contained in:
		
							parent
							
								
									d9c5e3bbf0
								
							
						
					
					
						commit
						3d119f0a3b
					
				
					 5 changed files with 169 additions and 236 deletions
				
			
		|  | @ -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'. */ | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|     SQLiteStmtUse(SQLiteStmt & stmt); |         unsigned int curArg = 1; | ||||||
|     ~SQLiteStmtUse(); |         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); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Use use() | ||||||
|  |     { | ||||||
|  |         return Use(*this); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* RAII helper that ensures transactions are aborted unless explicitly
 | /* RAII helper that ensures transactions are aborted unless explicitly
 | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue