From 0b95b426077ffacd1755769bb85f9089d3c0bb66 Mon Sep 17 00:00:00 2001 From: Fabian Schlenz Date: Wed, 13 Jul 2016 06:56:00 +0200 Subject: [PATCH] Moved Dabase changes into their own class. --- .../telegram_backup/Database.java | 176 +---------- .../telegram_backup/DatabaseUpdates.java | 296 ++++++++++++++++++ 2 files changed, 306 insertions(+), 166 deletions(-) create mode 100644 src/main/java/de/fabianonline/telegram_backup/DatabaseUpdates.java diff --git a/src/main/java/de/fabianonline/telegram_backup/Database.java b/src/main/java/de/fabianonline/telegram_backup/Database.java index 851b20c..b07f68e 100644 --- a/src/main/java/de/fabianonline/telegram_backup/Database.java +++ b/src/main/java/de/fabianonline/telegram_backup/Database.java @@ -47,8 +47,8 @@ import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; public class Database { private Connection conn; private Statement stmt; - private UserManager user_manager; - private TelegramClient client; + public UserManager user_manager; + public TelegramClient client; public Database(UserManager user_manager, TelegramClient client) { this(user_manager, client, true); @@ -81,176 +81,20 @@ public class Database { private void init(boolean update_db) { if (!update_db) return; - - try { - int version; - ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='database_versions'"); - rs.next(); - if (rs.getInt(1)==0) { - version = 0; - } else { - rs.close(); - rs = stmt.executeQuery("SELECT MAX(version) FROM database_versions"); - rs.next(); - version = rs.getInt(1); - rs.close(); - } - System.out.println("Database version: " + version); - - if (version==0) { - System.out.println(" Updating to version 1..."); - stmt.executeUpdate("CREATE TABLE messages (" - + "id INTEGER PRIMARY KEY ASC, " - + "dialog_id INTEGER, " - + "to_id INTEGER, " - + "from_id INTEGER, " - + "from_type TEXT, " - + "text TEXT, " - + "time TEXT, " - + "has_media BOOLEAN, " - + "sticker TEXT, " - + "data BLOB," - + "type TEXT)"); - stmt.executeUpdate("CREATE TABLE dialogs (" - + "id INTEGER PRIMARY KEY ASC, " - + "name TEXT, " - + "type TEXT)"); - stmt.executeUpdate("CREATE TABLE people (" - + "id INTEGER PRIMARY KEY ASC, " - + "first_name TEXT, " - + "last_name TEXT, " - + "username TEXT, " - + "type TEXT)"); - stmt.executeUpdate("CREATE TABLE database_versions (" - + "version INTEGER)"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (1)"); - version = 1; - } - if (version==1) { - System.out.println(" Updating to version 2..."); - stmt.executeUpdate("ALTER TABLE people RENAME TO 'users'"); - stmt.executeUpdate("ALTER TABLE users ADD COLUMN phone TEXT"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (2)"); - version = 2; - } - if (version==2) { - System.out.println(" Updating to version 3..."); - stmt.executeUpdate("ALTER TABLE dialogs RENAME TO 'chats'"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (3)"); - version = 3; - } - if (version==3) { - System.out.println(" Updating to version 4..."); - stmt.executeUpdate("CREATE TABLE messages_new (id INTEGER PRIMARY KEY ASC, dialog_id INTEGER, to_id INTEGER, from_id INTEGER, from_type TEXT, text TEXT, time INTEGER, has_media BOOLEAN, sticker TEXT, data BLOB, type TEXT);"); - stmt.executeUpdate("INSERT INTO messages_new SELECT * FROM messages"); - stmt.executeUpdate("DROP TABLE messages"); - stmt.executeUpdate("ALTER TABLE messages_new RENAME TO 'messages'"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (4)"); - version = 4; - } - if (version==4) { - System.out.println(" Updating to version 5..."); - stmt.executeUpdate("CREATE TABLE runs (id INTEGER PRIMARY KEY ASC, time INTEGER, start_id INTEGER, end_id INTEGER, count_missing INTEGER)"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (5)"); - version = 5; - } - if (version==5) { - backupDatabase(5); - System.out.println(" Updating to version 6..."); - stmt.executeUpdate( - "CREATE TABLE messages_new (\n" + - " id INTEGER PRIMARY KEY ASC,\n" + - " message_type TEXT,\n" + - " dialog_id INTEGER,\n" + - " chat_id INTEGER,\n" + - " sender_id INTEGER,\n" + - " fwd_from_id INTEGER,\n" + - " text TEXT,\n" + - " time INTEGER,\n" + - " has_media BOOLEAN,\n" + - " media_type TEXT,\n" + - " media_file TEXT,\n" + - " media_size INTEGER,\n" + - " media_json TEXT,\n" + - " markup_json TEXT,\n" + - " data BLOB)"); - LinkedHashMap mappings = new LinkedHashMap(); - mappings.put("id", "id"); - mappings.put("message_type", "type"); - mappings.put("dialog_id", "CASE from_type WHEN 'user' THEN dialog_id ELSE NULL END"); - mappings.put("chat_id", "CASE from_type WHEN 'chat' THEN dialog_id ELSE NULL END"); - mappings.put("sender_id", "from_id"); - mappings.put("text", "text"); - mappings.put("time", "time"); - mappings.put("has_media", "has_media"); - mappings.put("data", "data"); - StringBuilder query = new StringBuilder("INSERT INTO messages_new\n("); - boolean first; - first = true; - for(String s : mappings.keySet()) { - if (!first) query.append(", "); - query.append(s); - first = false; - } - query.append(")\nSELECT \n"); - first = true; - for (String s : mappings.values()) { - if (!first) query.append(", "); - query.append(s); - first = false; - } - query.append("\nFROM messages"); - stmt.executeUpdate(query.toString()); - - System.out.println(" Updating the data (this might take some time)..."); - rs = stmt.executeQuery("SELECT id, data FROM messages_new"); - PreparedStatement ps = conn.prepareStatement("UPDATE messages_new SET fwd_from_id=?, media_type=?, media_file=?, media_size=? WHERE id=?"); - while (rs.next()) { - ps.setInt(5, rs.getInt(1)); - TLMessage msg = bytesToTLMessage(rs.getBytes(2)); - if (msg==null || msg.getFwdFromId()==null || ! (msg.getFwdFromId() instanceof TLPeerUser)) { - ps.setNull(1, Types.INTEGER); - } else { - ps.setInt(1, ((TLPeerUser)msg.getFwdFromId()).getUserId()); - } - AbstractMediaFileManager f = FileManagerFactory.getFileManager(msg, user_manager, client); - if (f==null) { - ps.setNull(2, Types.VARCHAR); - ps.setNull(3, Types.VARCHAR); - ps.setNull(4, Types.INTEGER); - } else { - ps.setString(2, f.getName()); - ps.setString(3, f.getTargetFilename()); - ps.setInt(4, f.getSize()); - } - ps.addBatch(); - } - rs.close(); - conn.setAutoCommit(false); - ps.executeBatch(); - conn.commit(); - conn.setAutoCommit(true); - stmt.executeUpdate("DROP TABLE messages"); - stmt.executeUpdate("ALTER TABLE messages_new RENAME TO messages"); - stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (6)"); - version = 6; - } - - - - } catch (SQLException e) { - System.out.println(e.getSQLState()); - throw new RuntimeException(e); - } + DatabaseUpdates updates = new DatabaseUpdates(conn, this); + updates.doUpdates(); } - private void backupDatabase(int currentVersion) { + public void backupDatabase(int currentVersion) { String filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion); System.out.println(" Creating a backup of your database as " + filename); try { + String src = user_manager.getFileBase() + Config.FILE_NAME_DB; + String dst = user_manager.getFileBase() + filename; + Log.debug("Copying %s to %s", src, dst); Files.copy( - new File(user_manager.getFileBase() + Config.FILE_NAME_DB).toPath(), - new File(user_manager.getFileBase() + filename).toPath(), + new File(src).toPath(), + new File(dst).toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/de/fabianonline/telegram_backup/DatabaseUpdates.java b/src/main/java/de/fabianonline/telegram_backup/DatabaseUpdates.java new file mode 100644 index 0000000..030169d --- /dev/null +++ b/src/main/java/de/fabianonline/telegram_backup/DatabaseUpdates.java @@ -0,0 +1,296 @@ +package de.fabianonline.telegram_backup; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.sql.ResultSet; +import java.sql.PreparedStatement; +import com.github.badoualy.telegram.tl.api.*; +import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; +import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; + +public class DatabaseUpdates { + protected Connection conn; + protected Database db; + private static LinkedList updates = new LinkedList(); + + public DatabaseUpdates(Connection conn, Database db) { + this.conn = conn; + this.db = db; + Log.debug("Registering Database Updates..."); + Log.up(); + register(new DB_Update_1(conn, db)); + register(new DB_Update_2(conn, db)); + register(new DB_Update_3(conn, db)); + register(new DB_Update_4(conn, db)); + register(new DB_Update_5(conn, db)); + register(new DB_Update_6(conn, db)); + Log.down(); + } + + public void doUpdates() { + try { + Statement stmt = conn.createStatement(); + ResultSet rs; + Log.debug("DatabaseUpdate.doUpdates running"); + Log.up(); + + Log.debug("Getting current database version"); + Log.up(); + int version; + Log.debug("Checking if table database_versions exists"); + rs = stmt.executeQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='database_versions'"); + rs.next(); + if (rs.getInt(1)==0) { + Log.debug("Table does not exist"); + version = 0; + } else { + Log.debug("Table exists. Checking max version"); + rs.close(); + rs = stmt.executeQuery("SELECT MAX(version) FROM database_versions"); + rs.next(); + version = rs.getInt(1); + rs.close(); + } + Log.debug("version: %d", version); + Log.down(); + System.out.println("Database version: " + version); + Log.debug("Max available database version is %d", getMaxPossibleVersion()); + + if (version < getMaxPossibleVersion()) { + Log.debug("Update is necessary. %d => %d.", version, getMaxPossibleVersion()); + boolean backup = false; + for (int i=version+1; i<=getMaxPossibleVersion(); i++) { + if (getUpdateToVersion(i).needsBackup()) { + Log.debug("Update to version %d needs a backup", i); + backup=true; + } + } + if (backup) { + Log.debug("Performing backup"); + Log.up(); + db.backupDatabase(version); + Log.down(); + } + + Log.debug("Applying updates"); + Log.up(); + try { + for (int i=version+1; i<=getMaxPossibleVersion(); i++) { + Log.up(); + getUpdateToVersion(i).doUpdate(); + Log.down(); + } + } catch (SQLException e) { throw new RuntimeException(e); } + Log.down(); + } else { + Log.debug("No update necessary."); + } + + Log.down(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private DatabaseUpdate getUpdateToVersion(int i) { return updates.get(i); } + + private int getMaxPossibleVersion() { + return updates.size(); + } + + private void register(DatabaseUpdate d) { + Log.debug("Registering %s as update to version %d", d.getClass().getName(), d.getVersion()); + if (d.getVersion() != updates.size()+1) { + throw new RuntimeException("Tried to register DB update to version " + d.getVersion() + ", but would need update to version " + (updates.size()+1)); + } + updates.add(d); + } +} + +abstract class DatabaseUpdate { + protected Connection conn; + protected Statement stmt; + protected Database db; + public DatabaseUpdate(Connection conn, Database db) { + this.conn = conn; + try { + stmt = conn.createStatement(); + } catch (SQLException e) { throw new RuntimeException(e); } + this.db = db; + + } + public void doUpdate() throws SQLException { + Log.debug("Applying update to version %d", getVersion()); + System.out.println(" Updating to version " + getVersion() + "..."); + Log.up(); + _doUpdate(); + Log.down(); + Log.debug("Saving current database version to the db"); + stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (" + getVersion() + ")"); + } + + protected abstract void _doUpdate() throws SQLException; + public abstract int getVersion(); + public boolean needsBackup() { return false; } +} + +class DB_Update_1 extends DatabaseUpdate { + public int getVersion() { return 1; } + public DB_Update_1(Connection conn, Database db) { super(conn, db); } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate("CREATE TABLE messages (" + + "id INTEGER PRIMARY KEY ASC, " + + "dialog_id INTEGER, " + + "to_id INTEGER, " + + "from_id INTEGER, " + + "from_type TEXT, " + + "text TEXT, " + + "time TEXT, " + + "has_media BOOLEAN, " + + "sticker TEXT, " + + "data BLOB," + + "type TEXT)"); + stmt.executeUpdate("CREATE TABLE dialogs (" + + "id INTEGER PRIMARY KEY ASC, " + + "name TEXT, " + + "type TEXT)"); + stmt.executeUpdate("CREATE TABLE people (" + + "id INTEGER PRIMARY KEY ASC, " + + "first_name TEXT, " + + "last_name TEXT, " + + "username TEXT, " + + "type TEXT)"); + stmt.executeUpdate("CREATE TABLE database_versions (" + + "version INTEGER)"); + } +} + +class DB_Update_2 extends DatabaseUpdate { + public int getVersion() { return 2; } + public DB_Update_2(Connection conn, Database db) { super(conn, db); } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate("ALTER TABLE people RENAME TO 'users'"); + stmt.executeUpdate("ALTER TABLE users ADD COLUMN phone TEXT"); + } +} + +class DB_Update_3 extends DatabaseUpdate { + public int getVersion() { return 3; } + public DB_Update_3(Connection conn, Database db) { super(conn, db); } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate("ALTER TABLE dialogs RENAME TO 'chats'"); + } +} + +class DB_Update_4 extends DatabaseUpdate { + public int getVersion() { return 4; } + public DB_Update_4(Connection conn, Database db) { super(conn, db); } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate("CREATE TABLE messages_new (id INTEGER PRIMARY KEY ASC, dialog_id INTEGER, to_id INTEGER, from_id INTEGER, from_type TEXT, text TEXT, time INTEGER, has_media BOOLEAN, sticker TEXT, data BLOB, type TEXT);"); + stmt.executeUpdate("INSERT INTO messages_new SELECT * FROM messages"); + stmt.executeUpdate("DROP TABLE messages"); + stmt.executeUpdate("ALTER TABLE messages_new RENAME TO 'messages'"); + } +} + +class DB_Update_5 extends DatabaseUpdate { + public int getVersion() { return 5; } + public DB_Update_5(Connection conn, Database db) { super(conn, db); } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate("CREATE TABLE runs (id INTEGER PRIMARY KEY ASC, time INTEGER, start_id INTEGER, end_id INTEGER, count_missing INTEGER)"); + } +} + +class DB_Update_6 extends DatabaseUpdate { + public int getVersion() { return 6; } + public DB_Update_6(Connection conn, Database db) { super(conn, db); } + public boolean needsBackup() { return true; } + + protected void _doUpdate() throws SQLException { + stmt.executeUpdate( + "CREATE TABLE messages_new (\n" + + " id INTEGER PRIMARY KEY ASC,\n" + + " message_type TEXT,\n" + + " dialog_id INTEGER,\n" + + " chat_id INTEGER,\n" + + " sender_id INTEGER,\n" + + " fwd_from_id INTEGER,\n" + + " text TEXT,\n" + + " time INTEGER,\n" + + " has_media BOOLEAN,\n" + + " media_type TEXT,\n" + + " media_file TEXT,\n" + + " media_size INTEGER,\n" + + " media_json TEXT,\n" + + " markup_json TEXT,\n" + + " data BLOB)"); + LinkedHashMap mappings = new LinkedHashMap(); + mappings.put("id", "id"); + mappings.put("message_type", "type"); + mappings.put("dialog_id", "CASE from_type WHEN 'user' THEN dialog_id ELSE NULL END"); + mappings.put("chat_id", "CASE from_type WHEN 'chat' THEN dialog_id ELSE NULL END"); + mappings.put("sender_id", "from_id"); + mappings.put("text", "text"); + mappings.put("time", "time"); + mappings.put("has_media", "has_media"); + mappings.put("data", "data"); + StringBuilder query = new StringBuilder("INSERT INTO messages_new\n("); + boolean first; + first = true; + for(String s : mappings.keySet()) { + if (!first) query.append(", "); + query.append(s); + first = false; + } + query.append(")\nSELECT \n"); + first = true; + for (String s : mappings.values()) { + if (!first) query.append(", "); + query.append(s); + first = false; + } + query.append("\nFROM messages"); + stmt.executeUpdate(query.toString()); + + System.out.println(" Updating the data (this might take some time)..."); + ResultSet rs = stmt.executeQuery("SELECT id, data FROM messages_new"); + PreparedStatement ps = conn.prepareStatement("UPDATE messages_new SET fwd_from_id=?, media_type=?, media_file=?, media_size=? WHERE id=?"); + while (rs.next()) { + ps.setInt(5, rs.getInt(1)); + TLMessage msg = db.bytesToTLMessage(rs.getBytes(2)); + if (msg==null || msg.getFwdFromId()==null || ! (msg.getFwdFromId() instanceof TLPeerUser)) { + ps.setNull(1, Types.INTEGER); + } else { + ps.setInt(1, ((TLPeerUser)msg.getFwdFromId()).getUserId()); + } + AbstractMediaFileManager f = FileManagerFactory.getFileManager(msg, db.user_manager, db.client); + if (f==null) { + ps.setNull(2, Types.VARCHAR); + ps.setNull(3, Types.VARCHAR); + ps.setNull(4, Types.INTEGER); + } else { + ps.setString(2, f.getName()); + ps.setString(3, f.getTargetFilename()); + ps.setInt(4, f.getSize()); + } + ps.addBatch(); + } + rs.close(); + conn.setAutoCommit(false); + ps.executeBatch(); + conn.commit(); + conn.setAutoCommit(true); + stmt.executeUpdate("DROP TABLE messages"); + stmt.executeUpdate("ALTER TABLE messages_new RENAME TO messages"); + } +}