From 1ac8524dc29dceb7037b3e1fe39a43e8762c60c7 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 4 Feb 2014 19:51:41 +0000
Subject: [PATCH] Allow the code and the DB to have different but compatible
 schemas.

We check for compatibility by specifying a schema version and minimum
schema version in the code, storing them in the DB, and checking whether
the DB is too old for the code or vice versa.
---
 briar-android/res/values/strings.xml          |  2 +-
 .../api/db/DbSchemaException.java             | 10 ---
 .../src/org/briarproject/db/JdbcDatabase.java | 64 +++++++++++--------
 3 files changed, 39 insertions(+), 37 deletions(-)
 delete mode 100644 briar-api/src/org/briarproject/api/db/DbSchemaException.java

diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index a5a51a5188..9920a91944 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -15,7 +15,7 @@
     <string name="passwords_do_not_match">Passwords do not match</string>
     <string name="enter_password">Enter your password:</string>
     <string name="try_again">Wrong password, try again:</string>
-    <string name="startup_failed_notification_title">Briar could not start up</string>
+    <string name="startup_failed_notification_title">Briar could not start</string>
     <string name="startup_failed_notification_text">You may need to reinstall Briar.</string>
     <string name="expiry_warning">This software has expired.\nPlease install a newer version.</string>
     <string name="contact_list_button">Contacts</string>
diff --git a/briar-api/src/org/briarproject/api/db/DbSchemaException.java b/briar-api/src/org/briarproject/api/db/DbSchemaException.java
deleted file mode 100644
index 79dc98533e..0000000000
--- a/briar-api/src/org/briarproject/api/db/DbSchemaException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.briarproject.api.db;
-
-/**
- * Thrown when the schema of the database differs from the schema expected by
- * the code.
- */
-public class DbSchemaException extends DbException {
-
-	private static final long serialVersionUID = -4444069279533651710L;
-}
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index 004c0787b1..a2dd5ac86d 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -38,7 +38,6 @@ import org.briarproject.api.TransportId;
 import org.briarproject.api.TransportProperties;
 import org.briarproject.api.db.DbClosedException;
 import org.briarproject.api.db.DbException;
-import org.briarproject.api.db.DbSchemaException;
 import org.briarproject.api.db.MessageHeader;
 import org.briarproject.api.messaging.Group;
 import org.briarproject.api.messaging.GroupId;
@@ -61,7 +60,8 @@ import org.briarproject.api.transport.TemporarySecret;
  */
 abstract class JdbcDatabase implements Database<Connection> {
 
-	private static final int SCHEMA_VERSION = 2;
+	private static final int SCHEMA_VERSION = 3;
+	private static final int MIN_SCHEMA_VERSION = 3;
 
 	private static final String CREATE_SETTINGS =
 			"CREATE TABLE settings"
@@ -372,11 +372,13 @@ abstract class JdbcDatabase implements Database<Connection> {
 		Connection txn = startTransaction();
 		try {
 			if(reopen) {
-				if(!checkSchemaVersion(txn))
-					throw new DbSchemaException();
+				if(!checkSchemaVersion(txn)) throw new DbException();
 			} else {
 				createTables(txn);
-				setSchemaVersion(txn);
+				String schemaVersion = String.valueOf(SCHEMA_VERSION);
+				setSetting(txn, "schemaVersion", schemaVersion);
+				String minSchemaVersion = String.valueOf(MIN_SCHEMA_VERSION);
+				setSetting(txn, "minSchemaVersion", minSchemaVersion);
 			}
 			commitTransaction(txn);
 		} catch(DbException e) {
@@ -386,27 +388,53 @@ abstract class JdbcDatabase implements Database<Connection> {
 	}
 
 	private boolean checkSchemaVersion(Connection txn) throws DbException {
+		try {
+			String value = getSetting(txn, "schemaVersion");
+			int schemaVersion = Integer.valueOf(value);
+			if(schemaVersion == SCHEMA_VERSION) return true;
+			if(schemaVersion < MIN_SCHEMA_VERSION) return false;
+			value = getSetting(txn, "minSchemaVersion");
+			int minSchemaVersion = Integer.valueOf(value);
+			return SCHEMA_VERSION >= minSchemaVersion;
+		} catch(NumberFormatException e) {
+			throw new DbException(e);
+		}
+	}
+
+	private String getSetting(Connection txn, String key) throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
-		String value;
 		try {
 			String sql = "SELECT value FROM settings WHERE key = ?";
 			ps = txn.prepareStatement(sql);
-			ps.setString(1, "schemaVersion");
+			ps.setString(1, key);
 			rs = ps.executeQuery();
 			if(!rs.next()) throw new DbStateException();
-			value = rs.getString(1);
+			String value = rs.getString(1);
 			if(rs.next()) throw new DbStateException();
 			rs.close();
 			ps.close();
+			return value;
 		} catch(SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);
 			throw new DbException(e);
 		}
+	}
+
+	private void setSetting(Connection txn, String key, String value)
+			throws DbException {
+		PreparedStatement ps = null;
 		try {
-			return Integer.valueOf(value) == SCHEMA_VERSION;
-		} catch(NumberFormatException e) {
+			String sql = "INSERT INTO settings (key, value) VALUES (?, ?)";
+			ps = txn.prepareStatement(sql);
+			ps.setString(1, key);
+			ps.setString(2, value);
+			int affected = ps.executeUpdate();
+			if(affected < 1) throw new DbStateException();
+			ps.close();
+		} catch(SQLException e) {
+			tryToClose(ps);
 			throw new DbException(e);
 		}
 	}
@@ -469,22 +497,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		return s;
 	}
 
-	private void setSchemaVersion(Connection txn) throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "INSERT INTO settings (key, value) VALUES (?, ?)";
-			ps = txn.prepareStatement(sql);
-			ps.setString(1, "schemaVersion");
-			ps.setString(2, String.valueOf(SCHEMA_VERSION));
-			int affected = ps.executeUpdate();
-			if(affected != 1) throw new DbStateException();
-			ps.close();
-		} catch(SQLException e) {
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
 	public Connection startTransaction() throws DbException {
 		Connection txn = null;
 		synchronized(connections) {
-- 
GitLab