reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 3bfe61ce7b366b43f7ec9c2e7b84422543209364
parent 56f0dbd30c4811dd4bf2abfb0b093cae72cddb25
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Wed, 20 Jan 2016 15:10:48 -0500

handle every file conflicts cases the same way

Diffstat:
Msrc/database.cpp | 5+++++
Msrc/database.hpp | 1+
Msrc/registry.cpp | 29+++++++++++++++++++++++++++--
Msrc/registry.hpp | 6+++++-
Msrc/transaction.cpp | 71++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/transaction.hpp | 4+---
Mtest/database.cpp | 15+++++++++++++++
Mtest/registry.cpp | 20+++++++++++++++++---
8 files changed, 109 insertions(+), 42 deletions(-)

diff --git a/src/database.cpp b/src/database.cpp @@ -85,6 +85,11 @@ int Database::version() const return version; } +int Database::errorCode() const +{ + return sqlite3_extended_errcode(m_db); +} + void Database::begin() { // EXCLUSIVE -> don't wait until the first query to aquire a lock diff --git a/src/database.hpp b/src/database.hpp @@ -39,6 +39,7 @@ public: void exec(const char *sql); uint64_t lastInsertId() const; int version() const; + int errorCode() const; void begin(); void commit(); diff --git a/src/registry.cpp b/src/registry.cpp @@ -24,6 +24,7 @@ #include "remote.hpp" #include <reaper_plugin_functions.h> +#include <sqlite3.h> using namespace std; @@ -57,6 +58,10 @@ Registry::Registry(const Path &path) ); m_forgetFiles = m_db.prepare("DELETE FROM files WHERE entry = ?"); + m_savepoint = m_db.prepare("SAVEPOINT savept"); + m_release = m_db.prepare("RELEASE SAVEPOINT savept"); + m_restore = m_db.prepare("ROLLBACK TO SAVEPOINT savept"); + // lock the database m_db.begin(); } @@ -98,8 +103,11 @@ void Registry::migrate() m_db.commit(); } -void Registry::push(Version *ver) +void Registry::push(Version *ver, vector<Path> *conflicts) { + m_savepoint->exec(); + bool hasConflicts = false; + Package *pkg = ver->package(); Category *cat = pkg->category(); RemoteIndex *ri = cat->index(); @@ -120,8 +128,25 @@ void Registry::push(Version *ver) for(const Path &path : ver->files()) { m_insertFile->bind(1, entryId); m_insertFile->bind(2, path.join('/')); - m_insertFile->exec(); + try { + m_insertFile->exec(); + } + catch(const reapack_error &) { + if(conflicts && m_db.errorCode() == SQLITE_CONSTRAINT_UNIQUE) { + hasConflicts = true; + conflicts->push_back(path); + } + else { + m_restore->exec(); + throw; + } + } } + + if(hasConflicts) + m_restore->exec(); + else + m_release->exec(); } Registry::Entry Registry::query(Package *pkg) const diff --git a/src/registry.hpp b/src/registry.hpp @@ -49,7 +49,7 @@ public: Entry query(Package *) const; std::vector<Entry> queryAll(const Remote &) const; std::set<Path> getFiles(const Entry &) const; - void push(Version *); + void push(Version *, std::vector<Path> *conflicts = nullptr); void forget(const Entry &); void commit(); @@ -68,6 +68,10 @@ private: Statement *m_insertFile; Statement *m_clearFiles; Statement *m_forgetFiles; + + Statement *m_savepoint; + Statement *m_release; + Statement *m_restore; }; #endif diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -30,14 +30,14 @@ using namespace std; Transaction::Transaction(const Path &root) - : m_root(root), m_isCancelled(false), m_hasConflicts(false) + : m_root(root), m_isCancelled(false) { m_dbPath = m_root + "ReaPack"; m_registry = new Registry(m_dbPath + "registry.db"); m_downloadQueue.onDone([=](void *) { - if(m_installQueue.empty() || m_hasConflicts) + if(m_installQueue.empty()) finish(); else install(); @@ -72,31 +72,53 @@ void Transaction::upgradeAll(Download *dl) if(!saveFile(dl, path)) return; + RemoteIndex *ri; + try { - RemoteIndex *ri = RemoteIndex::load(dl->name(), path.join().c_str()); + ri = RemoteIndex::load(dl->name(), path.join().c_str()); m_remoteIndexes.push_back(ri); + } + catch(const reapack_error &e) { + addError(e.what(), dl->url()); + return; + } - for(Package *pkg : ri->packages()) { - Registry::Entry entry = m_registry->query(pkg); - - Version *ver = pkg->lastVersion(); + for(Package *pkg : ri->packages()) + upgrade(pkg); +} - set<Path> files = ver->files(); - registerFiles(files); +void Transaction::upgrade(Package *pkg) +{ + Version *ver = pkg->lastVersion(); + Registry::Entry entry = m_registry->query(pkg); - if(entry.status == Registry::UpToDate) { - if(allFilesExists(files)) - continue; - else - entry.status = Registry::Uninstalled; + try { + vector<Path> conflicts; + m_registry->push(ver, &conflicts); + + if(!conflicts.empty()) { + for(const Path &path : conflicts) { + addError("Conflict: " + path.join() + + " is already owned by another package", + ver->fullName()); } - m_installQueue.push({ver, entry}); + return; } } catch(const reapack_error &e) { - addError(e.what(), dl->url()); + addError(e.what(), ver->fullName()); + return; } + + if(entry.status == Registry::UpToDate) { + if(allFilesExists(ver->files())) + return; + else + entry.status = Registry::Uninstalled; + } + + m_installQueue.push({ver, entry}); } void Transaction::install() @@ -117,8 +139,6 @@ void Transaction::install() else m_new.push_back(entry); - m_registry->push(ver); - const set<Path> &removedFiles = task->removedFiles(); m_removals.insert(removedFiles.begin(), removedFiles.end()); @@ -236,21 +256,6 @@ bool Transaction::allFilesExists(const set<Path> &list) const return true; } -void Transaction::registerFiles(const set<Path> &list) -{ - for(const Path &path : list) { - if(!m_files.count(path)) - continue; - - addError("Conflict: This file is owned by more than one package", - path.join()); - - m_hasConflicts = true; - } - - m_files.insert(list.begin(), list.end()); -} - void Transaction::addTask(Task *task) { m_tasks.push_back(task); diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -75,8 +75,8 @@ private: void finish(); void upgradeAll(Download *); + void upgrade(Package *pkg); bool allFilesExists(const std::set<Path> &) const; - void registerFiles(const std::set<Path> &); void addTask(Task *); Registry *m_registry; @@ -95,8 +95,6 @@ private: std::vector<Task *> m_tasks; std::queue<Task *> m_taskQueue; - std::set<Path> m_files; - bool m_hasConflicts; Signal m_onFinish; Signal m_onDestroy; diff --git a/test/database.cpp b/test/database.cpp @@ -4,6 +4,8 @@ #include <errors.hpp> +#include <sqlite3.h> + using namespace std; static const char *M = "[database]"; @@ -160,3 +162,16 @@ TEST_CASE("bind temporary strings", M) { REQUIRE(got == "hello"); } + +TEST_CASE("sqlite error code", M) { + Database db; + db.exec("CREATE TABLE a(b INTEGER UNIQUE); INSERT INTO a VALUES(1)"); + REQUIRE(db.errorCode() == SQLITE_OK); + + try { + db.exec("INSERT INTO a VALUES(1)"); + } + catch(const reapack_error &) {} + + REQUIRE(db.errorCode() == SQLITE_CONSTRAINT_UNIQUE); +} diff --git a/test/registry.cpp b/test/registry.cpp @@ -108,7 +108,7 @@ TEST_CASE("forget registry entry", M) { REQUIRE(afterForget.version == 0); } -TEST_CASE("enforce unique files", M) { +TEST_CASE("file conflicts", M) { Registry reg; { @@ -120,13 +120,27 @@ TEST_CASE("enforce unique files", M) { Category cat("Hello", &ri); Package pkg(Package::ScriptType, "Duplicate Package", &cat); Version *ver = new Version("1.0", &pkg); - Source *src = new Source(Source::GenericPlatform, "file", "url", ver); - ver->addSource(src); + Source *src1 = new Source(Source::GenericPlatform, "file", "url", ver); + Source *src2 = new Source(Source::GenericPlatform, "file2", "url", ver); + ver->addSource(src1); + ver->addSource(src2); pkg.addVersion(ver); + CHECK(reg.query(&pkg).status == Registry::Uninstalled); + try { reg.push(ver); FAIL("duplicate was accepted"); } catch(const reapack_error &) {} + + REQUIRE(reg.query(&pkg).status == Registry::Uninstalled); + + vector<Path> conflicts; + reg.push(ver, &conflicts); + + REQUIRE(conflicts.size() == 1); + REQUIRE(conflicts[0] == src1->targetPath()); + + REQUIRE(reg.query(&pkg).status == Registry::Uninstalled); }