Added starred entries migration
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user