Added starred entries migration

This commit is contained in:
emeric
2022-01-23 20:19:27 +01:00
parent 4851581975
commit 9b8e2737e4
+376 -208
View File
@@ -73,60 +73,45 @@ namespace Database::Migration
Db& _db; Db& _db;
}; };
static
void void
doDbMigration(Session& session) migrateFromV5(Session& session)
{ {
static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"}; session.getDboSession().execute("DELETE FROM auth_token"); // format has changed
}
ScopedNoForeignKeys noPragmaKeys {session.getDb()}; static
void
migrateFromV6(Session& session)
{
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
while (1)
{
auto uniqueTransaction {session.createUniqueTransaction()};
Version version; static
try void
{ migrateFromV7(Session& session)
version = VersionInfo::getOrCreate(session)->getVersion(); {
LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION; session.getDboSession().execute("DROP TABLE similarity_settings");
if (version == LMS_DATABASE_VERSION) session.getDboSession().execute("DROP TABLE similarity_settings_feature");
{ session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(ScanSettings::RecommendationEngineType::Clusters)) + ")");
LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!"; }
return;
}
}
catch (std::exception& e)
{
LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what();
throw LmsException {outdatedMsg};
}
LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; static
void
migrateFromV8(Session& session)
{
// Better cover handling, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
if (version == 5) static
{ void
session.getDboSession().execute("DELETE FROM auth_token"); // format has changed migrateFromV9(Session& session)
} {
else if (version == 6) session.getDboSession().execute(R"(
{
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 7)
{
session.getDboSession().execute("DROP TABLE similarity_settings");
session.getDboSession().execute("DROP TABLE similarity_settings_feature");
session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(ScanSettings::RecommendationEngineType::Clusters)) + ")");
}
else if (version == 8)
{
// Better cover handling, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 9)
{
session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "track_bookmark" ( CREATE TABLE IF NOT EXISTS "track_bookmark" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
@@ -137,78 +122,109 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
constraint "fk_track_bookmark_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, constraint "fk_track_bookmark_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
constraint "fk_track_bookmark_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred constraint "fk_track_bookmark_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
);)"); );)");
} }
else if (version == 10)
{
ScanSettings::get(session).modify()->addAudioFileExtension(".m4b");
ScanSettings::get(session).modify()->addAudioFileExtension(".alac");
}
else if (version == 11)
{
// Sanitize bad MBID, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 12)
{
// Artist and release that have a badly parsed name but a MBID had no chance to updat the name
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 13)
{
// Always store UUID in lower case + better WMA parsing
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 14)
{
// SortName now set from metadata
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
else if (version == 15)
{
session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")");
}
else if (version == 16)
{
session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)");
session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything static
ScanSettings::get(session).modify()->incScanVersion(); void
} migrateFromV10(Session& session)
else if (version == 17) {
{ ScanSettings::get(session).modify()->addAudioFileExtension(".m4b");
// Drop colums total_disc/total_track from release ScanSettings::get(session).modify()->addAudioFileExtension(".alac");
session.getDboSession().execute(R"( }
static
void
migrateFromV11(Session& session)
{
// Sanitize bad MBID, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV12(Session& session)
{
// Artist and release that have a badly parsed name but a MBID had no chance to updat the name
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV13(Session& session)
{
// Always store UUID in lower case + better WMA parsing
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV14(Session& session)
{
// SortName now set from metadata
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV15(Session& session)
{
session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")");
}
static
void
migrateFromV16(Session& session)
{
session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)");
session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV17(Session& session)
{
// Drop colums total_disc/total_track from release
session.getDboSession().execute(R"(
CREATE TABLE "release_backup" ( CREATE TABLE "release_backup" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"name" text not null, "name" text not null,
"mbid" text not null "mbid" text not null
))"); ))");
session.getDboSession().execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release"); session.getDboSession().execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release");
session.getDboSession().execute("DROP TABLE release"); session.getDboSession().execute("DROP TABLE release");
session.getDboSession().execute("ALTER TABLE release_backup RENAME TO release"); session.getDboSession().execute("ALTER TABLE release_backup RENAME TO release");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
else if (version == 18)
{
session.getDboSession().execute(R"( static
void
migrateFromV18(Session& session)
{
session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "subsonic_settings" ( CREATE TABLE IF NOT EXISTS "subsonic_settings" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"api_enabled" boolean not null, "api_enabled" boolean not null,
"artist_list_mode" integer not null "artist_list_mode" integer not null
))"); ))");
} }
else if (version == 19)
{ static
session.getDboSession().execute(R"( void
migrateFromV19(Session& session)
{
session.getDboSession().execute(R"(
CREATE TABLE "user_backup" ( CREATE TABLE "user_backup" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
@@ -226,67 +242,96 @@ CREATE TABLE "user_backup" (
"repeat_all" boolean not null, "repeat_all" boolean not null,
"radio" boolean not null "radio" boolean not null
))"); ))");
session.getDboSession().execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "} session.getDboSession().execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "}
+ (User::defaultSubsonicTranscodeEnable ? "1" : "0") + (User::defaultSubsonicTranscodeEnable ? "1" : "0")
+ ", " + std::to_string(static_cast<int>(User::defaultSubsonicTranscodeFormat)) + ", " + std::to_string(static_cast<int>(User::defaultSubsonicTranscodeFormat))
+ ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate)
+ ", " + std::to_string(static_cast<int>(User::defaultSubsonicArtistListMode)) + ", " + std::to_string(static_cast<int>(User::defaultSubsonicArtistListMode))
+ ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); + ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user");
session.getDboSession().execute("DROP TABLE user"); session.getDboSession().execute("DROP TABLE user");
session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user");
} }
else if (version == 20)
{
session.getDboSession().execute("DROP TABLE subsonic_settings");
}
else if (version == 21)
{
session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL");
session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything static
ScanSettings::get(session).modify()->incScanVersion(); void
} migrateFromV20(Session& session)
else if (version == 22) {
{ session.getDboSession().execute("DROP TABLE subsonic_settings");
session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); }
// Just increment the scan version of the settings to make the next scheduled scan rescan everything static
ScanSettings::get(session).modify()->incScanVersion(); void
} migrateFromV21(Session& session)
else if (version == 23) {
{ session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL");
// Better cover detection session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); // Just increment the scan version of the settings to make the next scheduled scan rescan everything
} ScanSettings::get(session).modify()->incScanVersion();
else if (version == 24) }
{
// User's AuthMode
session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultAuthMode*/0)) + ")"); static
} void
else if (version == 25) migrateFromV22(Session& session)
{ {
// Better cover detection session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); // Just increment the scan version of the settings to make the next scheduled scan rescan everything
} ScanSettings::get(session).modify()->incScanVersion();
else if (version == 26) }
{
// Composer, mixer, etc. support
// Just increment the scan version of the settings to make the next scheduled scan rescan everything static
ScanSettings::get(session).modify()->incScanVersion(); void
} migrateFromV23(Session& session)
else if (version == 27) {
{ // Better cover detection
// Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists // Just increment the scan version of the settings to make the next scheduled scan rescan everything
// Just increment the scan version of the settings to make the next scheduled scan rescan everything ScanSettings::get(session).modify()->incScanVersion();
ScanSettings::get(session).modify()->incScanVersion(); }
}
else if (version == 28) static
{ void
// Drop Auth mode migrateFromV24(Session& session)
session.getDboSession().execute(R"( {
// User's AuthMode
session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultAuthMode*/0)) + ")");
}
static
void
migrateFromV25(Session& session)
{
// Better cover detection
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV26(Session& session)
{
// Composer, mixer, etc. support
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV27(Session& session)
{
// Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
static
void
migrateFromV28(Session& session)
{
// Drop Auth mode
session.getDboSession().execute(R"(
CREATE TABLE "user_backup" ( CREATE TABLE "user_backup" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
@@ -304,27 +349,33 @@ CREATE TABLE "user_backup" (
"repeat_all" boolean not null, "repeat_all" boolean not null,
"radio" boolean not null "radio" boolean not null
))"); ))");
session.getDboSession().execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); session.getDboSession().execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user");
session.getDboSession().execute("DROP TABLE user"); session.getDboSession().execute("DROP TABLE user");
session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user");
} }
else if (version == 29)
{
session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT");
session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT");
session.getDboSession().execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultScrobbler)) + ")");
session.getDboSession().execute("ALTER TABLE track ADD recording_mbid TEXT");
session.getDboSession().execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__"); static
void
migrateFromV29(Session& session)
{
session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT");
session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT");
session.getDboSession().execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultScrobbler)) + ")");
session.getDboSession().execute("ALTER TABLE track ADD recording_mbid TEXT");
// MBID changes session.getDboSession().execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); // MBID changes
} // Just increment the scan version of the settings to make the next scheduled scan rescan everything
else if (version == 30) ScanSettings::get(session).modify()->incScanVersion();
{ }
// drop "year" and "original_year" (rescan needed to convert them into dates)
session.getDboSession().execute(R"( static
void
migrateFromV30(Session& session)
{
// drop "year" and "original_year" (rescan needed to convert them into dates)
session.getDboSession().execute(R"(
CREATE TABLE "track_backup" ( CREATE TABLE "track_backup" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
@@ -345,22 +396,21 @@ CREATE TABLE "track_backup" (
"release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT, "release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT,
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
))"); ))");
session.getDboSession().execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track"); session.getDboSession().execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track");
session.getDboSession().execute("DROP TABLE track"); session.getDboSession().execute("DROP TABLE track");
session.getDboSession().execute("ALTER TABLE track_backup RENAME TO track"); session.getDboSession().execute("ALTER TABLE track_backup RENAME TO track");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
else if (version == 31)
{
// new star system, using dedicated ObjectSets per scrobbler
session.getDboSession().execute("DROP TABLE user_artist_starred");
session.getDboSession().execute("DROP TABLE user_release_starred");
session.getDboSession().execute("DROP TABLE user_track_starred");
session.getDboSession().execute(R"( static
CREATE TABLE IF NOT EXISTS "starred_artist" ( void
migrateFromV31(Session& session)
{
// new star system, using dedicated entries per scrobbler and date time
session.getDboSession().execute(R"(
CREATE TABLE "starred_artist" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"scrobbler" integer not null, "scrobbler" integer not null,
@@ -371,8 +421,8 @@ CREATE TABLE IF NOT EXISTS "starred_artist" (
constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))"); ))");
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "starred_release" ( CREATE TABLE "starred_release" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"scrobbler" integer not null, "scrobbler" integer not null,
@@ -383,8 +433,8 @@ CREATE TABLE IF NOT EXISTS "starred_release" (
constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))"); ))");
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "starred_track" ( CREATE TABLE "starred_track" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"scrobbler" integer not null, "scrobbler" integer not null,
@@ -395,9 +445,57 @@ CREATE TABLE IF NOT EXISTS "starred_track" (
constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))"); ))");
// new listen system, no longer using tracklists // Can't migrate using class mapping as mapping may evolve in the future
session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "listen" ( // use time_t to avoid rounding issues later
const Wt::WDateTime now {Wt::WDateTime::fromTime_t(Wt::WDateTime::currentDateTime().toTime_t())};
std::map<IdType::ValueType, Scrobbler> userScrobblers;
auto getScrobbler {[&](IdType::ValueType userId)
{
auto itScrobbler {userScrobblers.find(userId)};
if (itScrobbler != std::cend(userScrobblers))
return itScrobbler->second;
auto query {session.getDboSession().query<Scrobbler>("SELECT scrobbler FROM user WHERE id = ?").bind(userId)};
auto [itInserted, inserted] {userScrobblers.emplace(userId, query.resultValue())};
assert(inserted);
return itInserted->second;
}};
auto migrateStarEntries = [&session, &getScrobbler, &now](const std::string& colName, const std::string& oldTableName, const std::string& newTableName)
{
using UserIdObjectId = std::tuple<IdType::ValueType, IdType::ValueType>;
std::vector<UserIdObjectId> starredEntries;
auto query {session.getDboSession().query<UserIdObjectId>("SELECT user_id, " + colName + " from " + oldTableName)};
auto results {query.resultList()};
starredEntries.reserve(results.size());
for (const auto& entry : results)
starredEntries.push_back(entry);
LMS_LOG(DB, INFO) << "Found " << starredEntries.size() << " " << colName << " to migrate";
for (const auto [userId, entryId] : starredEntries)
{
session.getDboSession().execute("INSERT INTO " + newTableName + " ('version', 'scrobbler', 'date_time', '" + colName + "', 'user_id') VALUES (?, ?, ?, ?, ?)")
.bind(0)
.bind(getScrobbler(userId))
.bind(now.toString().toUTF8())
.bind(entryId)
.bind(userId);
}
session.getDboSession().execute("DROP TABLE " + oldTableName);
};
migrateStarEntries("artist_id","user_artist_starred", "starred_artist");
migrateStarEntries("release_id","user_release_starred", "starred_release");
migrateStarEntries("track_id","user_track_starred", "starred_track");
// new listen system, no longer using tracklists
session.getDboSession().execute(R"(
CREATE TABLE "listen" (
"id" integer primary key autoincrement, "id" integer primary key autoincrement,
"version" integer not null, "version" integer not null,
"date_time" text, "date_time" text,
@@ -408,12 +506,82 @@ CREATE TABLE IF NOT EXISTS "listen" (
constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))"); ))");
} }
else
void
doDbMigration(Session& session)
{
static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"};
ScopedNoForeignKeys noPragmaKeys {session.getDb()};
using MigrationFunction = std::function<void(Session&)>;
const std::map<unsigned, MigrationFunction> migrationFunctions
{
{5, migrateFromV5},
{6, migrateFromV6},
{7, migrateFromV7},
{8, migrateFromV8},
{9, migrateFromV9},
{10, migrateFromV10},
{11, migrateFromV11},
{12, migrateFromV12},
{13, migrateFromV13},
{14, migrateFromV14},
{15, migrateFromV15},
{16, migrateFromV16},
{17, migrateFromV17},
{18, migrateFromV18},
{19, migrateFromV19},
{20, migrateFromV20},
{21, migrateFromV21},
{22, migrateFromV22},
{23, migrateFromV23},
{24, migrateFromV24},
{25, migrateFromV25},
{26, migrateFromV26},
{27, migrateFromV27},
{28, migrateFromV28},
{29, migrateFromV29},
{30, migrateFromV30},
{31, migrateFromV31},
};
while (1)
{
auto uniqueTransaction {session.createUniqueTransaction()};
Version version;
try
{ {
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; version = VersionInfo::getOrCreate(session)->getVersion();
throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"}; LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION;
} }
catch (std::exception& e)
{
LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what();
throw LmsException {outdatedMsg};
}
if (version == LMS_DATABASE_VERSION)
{
LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!";
return;
}
else if (version > LMS_DATABASE_VERSION)
{
throw LmsException {"Server binary outdated, please upgrade it to handle this database"};
}
if (version < migrationFunctions.begin()->first)
throw LmsException {outdatedMsg};
LMS_LOG(DB, INFO) << "Migrating database from version " << version << "...";
auto itMigrationFunc {migrationFunctions.find(version)};
assert(itMigrationFunc != std::cend(migrationFunctions));
itMigrationFunc->second(session);
VersionInfo::get(session).modify()->setVersion(++version); VersionInfo::get(session).modify()->setVersion(++version);
} }