mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-05 17:43:37 +00:00
Add database migration code
This commit is contained in:
parent
7137a6b81a
commit
f579b90d7c
7 changed files with 398 additions and 149 deletions
|
@ -14,6 +14,7 @@
|
|||
* Fix multiple instances of SG being able to start
|
||||
* Fix garbled text appearing during startup in console
|
||||
* Fix startup code order and general re-factoring (adapted from midgetspy/Sick-Beard)
|
||||
* Add database migration code
|
||||
|
||||
[develop changelog]
|
||||
* Add TVRage network name standardization
|
||||
|
|
|
@ -1069,7 +1069,7 @@ def initialize(consoleLogging=True):
|
|||
|
||||
# initialize the main SB database
|
||||
myDB = db.DBConnection()
|
||||
db.upgradeDatabase(myDB, mainDB.InitialSchema)
|
||||
db.MigrationCode(myDB)
|
||||
|
||||
# initialize the cache database
|
||||
myDB = db.DBConnection('cache.db')
|
||||
|
|
|
@ -27,7 +27,8 @@ from sickbeard import encodingKludge as ek
|
|||
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
|
||||
|
||||
MIN_DB_VERSION = 9 # oldest db version we support migrating from
|
||||
MAX_DB_VERSION = 40
|
||||
MAX_DB_VERSION = 20000
|
||||
|
||||
|
||||
class MainSanityCheck(db.DBSanityCheck):
|
||||
def check(self):
|
||||
|
@ -155,11 +156,12 @@ def backupDatabase(version):
|
|||
# ======================
|
||||
# Add new migrations at the bottom of the list; subclass the previous migration.
|
||||
|
||||
class InitialSchema(db.SchemaUpgrade):
|
||||
def test(self):
|
||||
return self.hasTable("db_version")
|
||||
|
||||
# 0 -> 31
|
||||
class InitialSchema(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
if not self.hasTable("tv_shows") and not self.hasTable("db_version"):
|
||||
queries = [
|
||||
"CREATE TABLE db_version (db_version INTEGER);",
|
||||
|
@ -197,14 +199,14 @@ class InitialSchema(db.SchemaUpgrade):
|
|||
"If you have used other forks of SickGear, your database may be unusable due to their modifications."
|
||||
)
|
||||
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddSizeAndSceneNameFields(InitialSchema):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 10
|
||||
|
||||
# 9 -> 10
|
||||
class AddSizeAndSceneNameFields(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
|
||||
backupDatabase(10)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
if not self.hasColumn("tv_episodes", "file_size"):
|
||||
self.addColumn("tv_episodes", "file_size")
|
||||
|
@ -308,13 +310,14 @@ class AddSizeAndSceneNameFields(InitialSchema):
|
|||
[ep_file_name, cur_result["episode_id"]])
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class RenameSeasonFolders(AddSizeAndSceneNameFields):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 11
|
||||
|
||||
# 10 -> 11
|
||||
class RenameSeasonFolders(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
# rename the column
|
||||
self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows")
|
||||
self.connection.action(
|
||||
|
@ -329,9 +332,11 @@ class RenameSeasonFolders(AddSizeAndSceneNameFields):
|
|||
self.connection.action("DROP TABLE tmp_tv_shows")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class Add1080pAndRawHDQualities(RenameSeasonFolders):
|
||||
# 11 -> 12
|
||||
class Add1080pAndRawHDQualities(db.SchemaUpgrade):
|
||||
"""Add support for 1080p related qualities along with RawHD
|
||||
|
||||
Quick overview of what the upgrade needs to do:
|
||||
|
@ -347,9 +352,6 @@ class Add1080pAndRawHDQualities(RenameSeasonFolders):
|
|||
fullhdwebdl | | 1<<6
|
||||
"""
|
||||
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 12
|
||||
|
||||
def _update_status(self, old_status):
|
||||
(status, quality) = common.Quality.splitCompositeStatus(old_status)
|
||||
return common.Quality.compositeStatus(status, self._update_quality(quality))
|
||||
|
@ -464,16 +466,15 @@ class Add1080pAndRawHDQualities(RenameSeasonFolders):
|
|||
# cleanup and reduce db if any previous data was removed
|
||||
logger.log(u"Performing a vacuum on the database.", logger.DEBUG)
|
||||
self.connection.action("VACUUM")
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddShowidTvdbidIndex(Add1080pAndRawHDQualities):
|
||||
# 12 -> 13
|
||||
class AddShowidTvdbidIndex(db.SchemaUpgrade):
|
||||
""" Adding index on tvdb_id (tv_shows) and showid (tv_episodes) to speed up searches/queries """
|
||||
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 13
|
||||
|
||||
def execute(self):
|
||||
backupDatabase(13)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Check for duplicate shows before adding unique index.")
|
||||
MainSanityCheck(self.connection).fix_duplicate_shows('tvdb_id')
|
||||
|
@ -485,16 +486,14 @@ class AddShowidTvdbidIndex(Add1080pAndRawHDQualities):
|
|||
self.connection.action("CREATE UNIQUE INDEX idx_tvdb_id ON tv_shows (tvdb_id);")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddLastUpdateTVDB(AddShowidTvdbidIndex):
|
||||
# 13 -> 14
|
||||
class AddLastUpdateTVDB(db.SchemaUpgrade):
|
||||
""" Adding column last_update_tvdb to tv_shows for controlling nightly updates """
|
||||
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 14
|
||||
|
||||
def execute(self):
|
||||
backupDatabase(14)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column last_update_tvdb to tvshows")
|
||||
if not self.hasColumn("tv_shows", "last_update_tvdb"):
|
||||
|
@ -503,19 +502,21 @@ class AddLastUpdateTVDB(AddShowidTvdbidIndex):
|
|||
self.incDBVersion()
|
||||
|
||||
|
||||
class AddDBIncreaseTo15(AddLastUpdateTVDB):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 15
|
||||
|
||||
# 14 -> 15
|
||||
class AddDBIncreaseTo15(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddIMDbInfo(AddDBIncreaseTo15):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 16
|
||||
|
||||
# 15 -> 16
|
||||
class AddIMDbInfo(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
|
||||
self.connection.action(
|
||||
"CREATE TABLE imdb_info (tvdb_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC)")
|
||||
|
||||
|
@ -523,71 +524,73 @@ class AddIMDbInfo(AddDBIncreaseTo15):
|
|||
self.addColumn("tv_shows", "imdb_id")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddProperNamingSupport(AddIMDbInfo):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 17
|
||||
|
||||
# 16 -> 17
|
||||
class AddProperNamingSupport(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
self.addColumn("tv_episodes", "is_proper")
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddEmailSubscriptionTable(AddProperNamingSupport):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 18
|
||||
|
||||
# 17 -> 18
|
||||
class AddEmailSubscriptionTable(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
self.addColumn('tv_shows', 'notify_list', 'TEXT', None)
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddProperSearch(AddEmailSubscriptionTable):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 19
|
||||
|
||||
# 18 -> 19
|
||||
class AddProperSearch(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(19)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column last_proper_search to info")
|
||||
if not self.hasColumn("info", "last_proper_search"):
|
||||
self.addColumn("info", "last_proper_search", default=1)
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddDvdOrderOption(AddProperSearch):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 20
|
||||
|
||||
# 19 -> 20
|
||||
class AddDvdOrderOption(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column dvdorder to tvshows")
|
||||
if not self.hasColumn("tv_shows", "dvdorder"):
|
||||
self.addColumn("tv_shows", "dvdorder", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddSubtitlesSupport(AddDvdOrderOption):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 21
|
||||
|
||||
# 20 -> 21
|
||||
class AddSubtitlesSupport(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
if not self.hasColumn("tv_shows", "subtitles"):
|
||||
self.addColumn("tv_shows", "subtitles")
|
||||
self.addColumn("tv_episodes", "subtitles", "TEXT", "")
|
||||
self.addColumn("tv_episodes", "subtitles_searchcount")
|
||||
self.addColumn("tv_episodes", "subtitles_lastsearch", "TIMESTAMP", str(datetime.datetime.min))
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class ConvertTVShowsToIndexerScheme(AddSubtitlesSupport):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 22
|
||||
|
||||
# 21 -> 22
|
||||
class ConvertTVShowsToIndexerScheme(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(22)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Converting TV Shows table to Indexer Scheme...")
|
||||
|
||||
|
@ -608,14 +611,13 @@ class ConvertTVShowsToIndexerScheme(AddSubtitlesSupport):
|
|||
self.connection.action("UPDATE tv_shows SET indexer = 1")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class ConvertTVEpisodesToIndexerScheme(ConvertTVShowsToIndexerScheme):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 23
|
||||
|
||||
# 22 -> 23
|
||||
class ConvertTVEpisodesToIndexerScheme(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(23)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Converting TV Episodes table to Indexer Scheme...")
|
||||
|
||||
|
@ -639,14 +641,13 @@ class ConvertTVEpisodesToIndexerScheme(ConvertTVShowsToIndexerScheme):
|
|||
self.connection.action("UPDATE tv_episodes SET indexer = 1")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class ConvertIMDBInfoToIndexerScheme(ConvertTVEpisodesToIndexerScheme):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 24
|
||||
|
||||
# 23 -> 24
|
||||
class ConvertIMDBInfoToIndexerScheme(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(24)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Converting IMDB Info table to Indexer Scheme...")
|
||||
|
||||
|
@ -662,14 +663,13 @@ class ConvertIMDBInfoToIndexerScheme(ConvertTVEpisodesToIndexerScheme):
|
|||
self.connection.action("DROP TABLE tmp_imdb_info")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class ConvertInfoToIndexerScheme(ConvertIMDBInfoToIndexerScheme):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 25
|
||||
|
||||
# 24 -> 25
|
||||
class ConvertInfoToIndexerScheme(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(25)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Converting Info table to Indexer Scheme...")
|
||||
|
||||
|
@ -685,28 +685,27 @@ class ConvertInfoToIndexerScheme(ConvertIMDBInfoToIndexerScheme):
|
|||
self.connection.action("DROP TABLE tmp_info")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddArchiveFirstMatchOption(ConvertInfoToIndexerScheme):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 26
|
||||
|
||||
# 25 -> 26
|
||||
class AddArchiveFirstMatchOption(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(26)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column archive_firstmatch to tvshows")
|
||||
if not self.hasColumn("tv_shows", "archive_firstmatch"):
|
||||
self.addColumn("tv_shows", "archive_firstmatch", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddSceneNumbering(AddArchiveFirstMatchOption):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 27
|
||||
# 26 -> 27
|
||||
class AddSceneNumbering(db.SchemaUpgrade):
|
||||
|
||||
def execute(self):
|
||||
backupDatabase(27)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
if self.hasTable("scene_numbering"):
|
||||
self.connection.action("DROP TABLE scene_numbering")
|
||||
|
@ -715,14 +714,13 @@ class AddSceneNumbering(AddArchiveFirstMatchOption):
|
|||
"CREATE TABLE scene_numbering (indexer TEXT, indexer_id INTEGER, season INTEGER, episode INTEGER, scene_season INTEGER, scene_episode INTEGER, PRIMARY KEY (indexer_id, season, episode, scene_season, scene_episode))")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class ConvertIndexerToInteger(AddSceneNumbering):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 28
|
||||
|
||||
# 27 -> 28
|
||||
class ConvertIndexerToInteger(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(28)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
cl = []
|
||||
logger.log(u"Converting Indexer to Integer ...", logger.MESSAGE)
|
||||
|
@ -736,16 +734,15 @@ class ConvertIndexerToInteger(AddSceneNumbering):
|
|||
self.connection.mass_action(cl)
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddRequireAndIgnoreWords(ConvertIndexerToInteger):
|
||||
# 28 -> 29
|
||||
class AddRequireAndIgnoreWords(db.SchemaUpgrade):
|
||||
""" Adding column rls_require_words and rls_ignore_words to tv_shows """
|
||||
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 29
|
||||
|
||||
def execute(self):
|
||||
backupDatabase(29)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column rls_require_words to tvshows")
|
||||
if not self.hasColumn("tv_shows", "rls_require_words"):
|
||||
|
@ -756,14 +753,13 @@ class AddRequireAndIgnoreWords(ConvertIndexerToInteger):
|
|||
self.addColumn("tv_shows", "rls_ignore_words", "TEXT", "")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddSportsOption(AddRequireAndIgnoreWords):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 30
|
||||
|
||||
# 29 -> 30
|
||||
class AddSportsOption(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(30)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column sports to tvshows")
|
||||
if not self.hasColumn("tv_shows", "sports"):
|
||||
|
@ -782,65 +778,63 @@ class AddSportsOption(AddRequireAndIgnoreWords):
|
|||
self.connection.mass_action(cl)
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
class AddSceneNumberingToTvEpisodes(AddSportsOption):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 31
|
||||
|
||||
# 30 -> 31
|
||||
class AddSceneNumberingToTvEpisodes(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(31)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column scene_season and scene_episode to tvepisodes")
|
||||
self.addColumn("tv_episodes", "scene_season", "NUMERIC", "NULL")
|
||||
self.addColumn("tv_episodes", "scene_episode", "NUMERIC", "NULL")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.incDBVersion()
|
||||
|
||||
class AddAnimeTVShow(AddSceneNumberingToTvEpisodes):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 32
|
||||
|
||||
# 31 -> 32
|
||||
class AddAnimeTVShow(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(32)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column anime to tv_episodes")
|
||||
self.addColumn("tv_shows", "anime", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddAbsoluteNumbering(AddAnimeTVShow):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 33
|
||||
|
||||
# 32 -> 33
|
||||
class AddAbsoluteNumbering(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(33)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column absolute_number to tv_episodes")
|
||||
self.addColumn("tv_episodes", "absolute_number", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddSceneAbsoluteNumbering(AddAbsoluteNumbering):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 34
|
||||
|
||||
# 33 -> 34
|
||||
class AddSceneAbsoluteNumbering(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(34)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column absolute_number and scene_absolute_number to scene_numbering")
|
||||
self.addColumn("scene_numbering", "absolute_number", "NUMERIC", "0")
|
||||
self.addColumn("scene_numbering", "scene_absolute_number", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddAnimeBlacklistWhitelist(AddSceneAbsoluteNumbering):
|
||||
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 35
|
||||
|
||||
# 34 -> 35
|
||||
class AddAnimeBlacklistWhitelist(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(35)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
cl = []
|
||||
cl.append(["CREATE TABLE blacklist (show_id INTEGER, range TEXT, keyword TEXT)"])
|
||||
|
@ -848,50 +842,50 @@ class AddAnimeBlacklistWhitelist(AddSceneAbsoluteNumbering):
|
|||
self.connection.mass_action(cl)
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddSceneAbsoluteNumbering(AddAnimeBlacklistWhitelist):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 36
|
||||
|
||||
# 35 -> 36
|
||||
class AddSceneAbsoluteNumbering2(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(36)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column scene_absolute_number to tv_episodes")
|
||||
self.addColumn("tv_episodes", "scene_absolute_number", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddXemRefresh(AddSceneAbsoluteNumbering):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 37
|
||||
|
||||
# 36 -> 37
|
||||
class AddXemRefresh(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(37)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Creating table xem_refresh")
|
||||
self.connection.action(
|
||||
"CREATE TABLE xem_refresh (indexer TEXT, indexer_id INTEGER PRIMARY KEY, last_refreshed INTEGER)")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddSceneToTvShows(AddXemRefresh):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 38
|
||||
|
||||
# 37 -> 38
|
||||
class AddSceneToTvShows(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(38)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column scene to tv_shows")
|
||||
self.addColumn("tv_shows", "scene", "NUMERIC", "0")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddIndexerMapping(AddSceneToTvShows):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 39
|
||||
|
||||
# 38 -> 39
|
||||
class AddIndexerMapping(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(39)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
if self.hasTable("indexer_mapping"):
|
||||
self.connection.action("DROP TABLE indexer_mapping")
|
||||
|
@ -901,13 +895,13 @@ class AddIndexerMapping(AddSceneToTvShows):
|
|||
"CREATE TABLE indexer_mapping (indexer_id INTEGER, indexer NUMERIC, mindexer_id INTEGER, mindexer NUMERIC, PRIMARY KEY (indexer_id, indexer))")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
class AddVersionToTvEpisodes(AddIndexerMapping):
|
||||
def test(self):
|
||||
return self.checkDBVersion() >= 40
|
||||
|
||||
# 39 -> 40
|
||||
class AddVersionToTvEpisodes(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(40)
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u"Adding column version to tv_episodes and history")
|
||||
self.addColumn("tv_episodes", "version", "NUMERIC", "-1")
|
||||
|
@ -915,3 +909,46 @@ class AddVersionToTvEpisodes(AddIndexerMapping):
|
|||
self.addColumn("history", "version", "NUMERIC", "-1")
|
||||
|
||||
self.incDBVersion()
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
# 40 -> 10000
|
||||
class BumpDatabaseVersion(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
logger.log(u'Bumping database version')
|
||||
|
||||
self.setDBVersion(10000)
|
||||
return self.checkDBVersion()
|
||||
|
||||
# 41 -> 10001
|
||||
class Migrate41(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u'Bumping database version')
|
||||
|
||||
self.setDBVersion(10001)
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
# 10000 -> 20000
|
||||
class SickGearDatabaseVersion(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log('Bumping database version to new SickGear standards')
|
||||
|
||||
self.setDBVersion(20000)
|
||||
return self.checkDBVersion()
|
||||
|
||||
# 10001 -> 10000
|
||||
class RemoveDefaultEpStatusFromTvShows(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backupDatabase(self.checkDBVersion())
|
||||
|
||||
logger.log(u'Dropping column default_ep_status from tv_shows')
|
||||
self.dropColumn('tv_shows', 'default_ep_status')
|
||||
|
||||
self.setDBVersion(10000)
|
||||
return self.checkDBVersion()
|
142
sickbeard/db.py
142
sickbeard/db.py
|
@ -356,6 +356,69 @@ class SchemaUpgrade(object):
|
|||
self.connection.action("ALTER TABLE %s ADD %s %s" % (table, column, type))
|
||||
self.connection.action("UPDATE %s SET %s = ?" % (table, column), (default,))
|
||||
|
||||
def dropColumn(self, table, column):
|
||||
# get old table columns and store the ones we want to keep
|
||||
result = self.connection.select('pragma table_info(%s)' % table)
|
||||
keptColumns = [c for c in result if c['name'] != column]
|
||||
|
||||
keptColumnsNames = []
|
||||
final = []
|
||||
pk = []
|
||||
|
||||
# copy the old table schema, column by column
|
||||
for column in keptColumns:
|
||||
|
||||
keptColumnsNames.append(column['name'])
|
||||
|
||||
cl = []
|
||||
cl.append(column['name'])
|
||||
cl.append(column['type'])
|
||||
|
||||
'''
|
||||
To be implemented if ever required
|
||||
if column['dflt_value']:
|
||||
cl.append(str(column['dflt_value']))
|
||||
|
||||
if column['notnull']:
|
||||
cl.append(column['notnull'])
|
||||
'''
|
||||
|
||||
if int(column['pk']) != 0:
|
||||
pk.append(column['name'])
|
||||
|
||||
b = ' '.join(cl)
|
||||
final.append(b)
|
||||
|
||||
# join all the table column creation fields
|
||||
final = ', '.join(final)
|
||||
keptColumnsNames = ', '.join(keptColumnsNames)
|
||||
|
||||
# generate sql for the new table creation
|
||||
if len(pk) == 0:
|
||||
sql = 'CREATE TABLE %s_new (%s)' % (table, final)
|
||||
else:
|
||||
pk = ', '.join(pk)
|
||||
sql = 'CREATE TABLE %s_new (%s, PRIMARY KEY(%s))' % (table, final, pk)
|
||||
|
||||
# create new temporary table and copy the old table data across, barring the removed column
|
||||
self.connection.action(sql)
|
||||
self.connection.action('INSERT INTO %s_new SELECT %s FROM %s' % (table, keptColumnsNames, table))
|
||||
|
||||
# copy the old indexes from the old table
|
||||
result = self.connection.select('SELECT sql FROM sqlite_master WHERE tbl_name=? and type="index"', [table])
|
||||
|
||||
# remove the old table and rename the new table to take it's place
|
||||
self.connection.action('DROP TABLE %s' % table)
|
||||
self.connection.action('ALTER TABLE %s_new RENAME TO %s' % (table, table))
|
||||
|
||||
# write any indexes to the new table
|
||||
if len(result) > 0:
|
||||
for index in result:
|
||||
self.connection.action(index['sql'])
|
||||
|
||||
# vacuum the db as we will have a lot of space to reclaim after dropping tables
|
||||
self.connection.action("VACUUM")
|
||||
|
||||
def checkDBVersion(self):
|
||||
return self.connection.checkDBVersion()
|
||||
|
||||
|
@ -363,3 +426,82 @@ class SchemaUpgrade(object):
|
|||
new_version = self.checkDBVersion() + 1
|
||||
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
|
||||
return new_version
|
||||
|
||||
def setDBVersion(self, new_version):
|
||||
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
|
||||
return new_version
|
||||
|
||||
|
||||
def MigrationCode(myDB):
|
||||
|
||||
schema = {
|
||||
0: sickbeard.mainDB.InitialSchema, # 0->20000
|
||||
9: sickbeard.mainDB.AddSizeAndSceneNameFields,
|
||||
10: sickbeard.mainDB.RenameSeasonFolders,
|
||||
11: sickbeard.mainDB.Add1080pAndRawHDQualities,
|
||||
12: sickbeard.mainDB.AddShowidTvdbidIndex,
|
||||
13: sickbeard.mainDB.AddLastUpdateTVDB,
|
||||
14: sickbeard.mainDB.AddDBIncreaseTo15,
|
||||
15: sickbeard.mainDB.AddIMDbInfo,
|
||||
16: sickbeard.mainDB.AddProperNamingSupport,
|
||||
17: sickbeard.mainDB.AddEmailSubscriptionTable,
|
||||
18: sickbeard.mainDB.AddProperSearch,
|
||||
19: sickbeard.mainDB.AddDvdOrderOption,
|
||||
20: sickbeard.mainDB.AddSubtitlesSupport,
|
||||
21: sickbeard.mainDB.ConvertTVShowsToIndexerScheme,
|
||||
22: sickbeard.mainDB.ConvertTVEpisodesToIndexerScheme,
|
||||
23: sickbeard.mainDB.ConvertIMDBInfoToIndexerScheme,
|
||||
24: sickbeard.mainDB.ConvertInfoToIndexerScheme,
|
||||
25: sickbeard.mainDB.AddArchiveFirstMatchOption,
|
||||
26: sickbeard.mainDB.AddSceneNumbering,
|
||||
27: sickbeard.mainDB.ConvertIndexerToInteger,
|
||||
28: sickbeard.mainDB.AddRequireAndIgnoreWords,
|
||||
29: sickbeard.mainDB.AddSportsOption,
|
||||
30: sickbeard.mainDB.AddSceneNumberingToTvEpisodes,
|
||||
31: sickbeard.mainDB.AddAnimeTVShow,
|
||||
32: sickbeard.mainDB.AddAbsoluteNumbering,
|
||||
33: sickbeard.mainDB.AddSceneAbsoluteNumbering,
|
||||
34: sickbeard.mainDB.AddAnimeBlacklistWhitelist,
|
||||
35: sickbeard.mainDB.AddSceneAbsoluteNumbering2,
|
||||
36: sickbeard.mainDB.AddXemRefresh,
|
||||
37: sickbeard.mainDB.AddSceneToTvShows,
|
||||
38: sickbeard.mainDB.AddIndexerMapping,
|
||||
39: sickbeard.mainDB.AddVersionToTvEpisodes,
|
||||
|
||||
40: sickbeard.mainDB.BumpDatabaseVersion,
|
||||
41: sickbeard.mainDB.Migrate41,
|
||||
|
||||
10000: sickbeard.mainDB.SickGearDatabaseVersion,
|
||||
10001: sickbeard.mainDB.RemoveDefaultEpStatusFromTvShows
|
||||
|
||||
#20000: sickbeard.mainDB.AddCoolSickGearFeature1,
|
||||
#20001: sickbeard.mainDB.AddCoolSickGearFeature2,
|
||||
#20002: sickbeard.mainDB.AddCoolSickGearFeature3,
|
||||
}
|
||||
|
||||
db_version = myDB.checkDBVersion()
|
||||
logger.log(u'Detected database version: v' + str(db_version), logger.DEBUG)
|
||||
|
||||
if not (db_version in schema):
|
||||
if db_version == sickbeard.mainDB.MAX_DB_VERSION:
|
||||
logger.log(u'Database schema is up-to-date, no upgrade required')
|
||||
elif db_version < 10000:
|
||||
logger.log_error_and_exit(u'SickGear does not currently support upgrading from this database version')
|
||||
else:
|
||||
logger.log_error_and_exit(u'Invalid database version')
|
||||
|
||||
else:
|
||||
|
||||
while db_version < sickbeard.mainDB.MAX_DB_VERSION:
|
||||
try:
|
||||
update = schema[db_version](myDB)
|
||||
db_version = update.execute()
|
||||
except Exception, e:
|
||||
myDB.close()
|
||||
logger.log(u'Failed to update database with error: ' + ex(e) + ' attempting recovery...', logger.ERROR)
|
||||
|
||||
if restoreDatabase(db_version):
|
||||
# initialize the main SB database
|
||||
logger.log_error_and_exit(u'Successfully restored database version:' + str(db_version))
|
||||
else:
|
||||
logger.log_error_and_exit(u'Failed to restore database version:' + str(db_version))
|
|
@ -29,7 +29,7 @@ class DBBasicTests(test.SickbeardTestDBCase):
|
|||
|
||||
def test_select(self):
|
||||
self.db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
|
||||
|
||||
self.db.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "=================="
|
||||
|
|
68
tests/migration_tests.py
Normal file
68
tests/migration_tests.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import sys
|
||||
import os.path
|
||||
import glob
|
||||
import unittest
|
||||
import test_lib as test
|
||||
import sickbeard
|
||||
from time import sleep
|
||||
from sickbeard import db
|
||||
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../lib'))
|
||||
|
||||
sickbeard.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
|
||||
class MigrationBasicTests(test.SickbeardTestDBCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_migrations(self):
|
||||
schema = {
|
||||
0: sickbeard.mainDB.InitialSchema,
|
||||
31: sickbeard.mainDB.AddAnimeTVShow,
|
||||
32: sickbeard.mainDB.AddAbsoluteNumbering,
|
||||
33: sickbeard.mainDB.AddSceneAbsoluteNumbering,
|
||||
34: sickbeard.mainDB.AddAnimeBlacklistWhitelist,
|
||||
35: sickbeard.mainDB.AddSceneAbsoluteNumbering2,
|
||||
36: sickbeard.mainDB.AddXemRefresh,
|
||||
37: sickbeard.mainDB.AddSceneToTvShows,
|
||||
38: sickbeard.mainDB.AddIndexerMapping,
|
||||
39: sickbeard.mainDB.AddVersionToTvEpisodes,
|
||||
41: AddDefaultEpStatusToTvShows,
|
||||
}
|
||||
|
||||
count = 1
|
||||
while count < len(schema.keys()):
|
||||
myDB = db.DBConnection()
|
||||
|
||||
for version in sorted(schema.keys())[:count]:
|
||||
update = schema[version](myDB)
|
||||
update.execute()
|
||||
sleep(0.1)
|
||||
|
||||
db.MigrationCode(myDB)
|
||||
myDB.close()
|
||||
for filename in glob.glob(os.path.join(test.TESTDIR, test.TESTDBNAME) +'*'):
|
||||
os.remove(filename)
|
||||
|
||||
sleep(0.1)
|
||||
count += 1
|
||||
|
||||
|
||||
class AddDefaultEpStatusToTvShows(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
self.addColumn("tv_shows", "default_ep_status", "TEXT", "")
|
||||
self.setDBVersion(41)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "=================="
|
||||
print "STARTING - MIGRATION TESTS"
|
||||
print "=================="
|
||||
print "######################################################################"
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(MigrationBasicTests)
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
|
@ -20,11 +20,11 @@
|
|||
from __future__ import with_statement
|
||||
|
||||
import unittest
|
||||
|
||||
import sqlite3
|
||||
|
||||
import glob
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../lib'))
|
||||
|
||||
|
@ -173,7 +173,7 @@ def setUp_test_db():
|
|||
"""upgrades the db to the latest version
|
||||
"""
|
||||
# upgrading the db
|
||||
db.upgradeDatabase(db.DBConnection(), mainDB.InitialSchema)
|
||||
db.MigrationCode(db.DBConnection())
|
||||
|
||||
# fix up any db problems
|
||||
db.sanityCheckDatabase(db.DBConnection(), mainDB.MainSanityCheck)
|
||||
|
@ -191,8 +191,9 @@ def tearDown_test_db():
|
|||
"""
|
||||
# uncomment next line so leave the db intact between test and at the end
|
||||
#return False
|
||||
if os.path.exists(os.path.join(TESTDIR, TESTDBNAME)):
|
||||
os.remove(os.path.join(TESTDIR, TESTDBNAME))
|
||||
|
||||
for filename in glob.glob(os.path.join(TESTDIR, TESTDBNAME) + '*'):
|
||||
os.remove(filename)
|
||||
if os.path.exists(os.path.join(TESTDIR, TESTCACHEDBNAME)):
|
||||
os.remove(os.path.join(TESTDIR, TESTCACHEDBNAME))
|
||||
if os.path.exists(os.path.join(TESTDIR, TESTFAILEDDBNAME)):
|
||||
|
|
Loading…
Reference in a new issue