-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
/
Copy pathMigrations.java
307 lines (267 loc) · 16.6 KB
/
Migrations.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package org.schabi.newpipe.database;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import org.schabi.newpipe.MainActivity;
public final class Migrations {
/////////////////////////////////////////////////////////////////////////////
// Test new migrations manually by importing a database from daily usage //
// and checking if the migration works (Use the Database Inspector //
// https://developer.android.com/studio/inspect/database). //
// If you add a migration point it out in the pull request, so that //
// others remember to test it themselves. //
/////////////////////////////////////////////////////////////////////////////
public static final int DB_VER_1 = 1;
public static final int DB_VER_2 = 2;
public static final int DB_VER_3 = 3;
public static final int DB_VER_4 = 4;
public static final int DB_VER_5 = 5;
public static final int DB_VER_6 = 6;
public static final int DB_VER_7 = 7;
public static final int DB_VER_8 = 8;
public static final int DB_VER_9 = 9;
private static final String TAG = Migrations.class.getName();
public static final boolean DEBUG = MainActivity.DEBUG;
public static final Migration MIGRATION_1_2 = new Migration(DB_VER_1, DB_VER_2) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
if (DEBUG) {
Log.d(TAG, "Start migrating database");
}
/*
* Unfortunately these queries must be hardcoded due to the possibility of
* schema and names changing at a later date, thus invalidating the older migration
* scripts if they are not hardcoded.
* */
// Not much we can do about this, since room doesn't create tables before migration.
// It's either this or blasting the entire database anew.
database.execSQL("CREATE INDEX `index_search_history_search` "
+ "ON `search_history` (`search`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `streams` "
+ "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "`service_id` INTEGER NOT NULL, `url` TEXT, `title` TEXT, "
+ "`stream_type` TEXT, `duration` INTEGER, `uploader` TEXT, "
+ "`thumbnail_url` TEXT)");
database.execSQL("CREATE UNIQUE INDEX `index_streams_service_id_url` "
+ "ON `streams` (`service_id`, `url`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `stream_history` "
+ "(`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, "
+ "`repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), "
+ "FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) "
+ "ON UPDATE CASCADE ON DELETE CASCADE )");
database.execSQL("CREATE INDEX `index_stream_history_stream_id` "
+ "ON `stream_history` (`stream_id`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `stream_state` "
+ "(`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, "
+ "PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) "
+ "REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )");
database.execSQL("CREATE TABLE IF NOT EXISTS `playlists` "
+ "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "`name` TEXT, `thumbnail_url` TEXT)");
database.execSQL("CREATE INDEX `index_playlists_name` ON `playlists` (`name`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `playlist_stream_join` "
+ "(`playlist_id` INTEGER NOT NULL, `stream_id` INTEGER NOT NULL, "
+ "`join_index` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `join_index`), "
+ "FOREIGN KEY(`playlist_id`) REFERENCES `playlists`(`uid`) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE UNIQUE INDEX "
+ "`index_playlist_stream_join_playlist_id_join_index` "
+ "ON `playlist_stream_join` (`playlist_id`, `join_index`)");
database.execSQL("CREATE INDEX `index_playlist_stream_join_stream_id` "
+ "ON `playlist_stream_join` (`stream_id`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `remote_playlists` "
+ "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "`service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, "
+ "`thumbnail_url` TEXT, `uploader` TEXT, `stream_count` INTEGER)");
database.execSQL("CREATE INDEX `index_remote_playlists_name` "
+ "ON `remote_playlists` (`name`)");
database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` "
+ "ON `remote_playlists` (`service_id`, `url`)");
// Populate streams table with existing entries in watch history
// Latest data first, thus ignoring older entries with the same indices
database.execSQL("INSERT OR IGNORE INTO streams (service_id, url, title, "
+ "stream_type, duration, uploader, thumbnail_url) "
+ "SELECT service_id, url, title, 'VIDEO_STREAM', duration, "
+ "uploader, thumbnail_url "
+ "FROM watch_history "
+ "ORDER BY creation_date DESC");
// Once the streams have PKs, join them with the normalized history table
// and populate it with the remaining data from watch history
database.execSQL("INSERT INTO stream_history (stream_id, access_date, repeat_count)"
+ "SELECT uid, creation_date, 1 "
+ "FROM watch_history INNER JOIN streams "
+ "ON watch_history.service_id == streams.service_id "
+ "AND watch_history.url == streams.url "
+ "ORDER BY creation_date DESC");
database.execSQL("DROP TABLE IF EXISTS watch_history");
if (DEBUG) {
Log.d(TAG, "Stop migrating database");
}
}
};
public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
// Add NOT NULLs and new fields
database.execSQL("CREATE TABLE IF NOT EXISTS streams_new "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "service_id INTEGER NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL, "
+ "stream_type TEXT NOT NULL, duration INTEGER NOT NULL, "
+ "uploader TEXT NOT NULL, thumbnail_url TEXT, view_count INTEGER, "
+ "textual_upload_date TEXT, upload_date INTEGER, "
+ "is_upload_date_approximation INTEGER)");
database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, "
+ "duration, uploader, thumbnail_url, view_count, textual_upload_date, "
+ "upload_date, is_upload_date_approximation) "
+ "SELECT uid, service_id, url, ifnull(title, ''), "
+ "ifnull(stream_type, 'VIDEO_STREAM'), ifnull(duration, 0), "
+ "ifnull(uploader, ''), ifnull(thumbnail_url, ''), NULL, NULL, NULL, NULL "
+ "FROM streams WHERE url IS NOT NULL");
database.execSQL("DROP TABLE streams");
database.execSQL("ALTER TABLE streams_new RENAME TO streams");
database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url "
+ "ON streams (service_id, url)");
// Tables for feed feature
database.execSQL("CREATE TABLE IF NOT EXISTS feed "
+ "(stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(stream_id, subscription_id), "
+ "FOREIGN KEY(stream_id) REFERENCES streams(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, "
+ "icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)");
database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join "
+ "(group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(group_id, subscription_id), "
+ "FOREIGN KEY(group_id) REFERENCES feed_group(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id "
+ "ON feed_group_subscription_join (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated "
+ "(subscription_id INTEGER NOT NULL, last_updated INTEGER, "
+ "PRIMARY KEY(subscription_id), "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
}
};
public static final Migration MIGRATION_3_4 = new Migration(DB_VER_3, DB_VER_4) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL(
"ALTER TABLE streams ADD COLUMN uploader_url TEXT"
);
}
};
public static final Migration MIGRATION_4_5 = new Migration(DB_VER_4, DB_VER_5) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
public static final Migration MIGRATION_6_7 = new Migration(DB_VER_6, DB_VER_7) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
// Create a new column thumbnail_stream_id
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `thumbnail_stream_id` "
+ "INTEGER NOT NULL DEFAULT -1");
// Migrate the thumbnail_url to the thumbnail_stream_id
database.execSQL("UPDATE playlists SET thumbnail_stream_id = ("
+ " SELECT CASE WHEN COUNT(*) != 0 then stream_uid ELSE -1 END"
+ " FROM ("
+ " SELECT p.uid AS playlist_uid, s.uid AS stream_uid"
+ " FROM playlists p"
+ " LEFT JOIN playlist_stream_join ps ON p.uid = ps.playlist_id"
+ " LEFT JOIN streams s ON s.uid = ps.stream_id"
+ " WHERE s.thumbnail_url = p.thumbnail_url) AS temporary_table"
+ " WHERE playlist_uid = playlists.uid)");
// Remove the thumbnail_url field in the playlist table
database.execSQL("CREATE TABLE IF NOT EXISTS `playlists_new`"
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "name TEXT, "
+ "is_thumbnail_permanent INTEGER NOT NULL, "
+ "thumbnail_stream_id INTEGER NOT NULL)");
database.execSQL("INSERT INTO playlists_new"
+ " SELECT uid, name, is_thumbnail_permanent, thumbnail_stream_id "
+ " FROM playlists");
database.execSQL("DROP TABLE playlists");
database.execSQL("ALTER TABLE playlists_new RENAME TO playlists");
database.execSQL("CREATE INDEX IF NOT EXISTS "
+ "`index_playlists_name` ON `playlists` (`name`)");
}
};
public static final Migration MIGRATION_7_8 = new Migration(DB_VER_7, DB_VER_8) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("DELETE FROM search_history WHERE id NOT IN (SELECT id FROM (SELECT "
+ "MIN(id) as id FROM search_history GROUP BY trim(search), service_id ) tmp)");
database.execSQL("UPDATE search_history SET search = trim(search)");
}
};
public static final Migration MIGRATION_8_9 = new Migration(DB_VER_8, DB_VER_9) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
try {
database.beginTransaction();
// Update playlists.
// Create a temp table to initialize display_index.
database.execSQL("CREATE TABLE `playlists_tmp` "
+ "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "`name` TEXT, `is_thumbnail_permanent` INTEGER NOT NULL, "
+ "`thumbnail_stream_id` INTEGER NOT NULL, "
+ "`display_index` INTEGER NOT NULL)");
database.execSQL("INSERT INTO `playlists_tmp` "
+ "(`uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id`, "
+ "`display_index`) "
+ "SELECT `uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id`, "
+ "-1 "
+ "FROM `playlists`");
// Replace the old table, note that this also removes the index on the name which
// we don't need anymore.
database.execSQL("DROP TABLE `playlists`");
database.execSQL("ALTER TABLE `playlists_tmp` RENAME TO `playlists`");
// Update remote_playlists.
// Create a temp table to initialize display_index.
database.execSQL("CREATE TABLE `remote_playlists_tmp` "
+ "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "`service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, "
+ "`thumbnail_url` TEXT, `uploader` TEXT, "
+ "`display_index` INTEGER NOT NULL,"
+ "`stream_count` INTEGER)");
database.execSQL("INSERT INTO `remote_playlists_tmp` (`uid`, `service_id`, "
+ "`name`, `url`, `thumbnail_url`, `uploader`, `display_index`, "
+ "`stream_count`)"
+ "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, "
+ "-1, `stream_count` FROM `remote_playlists`");
// Replace the old table, note that this also removes the index on the name which
// we don't need anymore.
database.execSQL("DROP TABLE `remote_playlists`");
database.execSQL("ALTER TABLE `remote_playlists_tmp` RENAME TO `remote_playlists`");
// Create index on the new table.
database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` "
+ "ON `remote_playlists` (`service_id`, `url`)");
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
};
private Migrations() {
}
}