Throw meaningful exceptions for schema errors.

This commit is contained in:
akwizgran
2018-02-01 17:07:54 +00:00
parent 088564f22f
commit 2d87e34aa2
6 changed files with 49 additions and 12 deletions

View File

@@ -0,0 +1,7 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses a newer schema than the current code.
*/
public class DataTooNewException extends DbException {
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses an older schema than the current code and
* cannot be migrated.
*/
public class DataTooOldException extends DbException {
}

View File

@@ -37,6 +37,11 @@ public interface DatabaseComponent {
/** /**
* Opens the database and returns true if the database already existed. * Opens the database and returns true if the database already existed.
*
* @throws DataTooNewException if the data uses a newer schema than the
* current code
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/ */
boolean open() throws DbException; boolean open() throws DbException;

View File

@@ -2,6 +2,8 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
@@ -37,6 +39,11 @@ interface Database<T> {
/** /**
* Opens the database and returns true if the database already existed. * Opens the database and returns true if the database already existed.
*
* @throws DataTooNewException if the data uses a newer schema than the
* current code
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/ */
boolean open() throws DbException; boolean open() throws DbException;

View File

@@ -3,6 +3,8 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DbClosedException; import org.briarproject.bramble.api.db.DbClosedException;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
@@ -295,7 +297,7 @@ abstract class JdbcDatabase implements Database<Connection> {
Connection txn = startTransaction(); Connection txn = startTransaction();
try { try {
if (reopen) { if (reopen) {
if (!checkSchemaVersion(txn)) throw new DbException(); checkSchemaVersion(txn);
} else { } else {
createTables(txn); createTables(txn);
storeSchemaVersion(txn, CODE_SCHEMA_VERSION); storeSchemaVersion(txn, CODE_SCHEMA_VERSION);
@@ -310,16 +312,21 @@ abstract class JdbcDatabase implements Database<Connection> {
/** /**
* Compares the schema version stored in the database with the schema * Compares the schema version stored in the database with the schema
* version used by the current code, applies any suitable migrations to the * version used by the current code and applies any suitable migrations to
* data if necessary, and returns true if the schema now matches the * the data if necessary.
* current code. *
* @throws DataTooNewException if the data uses a newer schema than the
* current code
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/ */
private boolean checkSchemaVersion(Connection txn) throws DbException { private void checkSchemaVersion(Connection txn) throws DbException {
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE); Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1); int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1);
if (dataSchemaVersion == -1) return false; if (dataSchemaVersion == -1) throw new DbException();
if (dataSchemaVersion == CODE_SCHEMA_VERSION) return true; if (dataSchemaVersion == CODE_SCHEMA_VERSION) return;
if (CODE_SCHEMA_VERSION < dataSchemaVersion) return false; if (CODE_SCHEMA_VERSION < dataSchemaVersion)
throw new DataTooNewException();
// Apply any suitable migrations in order // Apply any suitable migrations in order
for (Migration<Connection> m : getMigrations()) { for (Migration<Connection> m : getMigrations()) {
int start = m.getStartVersion(), end = m.getEndVersion(); int start = m.getStartVersion(), end = m.getEndVersion();
@@ -333,7 +340,8 @@ abstract class JdbcDatabase implements Database<Connection> {
dataSchemaVersion = end; dataSchemaVersion = end;
} }
} }
return dataSchemaVersion == CODE_SCHEMA_VERSION; if (dataSchemaVersion != CODE_SCHEMA_VERSION)
throw new DataTooOldException();
} }
// Package access for testing // Package access for testing

View File

@@ -1,5 +1,7 @@
package org.briarproject.bramble.db; package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -95,7 +97,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
db.close(); db.close();
} }
@Test(expected = DbException.class) @Test(expected = DataTooNewException.class)
public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception { public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception {
// Open the DB for the first time // Open the DB for the first time
Database<Connection> db = createDatabase(asList(migration, migration1)); Database<Connection> db = createDatabase(asList(migration, migration1));
@@ -109,7 +111,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
db.open(); db.open();
} }
@Test(expected = DbException.class) @Test(expected = DataTooOldException.class)
public void testThrowsExceptionIfCodeIsNewerThanDataAndNoMigrations() public void testThrowsExceptionIfCodeIsNewerThanDataAndNoMigrations()
throws Exception { throws Exception {
// Open the DB for the first time // Open the DB for the first time
@@ -123,7 +125,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
db.open(); db.open();
} }
@Test(expected = DbException.class) @Test(expected = DataTooOldException.class)
public void testThrowsExceptionIfCodeIsNewerThanDataAndNoSuitableMigration() public void testThrowsExceptionIfCodeIsNewerThanDataAndNoSuitableMigration()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{