* Some wrapper objects to ensure that SQLite objects are properly
destroyed.
This commit is contained in:
		
							parent
							
								
									a053d2d8e5
								
							
						
					
					
						commit
						e0305bb7a8
					
				
					 2 changed files with 109 additions and 49 deletions
				
			
		|  | @ -18,6 +18,8 @@ | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| 
 | 
 | ||||||
|  | #include <sqlite3.h> | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
|  | @ -32,6 +34,37 @@ public: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | SQLite::~SQLite() | ||||||
|  | { | ||||||
|  |     try { | ||||||
|  |         if (db && sqlite3_close(db) != SQLITE_OK) | ||||||
|  |             throw SQLiteError(db, "closing database"); | ||||||
|  |     } catch (...) { | ||||||
|  |         ignoreException(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void SQLiteStmt::create(sqlite3 * db, const string & s) | ||||||
|  | { | ||||||
|  |     assert(!stmt); | ||||||
|  |     if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK) | ||||||
|  |         throw SQLiteError(db, "creating statement"); | ||||||
|  |     this->db = db; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | SQLiteStmt::~SQLiteStmt() | ||||||
|  | { | ||||||
|  |     try { | ||||||
|  |         if (stmt && sqlite3_finalize(stmt) != SQLITE_OK) | ||||||
|  |             throw SQLiteError(db, "finalizing statement"); | ||||||
|  |     } catch (...) { | ||||||
|  |         ignoreException(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void checkStoreNotSymlink() | void checkStoreNotSymlink() | ||||||
| { | { | ||||||
|     if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return; |     if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return; | ||||||
|  | @ -52,7 +85,6 @@ void checkStoreNotSymlink() | ||||||
| 
 | 
 | ||||||
| LocalStore::LocalStore() | LocalStore::LocalStore() | ||||||
| { | { | ||||||
|     db = 0; |  | ||||||
|     substitutablePathsLoaded = false; |     substitutablePathsLoaded = false; | ||||||
|      |      | ||||||
|     schemaPath = nixDBPath + "/schema"; |     schemaPath = nixDBPath + "/schema"; | ||||||
|  | @ -73,6 +105,8 @@ LocalStore::LocalStore() | ||||||
|    |    | ||||||
|     checkStoreNotSymlink(); |     checkStoreNotSymlink(); | ||||||
| 
 | 
 | ||||||
|  |     /* Acquire the big fat lock in shared mode to make sure that no
 | ||||||
|  |        schema upgrade is in progress. */ | ||||||
|     try { |     try { | ||||||
|         Path globalLockPath = nixDBPath + "/big-lock"; |         Path globalLockPath = nixDBPath + "/big-lock"; | ||||||
|         globalLock = openLockFile(globalLockPath.c_str(), true); |         globalLock = openLockFile(globalLockPath.c_str(), true); | ||||||
|  | @ -87,21 +121,33 @@ LocalStore::LocalStore() | ||||||
|         lockFile(globalLock, ltRead, true); |         lockFile(globalLock, ltRead, true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /* Open the Nix database. */ | ||||||
|  |     if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db.db, | ||||||
|  |             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK) | ||||||
|  |         throw Error("cannot open SQLite database"); | ||||||
|  | 
 | ||||||
|  |     if (sqlite3_busy_timeout(db, 60000) != SQLITE_OK) | ||||||
|  |         throw SQLiteError(db, "setting timeout"); | ||||||
|  |      | ||||||
|  |     /* Check the current database schema and if necessary do an
 | ||||||
|  |        upgrade. */ | ||||||
|     int curSchema = getSchema(); |     int curSchema = getSchema(); | ||||||
|     if (curSchema > nixSchemaVersion) |     if (curSchema > nixSchemaVersion) | ||||||
|         throw Error(format("current Nix store schema is version %1%, but I only support %2%") |         throw Error(format("current Nix store schema is version %1%, but I only support %2%") | ||||||
|             % curSchema % nixSchemaVersion); |             % curSchema % nixSchemaVersion); | ||||||
|     if (curSchema == 0) { /* new store */ |     if (curSchema == 0) { /* new store */ | ||||||
|         curSchema = nixSchemaVersion; |         curSchema = nixSchemaVersion; | ||||||
|  |         initSchema(); | ||||||
|         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); |         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); | ||||||
|     } |     } | ||||||
|     if (curSchema == 1) throw Error("your Nix store is no longer supported"); |     else if (curSchema == 1) throw Error("your Nix store is no longer supported"); | ||||||
|     if (curSchema < 5) |     else if (curSchema < 5) | ||||||
|         throw Error( |         throw Error( | ||||||
|             "Your Nix store has a database in Berkeley DB format,\n" |             "Your Nix store has a database in Berkeley DB format,\n" | ||||||
|             "which is no longer supported. To convert to the new format,\n" |             "which is no longer supported. To convert to the new format,\n" | ||||||
|             "please upgrade Nix to version 0.12 first."); |             "please upgrade Nix to version 0.12 first."); | ||||||
|     if (curSchema < 6) upgradeStore6(); |     else if (curSchema < 6) upgradeStore6(); | ||||||
|  |     else prepareStatements(); | ||||||
| 
 | 
 | ||||||
|     doFsync = queryBoolSetting("fsync-metadata", false); |     doFsync = queryBoolSetting("fsync-metadata", false); | ||||||
| } | } | ||||||
|  | @ -112,9 +158,6 @@ LocalStore::~LocalStore() | ||||||
|     try { |     try { | ||||||
|         flushDelayedUpdates(); |         flushDelayedUpdates(); | ||||||
| 
 | 
 | ||||||
|         if (db && sqlite3_close(db) != SQLITE_OK) |  | ||||||
|             throw SQLiteError(db, "closing database"); |  | ||||||
| 
 |  | ||||||
|         foreach (RunningSubstituters::iterator, i, runningSubstituters) { |         foreach (RunningSubstituters::iterator, i, runningSubstituters) { | ||||||
|             i->second.to.close(); |             i->second.to.close(); | ||||||
|             i->second.from.close(); |             i->second.from.close(); | ||||||
|  | @ -143,15 +186,19 @@ int LocalStore::getSchema() | ||||||
| 
 | 
 | ||||||
| void LocalStore::initSchema() | void LocalStore::initSchema() | ||||||
| { | { | ||||||
|     if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db, |  | ||||||
|             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK) |  | ||||||
|         throw Error("cannot open SQLite database"); |  | ||||||
| 
 |  | ||||||
|     if (sqlite3_busy_timeout(db, 60000) != SQLITE_OK) |  | ||||||
|         throw SQLiteError(db, "sett"); |  | ||||||
|      |  | ||||||
|     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"); |         throw SQLiteError(db, "initialising database schema"); | ||||||
|  | 
 | ||||||
|  |     prepareStatements(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void LocalStore::prepareStatements() | ||||||
|  | { | ||||||
|  |     stmtRegisterValidPath.create(db, | ||||||
|  |         "insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);"); | ||||||
|  |     stmtAddReference.create(db, | ||||||
|  |         "insert into Refs (referrer, reference) values (?, ?);"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1220,33 +1267,28 @@ void LocalStore::upgradeStore6() | ||||||
|     if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK) |     if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK) | ||||||
|         throw SQLiteError(db, "running `begin' command"); |         throw SQLiteError(db, "running `begin' command"); | ||||||
| 
 | 
 | ||||||
|     sqlite3_stmt * registerStmt; |  | ||||||
|     if (sqlite3_prepare_v2(db, "insert into ValidPaths (path, hash, registrationTime, deriver) values (?, ?, ?, ?);", |  | ||||||
|             -1, ®isterStmt, 0) != SQLITE_OK) |  | ||||||
|         throw SQLiteError(db, "creating statement"); |  | ||||||
|      |  | ||||||
|     std::map<Path, sqlite3_int64> pathToId; |     std::map<Path, sqlite3_int64> pathToId; | ||||||
|      |      | ||||||
|     foreach (PathSet::iterator, i, validPaths) { |     foreach (PathSet::iterator, i, validPaths) { | ||||||
|         ValidPathInfo info = queryPathInfo(*i, true); |         ValidPathInfo info = queryPathInfo(*i, true); | ||||||
|          |          | ||||||
|         if (sqlite3_reset(registerStmt) != SQLITE_OK) |         if (sqlite3_reset(stmtRegisterValidPath) != SQLITE_OK) | ||||||
|             throw SQLiteError(db, "resetting statement"); |             throw SQLiteError(db, "resetting statement"); | ||||||
|         if (sqlite3_bind_text(registerStmt, 1, i->c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) |         if (sqlite3_bind_text(stmtRegisterValidPath, 1, i->c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) | ||||||
|             throw SQLiteError(db, "binding argument 1"); |             throw SQLiteError(db, "binding argument 1"); | ||||||
|         string h = "sha256:" + printHash(info.hash); |         string h = "sha256:" + printHash(info.hash); | ||||||
|         if (sqlite3_bind_text(registerStmt, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) |         if (sqlite3_bind_text(stmtRegisterValidPath, 2, h.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) | ||||||
|             throw SQLiteError(db, "binding argument 2"); |             throw SQLiteError(db, "binding argument 2"); | ||||||
|         if (sqlite3_bind_int(registerStmt, 3, info.registrationTime) != SQLITE_OK) |         if (sqlite3_bind_int(stmtRegisterValidPath, 3, info.registrationTime) != SQLITE_OK) | ||||||
|             throw SQLiteError(db, "binding argument 3"); |             throw SQLiteError(db, "binding argument 3"); | ||||||
|         if (info.deriver != "") { |         if (info.deriver != "") { | ||||||
|             if (sqlite3_bind_text(registerStmt, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) |             if (sqlite3_bind_text(stmtRegisterValidPath, 4, info.deriver.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) | ||||||
|                 throw SQLiteError(db, "binding argument 4"); |                 throw SQLiteError(db, "binding argument 4"); | ||||||
|         } else { |         } else { | ||||||
|             if (sqlite3_bind_null(registerStmt, 4) != SQLITE_OK) |             if (sqlite3_bind_null(stmtRegisterValidPath, 4) != SQLITE_OK) | ||||||
|                 throw SQLiteError(db, "binding argument 4"); |                 throw SQLiteError(db, "binding argument 4"); | ||||||
|         } |         } | ||||||
|         if (sqlite3_step(registerStmt) != SQLITE_DONE) |         if (sqlite3_step(stmtRegisterValidPath) != SQLITE_DONE) | ||||||
|             throw SQLiteError(db, "registering valid path in database"); |             throw SQLiteError(db, "registering valid path in database"); | ||||||
| 
 | 
 | ||||||
|         pathToId[*i] = sqlite3_last_insert_rowid(db); |         pathToId[*i] = sqlite3_last_insert_rowid(db); | ||||||
|  | @ -1254,29 +1296,21 @@ void LocalStore::upgradeStore6() | ||||||
|         std::cerr << "."; |         std::cerr << "."; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (sqlite3_finalize(registerStmt) != SQLITE_OK) |  | ||||||
|         throw SQLiteError(db, "finalizing statement"); |  | ||||||
| 
 |  | ||||||
|     std::cerr << "|"; |     std::cerr << "|"; | ||||||
|      |      | ||||||
|     sqlite3_stmt * addRefStmt; |  | ||||||
|     if (sqlite3_prepare_v2(db, "insert into Refs (referrer, reference) values (?, ?);", |  | ||||||
|             -1, &addRefStmt, 0) != SQLITE_OK) |  | ||||||
|         throw SQLiteError(db, "creating statement"); |  | ||||||
|      |  | ||||||
|     foreach (PathSet::iterator, i, validPaths) { |     foreach (PathSet::iterator, i, validPaths) { | ||||||
|         ValidPathInfo info = queryPathInfo(*i, true); |         ValidPathInfo info = queryPathInfo(*i, true); | ||||||
|          |          | ||||||
|         foreach (PathSet::iterator, j, info.references) { |         foreach (PathSet::iterator, j, info.references) { | ||||||
|             if (sqlite3_reset(addRefStmt) != SQLITE_OK) |             if (sqlite3_reset(stmtAddReference) != SQLITE_OK) | ||||||
|                 throw SQLiteError(db, "resetting statement"); |                 throw SQLiteError(db, "resetting statement"); | ||||||
|             if (sqlite3_bind_int(addRefStmt, 1, pathToId[*i]) != SQLITE_OK) |             if (sqlite3_bind_int(stmtAddReference, 1, pathToId[*i]) != SQLITE_OK) | ||||||
|                 throw SQLiteError(db, "binding argument 1"); |                 throw SQLiteError(db, "binding argument 1"); | ||||||
|             if (pathToId.find(*j) == pathToId.end()) |             if (pathToId.find(*j) == pathToId.end()) | ||||||
|                 throw Error(format("path `%1%' referenced by `%2%' is invalid") % *j % *i); |                 throw Error(format("path `%1%' referenced by `%2%' is invalid") % *j % *i); | ||||||
|             if (sqlite3_bind_int(addRefStmt, 2, pathToId[*j]) != SQLITE_OK) |             if (sqlite3_bind_int(stmtAddReference, 2, pathToId[*j]) != SQLITE_OK) | ||||||
|                 throw SQLiteError(db, "binding argument 2"); |                 throw SQLiteError(db, "binding argument 2"); | ||||||
|             if (sqlite3_step(addRefStmt) != SQLITE_DONE) |             if (sqlite3_step(stmtAddReference) != SQLITE_DONE) | ||||||
|                 throw SQLiteError(db, "adding reference to database"); |                 throw SQLiteError(db, "adding reference to database"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -1287,12 +1321,6 @@ void LocalStore::upgradeStore6() | ||||||
| 
 | 
 | ||||||
|     if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK) |     if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK) | ||||||
|         throw SQLiteError(db, "running `commit' command"); |         throw SQLiteError(db, "running `commit' command"); | ||||||
|      |  | ||||||
|     if (sqlite3_finalize(addRefStmt) != SQLITE_OK) |  | ||||||
|         throw SQLiteError(db, "finalizing statement"); |  | ||||||
| 
 |  | ||||||
|     throw Error("foo"); |  | ||||||
| 
 |  | ||||||
|     writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); |     writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); | ||||||
| 
 | 
 | ||||||
|     lockFile(globalLock, ltRead, true); |     lockFile(globalLock, ltRead, true); | ||||||
|  |  | ||||||
|  | @ -6,7 +6,9 @@ | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| 
 | 
 | ||||||
| #include <sqlite3.h> | 
 | ||||||
|  | class sqlite3; | ||||||
|  | class sqlite3_stmt; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
|  | @ -14,8 +16,9 @@ namespace nix { | ||||||
| 
 | 
 | ||||||
| /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | ||||||
|    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. |    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. | ||||||
|    Version 4 is Nix 0.11.  Version 5 is Nix 0.12*/ |    Version 4 is Nix 0.11.  Version 5 is Nix 0.12-0.14.  Version 6 is | ||||||
| const int nixSchemaVersion = 5; |    Nix 0.15. */ | ||||||
|  | const int nixSchemaVersion = 6; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| extern string drvsLogDir; | extern string drvsLogDir; | ||||||
|  | @ -43,6 +46,28 @@ struct RunningSubstituter | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Wrapper object to close the SQLite database automatically. */ | ||||||
|  | struct SQLite | ||||||
|  | { | ||||||
|  |     sqlite3 * db; | ||||||
|  |     SQLite() { db = 0; } | ||||||
|  |     ~SQLite(); | ||||||
|  |     operator sqlite3 * () { return db; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Wrapper object to create and destroy SQLite prepared statements. */ | ||||||
|  | struct SQLiteStmt | ||||||
|  | { | ||||||
|  |     sqlite3 * db; | ||||||
|  |     sqlite3_stmt * stmt; | ||||||
|  |     SQLiteStmt() { stmt = 0; } | ||||||
|  |     void create(sqlite3 * db, const string & s); | ||||||
|  |     ~SQLiteStmt(); | ||||||
|  |     operator sqlite3_stmt * () { return stmt; } | ||||||
|  | }; | ||||||
|  |      | ||||||
|  | 
 | ||||||
| class LocalStore : public StoreAPI | class LocalStore : public StoreAPI | ||||||
| { | { | ||||||
| private: | private: | ||||||
|  | @ -163,12 +188,19 @@ private: | ||||||
|     /* Whether to do an fsync() after writing Nix metadata. */ |     /* Whether to do an fsync() after writing Nix metadata. */ | ||||||
|     bool doFsync; |     bool doFsync; | ||||||
| 
 | 
 | ||||||
|     sqlite3 * db; |     /* The SQLite database object. */ | ||||||
|  |     SQLite db; | ||||||
|  | 
 | ||||||
|  |     /* Some precompiled SQLite statements. */ | ||||||
|  |     SQLiteStmt stmtRegisterValidPath; | ||||||
|  |     SQLiteStmt stmtAddReference; | ||||||
| 
 | 
 | ||||||
|     int getSchema(); |     int getSchema(); | ||||||
| 
 | 
 | ||||||
|     void initSchema(); |     void initSchema(); | ||||||
| 
 | 
 | ||||||
|  |     void prepareStatements(); | ||||||
|  | 
 | ||||||
|     void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false); |     void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false); | ||||||
| 
 | 
 | ||||||
|     ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false); |     ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue