Copy database_upgrader Migrator from Etiquette.
This commit is contained in:
		
							parent
							
								
									1e59d7be06
								
							
						
					
					
						commit
						885bc2b711
					
				
					 1 changed files with 167 additions and 97 deletions
				
			
		|  | @ -6,36 +6,100 @@ import sys | ||||||
| import bot | import bot | ||||||
| import ycdl | import ycdl | ||||||
| 
 | 
 | ||||||
|  | class Migrator: | ||||||
|  |     ''' | ||||||
|  |     Many of the upgraders involve adding columns. ALTER TABLE ADD COLUMN only | ||||||
|  |     allows adding at the end, which I usually don't prefer. In order to add a | ||||||
|  |     column in the middle, you must rename the table, create a new one, transfer | ||||||
|  |     the data, and drop the old one. But, foreign keys and indices will still | ||||||
|  |     point to the old table, which causes broken foreign keys and dropped | ||||||
|  |     indices. So, the only way to prevent all that is to regenerate all affected | ||||||
|  |     tables and indices. Rather than parsing relationships to determine the | ||||||
|  |     affected tables, this implementation just regenerates everything. | ||||||
|  | 
 | ||||||
|  |     It's kind of horrible but it allows me to have the columns in the order I | ||||||
|  |     want instead of just always appending. Besides, modifying collations cannot | ||||||
|  |     be done in-place either. | ||||||
|  | 
 | ||||||
|  |     If you want to truly remove a table or index and not have it get | ||||||
|  |     regenerated, just do that before instantiating the Migrator. | ||||||
|  |     ''' | ||||||
|  |     def __init__(self, ycdldb): | ||||||
|  |         self.ycdldb = ycdldb | ||||||
|  | 
 | ||||||
|  |         query = 'SELECT name, sql FROM sqlite_master WHERE type == "table"' | ||||||
|  |         self.tables = { | ||||||
|  |             name: {'create': sql, 'transfer': f'INSERT INTO {name} SELECT * FROM {name}_old'} | ||||||
|  |             for (name, sql) in self.ycdldb.sql_select(query) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         # The user may be adding entirely new tables derived from the data of | ||||||
|  |         # old ones. We'll need to skip new tables for the rename and drop_old | ||||||
|  |         # steps. So we track which tables already existed at the beginning. | ||||||
|  |         self.existing_tables = set(self.tables) | ||||||
|  | 
 | ||||||
|  |         query = 'SELECT name, sql FROM sqlite_master WHERE type == "index" AND name NOT LIKE "sqlite_%"' | ||||||
|  |         self.indices = list(self.ycdldb.sql_select(query)) | ||||||
|  | 
 | ||||||
|  |     def go(self): | ||||||
|  |         # This loop is split in many parts, because otherwise if table A | ||||||
|  |         # references table B and table A is completely reconstructed, it will | ||||||
|  |         # be pointing to the version of B which has not been reconstructed yet, | ||||||
|  |         # which is about to get renamed to B_old and then A's reference will be | ||||||
|  |         # broken. | ||||||
|  |         self.ycdldb.sql_execute('PRAGMA foreign_keys = OFF') | ||||||
|  |         self.ycdldb.sql_execute('BEGIN') | ||||||
|  |         for (name, table) in self.tables.items(): | ||||||
|  |             if name not in self.existing_tables: | ||||||
|  |                 continue | ||||||
|  |             self.ycdldb.sql_execute(f'ALTER TABLE {name} RENAME TO {name}_old') | ||||||
|  | 
 | ||||||
|  |         for (name, table) in self.tables.items(): | ||||||
|  |             self.ycdldb.sql_execute(table['create']) | ||||||
|  | 
 | ||||||
|  |         for (name, table) in self.tables.items(): | ||||||
|  |             self.ycdldb.sql_execute(table['transfer']) | ||||||
|  | 
 | ||||||
|  |         for (name, query) in self.tables.items(): | ||||||
|  |             if name not in self.existing_tables: | ||||||
|  |                 continue | ||||||
|  |             self.ycdldb.sql_execute(f'DROP TABLE {name}_old') | ||||||
|  | 
 | ||||||
|  |         for (name, query) in self.indices: | ||||||
|  |             self.ycdldb.sql_execute(query) | ||||||
|  | 
 | ||||||
| def upgrade_1_to_2(ycdldb): | def upgrade_1_to_2(ycdldb): | ||||||
|     ''' |     ''' | ||||||
|     In this version, the `duration` column was added to the videos table. |     In this version, the `duration` column was added to the videos table. | ||||||
|     ''' |     ''' | ||||||
|     ycdldb.sql.execute('ALTER TABLE videos RENAME TO videos_old') |     m = Migrator(ycdldb) | ||||||
|     ycdldb.sql.execute(''' | 
 | ||||||
|         CREATE TABLE videos( |     m.tables['videos']['create'] = ''' | ||||||
|             id TEXT, |     CREATE TABLE videos( | ||||||
|             published INT, |         id TEXT, | ||||||
|             author_id TEXT, |         published INT, | ||||||
|             title TEXT, |         author_id TEXT, | ||||||
|             description TEXT, |         title TEXT, | ||||||
|             duration INT, |         description TEXT, | ||||||
|             thumbnail TEXT, |         duration INT, | ||||||
|             download TEXT |         thumbnail TEXT, | ||||||
|         ) |         download TEXT | ||||||
|     ''') |     ); | ||||||
|     ycdldb.sql.execute(''' |     ''' | ||||||
|         INSERT INTO videos SELECT |     m.tables['videos']['transfer'] = ''' | ||||||
|             id, |     INSERT INTO videos SELECT | ||||||
|             published, |         id, | ||||||
|             author_id, |         published, | ||||||
|             title, |         author_id, | ||||||
|             description, |         title, | ||||||
|             NULL, |         description, | ||||||
|             thumbnail, |         NULL, | ||||||
|             download |         thumbnail, | ||||||
|         FROM videos_old |         download | ||||||
|     ''') |     FROM videos_old; | ||||||
|     ycdldb.sql.execute('DROP TABLE videos_old') |     ''' | ||||||
|  | 
 | ||||||
|  |     m.go() | ||||||
| 
 | 
 | ||||||
| def upgrade_2_to_3(ycdldb): | def upgrade_2_to_3(ycdldb): | ||||||
|     ''' |     ''' | ||||||
|  | @ -48,59 +112,63 @@ def upgrade_3_to_4(ycdldb): | ||||||
|     ''' |     ''' | ||||||
|     In this version, the `views` column was added to the videos table. |     In this version, the `views` column was added to the videos table. | ||||||
|     ''' |     ''' | ||||||
|     ycdldb.sql.execute('ALTER TABLE videos RENAME TO videos_old') |     m = Migrator(ycdldb) | ||||||
|     ycdldb.sql.execute(''' | 
 | ||||||
|         CREATE TABLE videos( |     m.tables['videos']['create'] = ''' | ||||||
|             id TEXT, |     CREATE TABLE videos( | ||||||
|             published INT, |         id TEXT, | ||||||
|             author_id TEXT, |         published INT, | ||||||
|             title TEXT, |         author_id TEXT, | ||||||
|             description TEXT, |         title TEXT, | ||||||
|             duration INT, |         description TEXT, | ||||||
|             views INT, |         duration INT, | ||||||
|             thumbnail TEXT, |         views INT, | ||||||
|             download TEXT |         thumbnail TEXT, | ||||||
|         ) |         download TEXT | ||||||
|     ''') |     ); | ||||||
|     ycdldb.sql.execute(''' |     ''' | ||||||
|         INSERT INTO videos SELECT |     m.tables['videos']['transfer'] = ''' | ||||||
|             id, |     INSERT INTO videos SELECT | ||||||
|             published, |         id, | ||||||
|             author_id, |         published, | ||||||
|             title, |         author_id, | ||||||
|             description, |         title, | ||||||
|             duration, |         description, | ||||||
|             NULL, |         duration, | ||||||
|             thumbnail, |         NULL, | ||||||
|             download |         thumbnail, | ||||||
|         FROM videos_old |         download | ||||||
|     ''') |     FROM videos_old; | ||||||
|     ycdldb.sql.execute('DROP TABLE videos_old') |     ''' | ||||||
|  | 
 | ||||||
|  |     m.go() | ||||||
| 
 | 
 | ||||||
| def upgrade_4_to_5(ycdldb): | def upgrade_4_to_5(ycdldb): | ||||||
|     ''' |     ''' | ||||||
|     In this version, the `uploads_playlist` column was added to the channels table. |     In this version, the `uploads_playlist` column was added to the channels table. | ||||||
|     ''' |     ''' | ||||||
|     ycdldb.sql.execute('ALTER TABLE channels RENAME TO channels_old') |     m = Migrator(ycdldb) | ||||||
|     ycdldb.sql.execute(''' | 
 | ||||||
|         CREATE TABLE channels( |     m.tables['channels']['create'] = ''' | ||||||
|             id TEXT, |     CREATE TABLE channels( | ||||||
|             name TEXT, |         id TEXT, | ||||||
|             uploads_playlist TEXT, |         name TEXT, | ||||||
|             directory TEXT COLLATE NOCASE, |         uploads_playlist TEXT, | ||||||
|             automark TEXT |         directory TEXT COLLATE NOCASE, | ||||||
|         ) |         automark TEXT | ||||||
|     ''') |     ); | ||||||
|     ycdldb.sql.execute(''' |     ''' | ||||||
|         INSERT INTO channels SELECT |     m.tables['channels']['transfer'] = ''' | ||||||
|             id, |     INSERT INTO channels SELECT | ||||||
|             name, |         id, | ||||||
|             NULL, |         name, | ||||||
|             directory, |         NULL, | ||||||
|             automark |         directory, | ||||||
|         FROM channels_old |         automark | ||||||
|     ''') |     FROM channels_old; | ||||||
|     ycdldb.sql.execute('DROP TABLE channels_old') |     ''' | ||||||
|  | 
 | ||||||
|  |     m.go() | ||||||
| 
 | 
 | ||||||
|     rows = ycdldb.sql.execute('SELECT id FROM channels').fetchall() |     rows = ycdldb.sql.execute('SELECT id FROM channels').fetchall() | ||||||
|     channels = [row[0] for row in rows] |     channels = [row[0] for row in rows] | ||||||
|  | @ -121,28 +189,30 @@ def upgrade_5_to_6(ycdldb): | ||||||
|     to `download_directory` to be in line with the default config's name for |     to `download_directory` to be in line with the default config's name for | ||||||
|     the same value, and the `queuefile_extension` column was added. |     the same value, and the `queuefile_extension` column was added. | ||||||
|     ''' |     ''' | ||||||
|     ycdldb.sql.execute('ALTER TABLE channels RENAME TO channels_old') |     m = Migrator(ycdldb) | ||||||
|     ycdldb.sql.execute(''' | 
 | ||||||
|         CREATE TABLE channels( |     m.tables['channels']['create'] = ''' | ||||||
|             id TEXT, |     CREATE TABLE channels( | ||||||
|             name TEXT, |         id TEXT, | ||||||
|             uploads_playlist TEXT, |         name TEXT, | ||||||
|             download_directory TEXT COLLATE NOCASE, |         uploads_playlist TEXT, | ||||||
|             queuefile_extension TEXT COLLATE NOCASE, |         download_directory TEXT COLLATE NOCASE, | ||||||
|             automark TEXT |         queuefile_extension TEXT COLLATE NOCASE, | ||||||
|         ) |         automark TEXT | ||||||
|     ''') |     ); | ||||||
|     ycdldb.sql.execute(''' |     ''' | ||||||
|         INSERT INTO channels SELECT |     m.tables['channels']['transfer'] = ''' | ||||||
|             id, |     INSERT INTO channels SELECT | ||||||
|             name, |         id, | ||||||
|             uploads_playlist, |         name, | ||||||
|             directory, |         uploads_playlist, | ||||||
|             NULL, |         directory, | ||||||
|             automark |         NULL, | ||||||
|         FROM channels_old |         automark | ||||||
|     ''') |     FROM channels_old; | ||||||
|     ycdldb.sql.execute('DROP TABLE channels_old') |     ''' | ||||||
|  | 
 | ||||||
|  |     m.go() | ||||||
| 
 | 
 | ||||||
| def upgrade_6_to_7(ycdldb): | def upgrade_6_to_7(ycdldb): | ||||||
|     ''' |     ''' | ||||||
|  | @ -203,7 +273,7 @@ def upgrade_all(data_directory): | ||||||
|         upgrade_function = eval(upgrade_function) |         upgrade_function = eval(upgrade_function) | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             ycdldb.sql.execute('BEGIN') |             ycdldb.sql.execute('PRAGMA foreign_keys = ON') | ||||||
|             upgrade_function(ycdldb) |             upgrade_function(ycdldb) | ||||||
|         except Exception as exc: |         except Exception as exc: | ||||||
|             ycdldb.rollback() |             ycdldb.rollback() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue