diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp index 704d3e42..a75ca82f 100644 --- a/src/libs/database/impl/Migration.cpp +++ b/src/libs/database/impl/Migration.cpp @@ -35,7 +35,7 @@ namespace lms::db { namespace { - static constexpr Version LMS_DATABASE_VERSION{ 81 }; + static constexpr Version LMS_DATABASE_VERSION{ 82 }; } VersionInfo::VersionInfo() @@ -1072,6 +1072,30 @@ FROM tracklist)"); utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } + void migrateFromV81(Session& session) + { + // Add country + release country + utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "country" ( + "id" integer primary key autoincrement, + "version" integer not null, + "name" text not null + ))"); + + utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_country" ( + "country_id" bigint, + "release_id" bigint, + primary key ("country_id", "release_id"), + constraint "fk_release_country_key1" foreign key ("country_id") references "country" ("id") on delete cascade deferrable initially deferred, + constraint "fk_release_country_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred + ))"); + + utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_country" on "release_country" ("country_id"))"); + utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_release" on "release_country" ("release_id"))"); + + // Just increment the scan version of the settings to make the next scan rescan everything + utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); + } + bool doDbMigration(Session& session) { constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" }; @@ -1129,6 +1153,7 @@ FROM tracklist)"); { 78, migrateFromV78 }, { 79, migrateFromV79 }, { 80, migrateFromV80 }, + { 81, migrateFromV81 }, }; bool migrationPerformed{}; diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp index 89f7a9ed..6c19be1f 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -253,6 +253,50 @@ namespace lms::db } // namespace + Country::Country(std::string_view name) + : _name{ name } + { + // As we use the name to uniquely identoify release type, we must throw (and not truncate) + if (name.size() > _maxNameLength) + throw Exception{ "Country name is too long: " + std::string{ name } + "'" }; + } + + Country::pointer Country::create(Session& session, std::string_view name) + { + return session.getDboSession()->add(std::unique_ptr{ new Country{ name } }); + } + + std::size_t Country::getCount(Session& session) + { + return utils::fetchQuerySingleResult(session.getDboSession()->query("SELECT COUNT(*) FROM country")); + } + + Country::pointer Country::find(Session& session, CountryId id) + { + session.checkReadTransaction(); + + return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT c from country c").where("c.id = ?").bind(id)); + } + + Country::pointer Country::find(Session& session, std::string_view name) + { + session.checkReadTransaction(); + + if (name.size() > _maxNameLength) + throw Exception{ "Requeted Country name is too long: " + std::string{ name } + "'" }; + + return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT c from country c").where("c.name = ?").bind(name)); + } + + RangeResults Country::findOrphanIds(Session& session, std::optional range) + { + session.checkReadTransaction(); + + // select the labels that have no releases + auto query{ session.getDboSession()->query("select c.id from country c LEFT OUTER JOIN release_country r_c ON c.id = r_c.country_id WHERE r_c.release_id IS NULL") }; + return utils::execRangeQuery(query, range); + } + Label::Label(std::string_view name) : _name{ name } { @@ -612,6 +656,11 @@ namespace lms::db _labels.clear(); } + void Release::clearCountries() + { + _countries.clear(); + } + void Release::clearReleaseTypes() { _releaseTypes.clear(); @@ -622,6 +671,11 @@ namespace lms::db _labels.insert(getDboPtr(label)); } + void Release::addCountry(ObjectPtr country) + { + _countries.insert(getDboPtr(country)); + } + void Release::addReleaseType(ObjectPtr releaseType) { _releaseTypes.insert(getDboPtr(releaseType)); @@ -659,8 +713,22 @@ namespace lms::db { std::vector res; - for (const auto& label : _labels) + auto query{ _labels.find() }; + utils::forEachQueryResult(query, [&](const Label::pointer& label) { res.push_back(std::string{ label->getName() }); + }); + + return res; + } + + std::vector Release::getCountryNames() const + { + std::vector res; + + auto query{ _countries.find() }; + utils::forEachQueryResult(query, [&](const Country::pointer& country) { + res.push_back(std::string{ country->getName() }); + }); return res; } @@ -669,8 +737,10 @@ namespace lms::db { std::vector res; - for (const auto& releaseType : _releaseTypes) + auto query{ _releaseTypes.find() }; + utils::forEachQueryResult(query, [&](const ReleaseType::pointer& releaseType) { res.push_back(std::string{ releaseType->getName() }); + }); return res; } diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index 25af8d32..42835a20 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -102,6 +102,7 @@ namespace lms::db _session.mapClass("auth_token"); _session.mapClass("cluster"); _session.mapClass("cluster_type"); + _session.mapClass("country"); _session.mapClass("directory"); _session.mapClass("image"); _session.mapClass