mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
Compare commits
3 Commits
sqlite-jdb
...
library-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cbc67c3cd | ||
|
|
da186abced | ||
|
|
98d1fc4ce2 |
@@ -104,12 +104,3 @@ mailbox integration test:
|
||||
script:
|
||||
- (cd briar-mailbox; git fetch; git reset --hard origin/main)
|
||||
- MAILBOX_INTEGRATION_TESTS=true ./gradlew --info mailbox-integration-tests:test
|
||||
|
||||
db_performance_comparison_test:
|
||||
extends: .base-test
|
||||
stage: optional_tests
|
||||
script:
|
||||
- OPTIONAL_TESTS=org.briarproject.bramble.db.H2SqliteDatabasePerformanceComparisonTest ./gradlew --info -Djava.security.egd=file:/dev/urandom :bramble-core:test --tests H2SqliteDatabasePerformanceComparisonTest
|
||||
rules:
|
||||
- when: manual
|
||||
|
||||
|
||||
1
bramble-android/.gitignore
vendored
1
bramble-android/.gitignore
vendored
@@ -4,4 +4,3 @@ build
|
||||
.settings
|
||||
src/main/res/raw/*.zip
|
||||
src/main/jniLibs
|
||||
!src/main/jniLibs/.gitkeep
|
||||
@@ -11,8 +11,6 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// FIXME: sqlite-jdbc-crypt uses __register_atfork which is only available on API >= 23.
|
||||
// We might be able to solve this by recompiling (or asking upstream to recompile)
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 10506
|
||||
@@ -39,7 +37,6 @@ android {
|
||||
|
||||
configurations {
|
||||
tor
|
||||
sqliteJdbcCrypt
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -60,8 +57,6 @@ dependencies {
|
||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||
tor "org.briarproject:snowflake-android:$snowflake_version"
|
||||
|
||||
sqliteJdbcCrypt "io.github.willena:sqlite-jdbc:$sqlite_jdbc_crypt_version"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
@@ -74,50 +69,26 @@ dependencies {
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
}
|
||||
|
||||
def jniLibsDir = 'src/main/jniLibs'
|
||||
def torLibsDir = 'src/main/jniLibs'
|
||||
|
||||
task cleanJniLibs {
|
||||
inputs.dir jniLibsDir
|
||||
outputs.dir jniLibsDir
|
||||
task cleanTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
delete fileTree(jniLibsDir).filter { it.name.endsWith('.so') }
|
||||
delete fileTree(torLibsDir)
|
||||
}
|
||||
}
|
||||
|
||||
clean.dependsOn cleanJniLibs
|
||||
clean.dependsOn cleanTorBinaries
|
||||
|
||||
task unpackJniLibs {
|
||||
outputs.dir jniLibsDir
|
||||
task unpackTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
// Tor
|
||||
copy {
|
||||
from configurations.tor.collect { zipTree(it) }
|
||||
into jniLibsDir
|
||||
}
|
||||
// sqlite-jdbc-crypt
|
||||
def archMap = [
|
||||
aarch64: 'arm64-v8a',
|
||||
arm : 'armeabi-v7a',
|
||||
x86 : 'x86',
|
||||
x86_64 : 'x86_64'
|
||||
]
|
||||
configurations.sqliteJdbcCrypt.collect { File artifact ->
|
||||
zipTree(artifact).each { File f ->
|
||||
for (String arch : archMap.keySet()) {
|
||||
if (f.absolutePath.endsWith("/Linux-Android/$arch/libsqlitejdbc.so")) {
|
||||
def archDir = new File(jniLibsDir, archMap.get(arch))
|
||||
archDir.mkdirs()
|
||||
copy {
|
||||
from f
|
||||
into archDir
|
||||
}
|
||||
break
|
||||
into torLibsDir
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dependsOn cleanJniLibs
|
||||
dependsOn cleanTorBinaries
|
||||
}
|
||||
|
||||
preBuild.dependsOn unpackJniLibs
|
||||
preBuild.dependsOn unpackTorBinaries
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
-dontwarn org.h2.**
|
||||
-dontnote org.h2.**
|
||||
|
||||
# Keep sqlite-jdbc-crypt classes that are loaded via reflection or accessed via JNI
|
||||
-keep class org.sqlite.** { *; }
|
||||
|
||||
-keep class dagger.** { *; }
|
||||
-dontwarn dagger.**
|
||||
-dontnote dagger.**
|
||||
|
||||
@@ -18,7 +18,6 @@ dependencyVerification {
|
||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||
'io.github.willena:sqlite-jdbc:3.41.2.1:sqlite-jdbc-3.41.2.1.jar:fb60e7137c1791db89240701338d31ca42a0bec5508c1aab1c1131cf885f2309',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
|
||||
@@ -14,9 +14,9 @@ dependencies {
|
||||
api "org.briarproject:onionwrapper-core:$onionwrapper_version"
|
||||
|
||||
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation "io.github.willena:sqlite-jdbc:$sqlite_jdbc_crypt_version"
|
||||
// 2.2.222 includes a precompiled Java 21 class that breaks the jetifier, so it will require
|
||||
// a Gradle/AGP upgrade
|
||||
implementation 'com.h2database:h2:2.2.220'
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
|
||||
@@ -413,9 +413,6 @@ interface Database<T> {
|
||||
*/
|
||||
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
|
||||
|
||||
Collection<String> explainGetMessageIds(T txn, GroupId g)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any delivered messages in the given group with
|
||||
* metadata that matches all entries in the given query. If the query is
|
||||
|
||||
@@ -24,7 +24,7 @@ public class DatabaseModule {
|
||||
@Singleton
|
||||
Database<Connection> provideDatabase(DatabaseConfig config,
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
return new SqliteDatabase(config, messageFactory, clock);
|
||||
return new H2Database(config, messageFactory, clock);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -4,16 +4,14 @@ class DatabaseTypes {
|
||||
|
||||
private final String hashType, secretType, binaryType;
|
||||
private final String counterType, stringType;
|
||||
private final String explainCommand; // FIXME: Remove
|
||||
|
||||
public DatabaseTypes(String hashType, String secretType, String binaryType,
|
||||
String counterType, String stringType, String explainCommand) {
|
||||
String counterType, String stringType) {
|
||||
this.hashType = hashType;
|
||||
this.secretType = secretType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.stringType = stringType;
|
||||
this.explainCommand = explainCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +22,6 @@ class DatabaseTypes {
|
||||
* <li> _BINARY
|
||||
* <li> _COUNTER
|
||||
* <li> _STRING
|
||||
* <li> _EXPLAIN
|
||||
*/
|
||||
String replaceTypes(String s) {
|
||||
s = s.replaceAll("_HASH", hashType);
|
||||
@@ -32,7 +29,6 @@ class DatabaseTypes {
|
||||
s = s.replaceAll("_BINARY", binaryType);
|
||||
s = s.replaceAll("_COUNTER", counterType);
|
||||
s = s.replaceAll("_STRING", stringType);
|
||||
s = s.replaceAll("_EXPLAIN", explainCommand);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,14 +38,11 @@ class H2Database extends JdbcDatabase {
|
||||
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
|
||||
private static final String BINARY_TYPE = "BINARY VARYING";
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String EXPLAIN_COMMAND = "EXPLAIN";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
@@ -60,8 +57,7 @@ class H2Database extends JdbcDatabase {
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
||||
+ ";WRITE_DELAY=0";
|
||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;WRITE_DELAY=0";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,7 +72,7 @@ class H2Database extends JdbcDatabase {
|
||||
boolean reopen = isNonEmptyDirectory(dir);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
|
||||
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
|
||||
super.open("org.h2.Driver", reopen, false, key, listener);
|
||||
super.open("org.h2.Driver", reopen, key, listener);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory after opening DB:");
|
||||
logFileOrDir(LOG, INFO, dir.getParentFile());
|
||||
|
||||
@@ -38,13 +38,11 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE = "INTEGER NOT NULL"
|
||||
+ " PRIMARY KEY GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String EXPLAIN_COMMAND = "EXPLAIN PLAN FOR";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
@@ -72,7 +70,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
boolean reopen = isNonEmptyDirectory(dir);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
|
||||
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, true, key, listener);
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE TABLE settings"
|
||||
+ " (namespace _STRING NOT NULL,"
|
||||
+ " settingKey _STRING NOT NULL,"
|
||||
+ " value _STRING NOT NULL,"
|
||||
+ " settingValue _STRING NOT NULL,"
|
||||
+ " PRIMARY KEY (namespace, settingKey))";
|
||||
|
||||
private static final String CREATE_LOCAL_AUTHORS =
|
||||
@@ -143,7 +143,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " handshakePublicKey _BINARY," // Null if key is unknown
|
||||
+ " localAuthorId _HASH NOT NULL,"
|
||||
+ " verified BOOLEAN NOT NULL,"
|
||||
+ " syncVersions _BINARY DEFAULT x'00' NOT NULL,"
|
||||
+ " syncVersions _BINARY DEFAULT X'00' NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId),"
|
||||
+ " FOREIGN KEY (localAuthorId)"
|
||||
+ " REFERENCES localAuthors (authorId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
@@ -160,7 +161,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE TABLE groupMetadata"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " metaValue _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId, metaKey),"
|
||||
+ " FOREIGN KEY (groupId)"
|
||||
+ " REFERENCES groups (groupId)"
|
||||
@@ -205,7 +206,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " groupId _HASH NOT NULL," // Denormalised
|
||||
+ " state INT NOT NULL," // Denormalised
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " metaValue _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, metaKey),"
|
||||
+ " FOREIGN KEY (messageId)"
|
||||
+ " REFERENCES messages (messageId)"
|
||||
@@ -294,11 +295,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " active BOOLEAN NOT NULL,"
|
||||
+ " rootKey _SECRET," // Null for rotation keys
|
||||
+ " alice BOOLEAN," // Null for rotation keys
|
||||
// FIXME: Primary key has changed, migration needed
|
||||
+ " PRIMARY KEY (transportId, keySetId),"
|
||||
+ " FOREIGN KEY (transportId)"
|
||||
+ " REFERENCES transports (transportId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
// FIXME: Unique constraint removed, migration needed
|
||||
+ " UNIQUE (keySetId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -357,85 +358,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)";
|
||||
|
||||
// FIXME: Migration needs to add new index
|
||||
private static final String INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID_KEY_SET_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByTransportIdKeySetId"
|
||||
+ " ON outgoingKeys (transportId, keySetId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_CONTACTS_BY_LOCAL_AUTHOR_ID =
|
||||
"CREATE INDEX IF NOT EXISTS contactsByLocalAuthorId"
|
||||
+ " ON contacts (localAuthorId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_GROUP_METADATA_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS groupMetadataByGroupId"
|
||||
+ " ON groupMetadata (groupId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_GROUP_VISIBILITIES_BY_CONTACT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS groupVisibilitiesByContactId"
|
||||
+ " ON groupVisibilities (contactId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_GROUP_VISIBILITIES_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS groupVisibilitiesByGroupId"
|
||||
+ " ON groupVisibilities (groupId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_MESSAGES_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS messagesByGroupId"
|
||||
+ " ON messages (groupId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_MESSAGE_METADATA_BY_MESSAGE_ID =
|
||||
"CREATE INDEX IF NOT EXISTS messageMetadataByMessageId"
|
||||
+ " ON messageMetadata (messageId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_MESSAGE_METADATA_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupId"
|
||||
+ " ON messageMetadata (groupId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_MESSAGE_DEPENDENCIES_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS messageDependenciesByGroupId"
|
||||
+ " ON messageDependencies (groupId)";
|
||||
|
||||
private static final String
|
||||
FOREIGN_INDEX_MESSAGE_DEPENDENCIES_BY_MESSAGE_ID =
|
||||
"CREATE INDEX IF NOT EXISTS messageDependenciesByMessageId"
|
||||
+ " ON messageDependencies (messageId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_OFFERS_BY_CONTACT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS offersByContactId"
|
||||
+ " ON offers (contactId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_STATUSES_BY_MESSAGE_ID =
|
||||
"CREATE INDEX IF NOT EXISTS statusesByMessageId"
|
||||
+ " ON statuses (messageId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_STATUSES_BY_CONTACT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS statusesByContactId"
|
||||
+ " ON statuses (contactId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_STATUSES_BY_GROUP_ID =
|
||||
"CREATE INDEX IF NOT EXISTS statusesByGroupId"
|
||||
+ " ON statuses (groupId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByTransportId"
|
||||
+ " ON outgoingKeys (transportId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_OUTGOING_KEYS_BY_CONTACT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByContactId"
|
||||
+ " ON outgoingKeys (contactId)";
|
||||
|
||||
private static final String
|
||||
FOREIGN_INDEX_OUTGOING_KEYS_BY_PENDING_CONTACT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByPendingContactId"
|
||||
+ " ON outgoingKeys (pendingContactId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_INCOMING_KEYS_BY_TRANSPORT_ID =
|
||||
"CREATE INDEX IF NOT EXISTS incomingKeysByTransportId"
|
||||
+ " ON incomingKeys (transportId)";
|
||||
|
||||
private static final String FOREIGN_INDEX_INCOMING_KEYS_BY_KEY_SET_ID =
|
||||
"CREATE INDEX IF NOT EXISTS incomingKeysByKeySetId"
|
||||
+ " ON incomingKeys (keySetId)";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
@@ -471,7 +393,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
protected void open(String driverClass, boolean reopen,
|
||||
boolean createForeignKeyIndexes,
|
||||
@SuppressWarnings("unused") SecretKey key,
|
||||
@Nullable MigrationListener listener) throws DbException {
|
||||
// Load the JDBC driver
|
||||
@@ -498,7 +419,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("db dirty? " + wasDirtyOnInitialisation);
|
||||
}
|
||||
createIndexes(txn, createForeignKeyIndexes);
|
||||
createIndexes(txn);
|
||||
setDirty(txn, true);
|
||||
commitTransaction(txn);
|
||||
} catch (DbException e) {
|
||||
@@ -631,8 +552,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
private void createIndexes(Connection txn, boolean createForeignKeyIndexes)
|
||||
throws DbException {
|
||||
private void createIndexes(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
@@ -644,31 +564,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TX_COUNT_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
|
||||
s.executeUpdate(INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID_KEY_SET_ID);
|
||||
// Some DB implementations automatically create indexes on columns
|
||||
// that are foreign keys, others don't
|
||||
if (createForeignKeyIndexes) {
|
||||
s.executeUpdate(FOREIGN_INDEX_CONTACTS_BY_LOCAL_AUTHOR_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_GROUP_METADATA_BY_GROUP_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_GROUP_VISIBILITIES_BY_CONTACT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_GROUP_VISIBILITIES_BY_GROUP_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_MESSAGES_BY_GROUP_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_MESSAGE_METADATA_BY_MESSAGE_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_MESSAGE_METADATA_BY_GROUP_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_MESSAGE_DEPENDENCIES_BY_GROUP_ID);
|
||||
s.executeUpdate(
|
||||
FOREIGN_INDEX_MESSAGE_DEPENDENCIES_BY_MESSAGE_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_OFFERS_BY_CONTACT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_STATUSES_BY_MESSAGE_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_STATUSES_BY_CONTACT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_STATUSES_BY_GROUP_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_OUTGOING_KEYS_BY_CONTACT_ID);
|
||||
s.executeUpdate(
|
||||
FOREIGN_INDEX_OUTGOING_KEYS_BY_PENDING_CONTACT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_INCOMING_KEYS_BY_TRANSPORT_ID);
|
||||
s.executeUpdate(FOREIGN_INDEX_INCOMING_KEYS_BY_KEY_SET_ID);
|
||||
}
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
@@ -2019,38 +1914,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> explainGetMessageIds(Connection txn, GroupId g)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = dbTypes.replaceTypes("_EXPLAIN SELECT messageId"
|
||||
+ " FROM messages"
|
||||
+ " WHERE groupId = ? AND state = ?");
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
ps.setInt(2, DELIVERED.getValue());
|
||||
rs = ps.executeQuery();
|
||||
int cols = rs.getMetaData().getColumnCount();
|
||||
List<String> explanation = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i <= cols; i++) {
|
||||
sb.append(rs.getString(i)).append(' ');
|
||||
}
|
||||
explanation.add(sb.toString());
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return explanation;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessageIds(Connection txn, GroupId g,
|
||||
Metadata query) throws DbException {
|
||||
@@ -2063,7 +1926,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
Set<MessageId> intersection = null;
|
||||
String sql = "SELECT messageId FROM messageMetadata"
|
||||
+ " WHERE groupId = ? AND state = ?"
|
||||
+ " AND metaKey = ? AND value = ?";
|
||||
+ " AND metaKey = ? AND metaValue = ?";
|
||||
for (Entry<String, byte[]> e : query.entrySet()) {
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
@@ -2119,7 +1982,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT messageId, metaKey, value"
|
||||
String sql = "SELECT messageId, metaKey, metaValue"
|
||||
+ " FROM messageMetadata"
|
||||
+ " WHERE groupId = ? AND state = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
@@ -2164,7 +2027,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM groupMetadata"
|
||||
String sql = "SELECT metaKey, metaValue FROM groupMetadata"
|
||||
+ " WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
@@ -2187,7 +2050,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata"
|
||||
String sql = "SELECT metaKey, metaValue FROM messageMetadata"
|
||||
+ " WHERE state = ? AND messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, DELIVERED.getValue());
|
||||
@@ -2211,7 +2074,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata"
|
||||
String sql = "SELECT metaKey, metaValue FROM messageMetadata"
|
||||
+ " WHERE (state = ? OR state = ?)"
|
||||
+ " AND messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
@@ -2734,9 +2597,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(1));
|
||||
String alias = rs.getString(2);
|
||||
long timestamp = rs.getLong(3);
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return new PendingContact(p, publicKey, alias, timestamp);
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
@@ -2818,7 +2678,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT settingKey, value FROM settings"
|
||||
String sql = "SELECT settingKey, settingValue FROM settings"
|
||||
+ " WHERE namespace = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, namespace);
|
||||
@@ -3054,7 +2914,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
g.getBytes(), meta, "groupMetadata", "groupId");
|
||||
if (added.isEmpty()) return;
|
||||
// Insert any keys that don't already exist
|
||||
String sql = "INSERT INTO groupMetadata (groupId, metaKey, value)"
|
||||
String sql = "INSERT INTO groupMetadata"
|
||||
+ " (groupId, metaKey, metaValue)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
@@ -3097,7 +2958,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.close();
|
||||
// Insert any keys that don't already exist
|
||||
sql = "INSERT INTO messageMetadata"
|
||||
+ " (messageId, groupId, state, metaKey, value)"
|
||||
+ " (messageId, groupId, state, metaKey, metaValue)"
|
||||
+ " VALUES (?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
@@ -3156,7 +3017,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
if (notRemoved.isEmpty()) return Collections.emptyMap();
|
||||
// Update any keys that already exist
|
||||
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||
String sql = "UPDATE " + tableName + " SET metaValue = ?"
|
||||
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(2, id);
|
||||
@@ -3193,7 +3054,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
// Update any settings that already exist
|
||||
String sql = "UPDATE settings SET value = ?"
|
||||
String sql = "UPDATE settings SET settingValue = ?"
|
||||
+ " WHERE namespace = ? AND settingKey = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
for (Entry<String, String> e : s.entrySet()) {
|
||||
@@ -3209,7 +3070,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (rows > 1) throw new DbStateException();
|
||||
}
|
||||
// Insert any settings that don't already exist
|
||||
sql = "INSERT INTO settings (namespace, settingKey, value)"
|
||||
sql = "INSERT INTO settings (namespace, settingKey, settingValue)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
int updateIndex = 0, inserted = 0;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbClosedException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.sqlite.mc.SQLiteMCSqlCipherConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
|
||||
|
||||
/**
|
||||
* Contains all the SQLite-specific code for the database.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
class SqliteDatabase extends JdbcDatabase {
|
||||
|
||||
private static final Logger LOG = getLogger(SqliteDatabase.class.getName());
|
||||
|
||||
private static final String HASH_TYPE = "BLOB";
|
||||
private static final String SECRET_TYPE = "BLOB";
|
||||
private static final String BINARY_TYPE = "BLOB";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER PRIMARY KEY AUTOINCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String EXPLAIN_COMMAND = "EXPLAIN QUERY PLAN";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Nullable
|
||||
private volatile Properties properties = null;
|
||||
|
||||
@Inject
|
||||
SqliteDatabase(DatabaseConfig config, MessageFactory messageFactory,
|
||||
Clock clock) {
|
||||
super(dbTypes, messageFactory, clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:sqlite:" + path + "?cipher=sqlcipher";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
properties = SQLiteMCSqlCipherConfig.getDefault()
|
||||
.withHexKey(key.getBytes())
|
||||
.build()
|
||||
.toProperties();
|
||||
File dir = config.getDatabaseDirectory();
|
||||
boolean reopen = isNonEmptyDirectory(dir);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
|
||||
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
|
||||
super.open("org.sqlite.JDBC", reopen, true, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
Connection c = null;
|
||||
try {
|
||||
c = createConnection();
|
||||
setDirty(c, false);
|
||||
c.close();
|
||||
closeAllConnections();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(c, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws DbException, SQLException {
|
||||
Properties properties = this.properties;
|
||||
if (properties == null) throw new DbClosedException();
|
||||
Connection c = DriverManager.getConnection(url, properties);
|
||||
Statement s = null;
|
||||
try {
|
||||
s = c.createStatement();
|
||||
s.execute("PRAGMA foreign_keys = ON");
|
||||
s.execute("PRAGMA secure_delete = ON");
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
tryToClose(c, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compactAndClose() throws DbException {
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class H2SqliteDatabasePerformanceComparisonTest
|
||||
extends DatabasePerformanceComparisonTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
assumeTrue(isOptionalTestEnabled(
|
||||
H2SqliteDatabasePerformanceComparisonTest.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
Database<Connection> createDatabase(boolean conditionA,
|
||||
DatabaseConfig databaseConfig, MessageFactory messageFactory,
|
||||
Clock clock) {
|
||||
if (conditionA) {
|
||||
return new H2Database(databaseConfig, messageFactory, clock);
|
||||
} else {
|
||||
return new SqliteDatabase(databaseConfig, messageFactory, clock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
@@ -23,21 +23,18 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
|
||||
private static final String CREATE_TABLE = "CREATE TABLE foo"
|
||||
+ " (key INT NOT NULL,"
|
||||
+ " (_key INT NOT NULL,"
|
||||
+ " counter INT NOT NULL)";
|
||||
private static final String INSERT_ROW =
|
||||
"INSERT INTO foo (key, counter) VALUES (1, 123)";
|
||||
"INSERT INTO foo (_key, counter) VALUES (1, 123)";
|
||||
private static final String GET_COUNTER =
|
||||
"SELECT counter FROM foo WHERE key = 1";
|
||||
"SELECT counter FROM foo WHERE _key = 1";
|
||||
private static final String SET_COUNTER =
|
||||
"UPDATE foo SET counter = ? WHERE key = 1";
|
||||
"UPDATE foo SET counter = ? WHERE _key = 1";
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String withMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=TRUE;MVCC=TRUE";
|
||||
private final String withoutMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
|
||||
private final String url = "jdbc:h2:split:" + db.getAbsolutePath();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -52,14 +49,14 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testDoesNotReadUncommittedWritesWithMvcc() throws Exception {
|
||||
Connection connection = openConnection(true);
|
||||
Connection connection = openConnection();
|
||||
try {
|
||||
createTableAndInsertRow(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
// Start the first transaction
|
||||
Connection txn1 = openConnection(true);
|
||||
Connection txn1 = openConnection();
|
||||
try {
|
||||
txn1.setAutoCommit(false);
|
||||
// The first transaction should read the initial value
|
||||
@@ -67,7 +64,7 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
// The first transaction updates the value but doesn't commit it
|
||||
assertEquals(1, setCounter(txn1, 234));
|
||||
// Start the second transaction
|
||||
Connection txn2 = openConnection(true);
|
||||
Connection txn2 = openConnection();
|
||||
try {
|
||||
txn2.setAutoCommit(false);
|
||||
// The second transaction should still read the initial value
|
||||
@@ -86,14 +83,14 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testLastWriterWinsWithMvcc() throws Exception {
|
||||
Connection connection = openConnection(true);
|
||||
Connection connection = openConnection();
|
||||
try {
|
||||
createTableAndInsertRow(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
// Start the first transaction
|
||||
Connection txn1 = openConnection(true);
|
||||
Connection txn1 = openConnection();
|
||||
try {
|
||||
txn1.setAutoCommit(false);
|
||||
// The first transaction should read the initial value
|
||||
@@ -101,7 +98,7 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
// The first transaction updates the value but doesn't commit it
|
||||
assertEquals(1, setCounter(txn1, 234));
|
||||
// Start the second transaction
|
||||
Connection txn2 = openConnection(true);
|
||||
Connection txn2 = openConnection();
|
||||
try {
|
||||
txn2.setAutoCommit(false);
|
||||
// The second transaction should still read the initial value
|
||||
@@ -119,7 +116,7 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
txn1.close();
|
||||
}
|
||||
// The second transaction was the last writer, so it should win
|
||||
connection = openConnection(true);
|
||||
connection = openConnection();
|
||||
try {
|
||||
assertEquals(345, getCounter(connection));
|
||||
} finally {
|
||||
@@ -129,20 +126,20 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testLockTimeoutOnRowWithMvcc() throws Exception {
|
||||
Connection connection = openConnection(true);
|
||||
Connection connection = openConnection();
|
||||
try {
|
||||
createTableAndInsertRow(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
// Start the first transaction
|
||||
Connection txn1 = openConnection(true);
|
||||
Connection txn1 = openConnection();
|
||||
try {
|
||||
txn1.setAutoCommit(false);
|
||||
// The first transaction should read the initial value
|
||||
assertEquals(123, getCounter(txn1));
|
||||
// Start the second transaction
|
||||
Connection txn2 = openConnection(true);
|
||||
Connection txn2 = openConnection();
|
||||
try {
|
||||
txn2.setAutoCommit(false);
|
||||
// The second transaction should read the initial value
|
||||
@@ -167,84 +164,8 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLockTimeoutOnTableWithoutMvcc() throws Exception {
|
||||
Connection connection = openConnection(false);
|
||||
try {
|
||||
createTableAndInsertRow(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
// Start the first transaction
|
||||
Connection txn1 = openConnection(false);
|
||||
try {
|
||||
txn1.setAutoCommit(false);
|
||||
// The first transaction should read the initial value
|
||||
assertEquals(123, getCounter(txn1));
|
||||
// Start the second transaction
|
||||
Connection txn2 = openConnection(false);
|
||||
try {
|
||||
txn2.setAutoCommit(false);
|
||||
// The second transaction should read the initial value
|
||||
assertEquals(123, getCounter(txn2));
|
||||
// The first transaction tries to update the value
|
||||
try {
|
||||
setCounter(txn1, 345);
|
||||
fail();
|
||||
} catch (SQLException expected) {
|
||||
// Expected: the table is locked by the second transaction
|
||||
}
|
||||
// Abort the transactions
|
||||
txn1.rollback();
|
||||
txn2.rollback();
|
||||
} finally {
|
||||
txn2.close();
|
||||
}
|
||||
} finally {
|
||||
txn1.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteLockTimeoutOnTableWithoutMvcc() throws Exception {
|
||||
Connection connection = openConnection(false);
|
||||
try {
|
||||
createTableAndInsertRow(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
// Start the first transaction
|
||||
Connection txn1 = openConnection(false);
|
||||
try {
|
||||
txn1.setAutoCommit(false);
|
||||
// The first transaction should read the initial value
|
||||
assertEquals(123, getCounter(txn1));
|
||||
// The first transaction updates the value but doesn't commit yet
|
||||
assertEquals(1, setCounter(txn1, 345));
|
||||
// Start the second transaction
|
||||
Connection txn2 = openConnection(false);
|
||||
try {
|
||||
txn2.setAutoCommit(false);
|
||||
// The second transaction tries to read the value
|
||||
try {
|
||||
getCounter(txn2);
|
||||
fail();
|
||||
} catch (SQLException expected) {
|
||||
// Expected: the table is locked by the first transaction
|
||||
}
|
||||
// Abort the transactions
|
||||
txn1.rollback();
|
||||
txn2.rollback();
|
||||
} finally {
|
||||
txn2.close();
|
||||
}
|
||||
} finally {
|
||||
txn1.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Connection openConnection(boolean mvcc) throws SQLException {
|
||||
return DriverManager.getConnection(mvcc ? withMvcc : withoutMvcc);
|
||||
private Connection openConnection() throws SQLException {
|
||||
return DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
private void createTableAndInsertRow(Connection c) throws SQLException {
|
||||
|
||||
@@ -10,11 +10,9 @@ import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,9 +20,4 @@ public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
return new HyperSqlDatabase(config, messageFactory ,clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testExplainGetMessageIds() {
|
||||
// Ugh, HSQLDB can't handle EXPLAIN PLAN FOR in prepared statements
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2500,21 +2500,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
}
|
||||
|
||||
// FIXME: Remove
|
||||
@Test
|
||||
public void testExplainGetMessageIds() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
db.addGroup(txn, group);
|
||||
Collection<String> explanation = db.explainGetMessageIds(txn, groupId);
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
|
||||
System.out.println("getMessageIds(T, GroupId)");
|
||||
for (String line : explanation) System.out.println(line);
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private Database<Connection> open(boolean resume) throws Exception {
|
||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class SqliteDatabaseTest extends JdbcDatabaseTest {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
return new SqliteDatabase(config, messageFactory, clock);
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,12 @@ dependencyVerification {
|
||||
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||
'com.h2database:h2:2.2.220:h2-2.2.220.jar:978ab863018d3f965e38880571c36293ea8b10a8086194159c4d5d20b50f0a57',
|
||||
'com.squareup.okhttp3:mockwebserver:4.10.0:mockwebserver-4.10.0.jar:af29da234e63159d6e0dea43bf8288eea97d71cdf1651a5ee2d6c0d0d4adbf8f',
|
||||
'com.squareup.okhttp3:okhttp:4.10.0:okhttp-4.10.0.jar:7580f14fa1691206e37081ad3f92063b1603b328da0bb316f2fef02e0562e7ec',
|
||||
'com.squareup.okio:okio-jvm:3.0.0:okio-jvm-3.0.0.jar:be64a0cc1f28ea9cd5c970dd7e7557af72c808d738c495b397bf897c9921e907',
|
||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||
'io.github.willena:sqlite-jdbc:3.41.2.1:sqlite-jdbc-3.41.2.1.jar:fb60e7137c1791db89240701338d31ca42a0bec5508c1aab1c1131cf885f2309',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
|
||||
@@ -21,31 +21,22 @@ android {
|
||||
|
||||
packagingOptions {
|
||||
doNotStrip '**/*.so'
|
||||
jniLibs {
|
||||
// Unpack native libs from the APK rather than using them in-place. We package the
|
||||
// Tor binaries as native libs and need them to be unpacked so we can execute them
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// FIXME: sqlite-jdbc-crypt uses __register_atfork which is only available on API >= 23.
|
||||
// We might be able to solve this by recompiling (or asking upstream to recompile)
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 10506
|
||||
versionName "1.5.6"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
def now = (long) (System.currentTimeMillis() / 1000)
|
||||
buildConfigField "Long", "BuildTimestamp",
|
||||
"${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L"
|
||||
|
||||
testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner'
|
||||
testInstrumentationRunnerArguments disableAnalytics: 'true'
|
||||
}
|
||||
@@ -157,7 +148,7 @@ dependencies {
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.3'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
|
||||
@@ -25,9 +25,8 @@
|
||||
-dontnote com.android.org.conscrypt.SSLParametersImpl
|
||||
-dontnote org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
|
||||
-dontnote sun.security.ssl.SSLContextImpl
|
||||
-dontwarn org.bouncycastle.jsse.**
|
||||
-dontwarn org.conscrypt.**
|
||||
-dontwarn org.openjsse.**
|
||||
-dontwarn org.conscrypt.OpenSSLProvider
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
|
||||
# HTML sanitiser
|
||||
-keep class org.jsoup.safety.Whitelist
|
||||
|
||||
@@ -150,8 +150,7 @@ public class AppModule {
|
||||
//FIXME: StrictMode
|
||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskReads();
|
||||
StrictMode.allowThreadDiskWrites();
|
||||
File dbDir = app.getApplicationContext().getDir("db_sqlite",
|
||||
MODE_PRIVATE);
|
||||
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
|
||||
StrictMode.setThreadPolicy(tp);
|
||||
KeyStrengthener keyStrengthener = SDK_INT >= 23
|
||||
|
||||
@@ -87,8 +87,8 @@ dependencyVerification {
|
||||
'androidx.window:window:1.0.0:window-1.0.0.aar:3212985be4127373ca4d0ea7f8b81a250ae2105e924f7940105d067a0f9ac130',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.almworks.sqlite4java:sqlite4java:1.0.392:sqlite4java-1.0.392.jar:243a64470fda0e86a6fddeb0af4c7aa9426ce84e68cbfe18d75ee5da4b7e0b92',
|
||||
'com.android.tools:desugar_jdk_libs:2.0.3:desugar_jdk_libs-2.0.3.jar:7f4e68385dfe88eba4344ff71912912bdc731806b9b2ce4a12bef9aa6a7d4565',
|
||||
'com.android.tools:desugar_jdk_libs_configuration:2.0.3:desugar_jdk_libs_configuration-2.0.3.jar:9f834a20ca4e1376522afe7bf523d3f1d5e9ad9bb0214b534ee79cc8481b4fc5',
|
||||
'com.android.tools:desugar_jdk_libs_configuration_nio:2.0.3:desugar_jdk_libs_configuration_nio-2.0.3.jar:1e46fd75ae4db46ae592a17afeda3865321492020847c16ec82b1d2026ed967a',
|
||||
'com.android.tools:desugar_jdk_libs_nio:2.0.3:desugar_jdk_libs_nio-2.0.3.jar:7f4e68385dfe88eba4344ff71912912bdc731806b9b2ce4a12bef9aa6a7d4565',
|
||||
'com.github.bumptech.glide:annotations:4.14.2:annotations-4.14.2.jar:8419bf262be70edeb6b9582b386546be66d2e8659c7aae65fd69a9ede02c4877',
|
||||
'com.github.bumptech.glide:compiler:4.14.2:compiler-4.14.2.jar:315b1325283c3d0cf9bc0599c1ecdb85e5f7863b1aa25991b63d616b13930cb6',
|
||||
'com.github.bumptech.glide:gifdecoder:4.14.2:gifdecoder-4.14.2.aar:d021eee1ac1a036fcdc377b6dc3b218f4a0cc2bc2f096d69b474198b635e8302',
|
||||
|
||||
@@ -64,7 +64,7 @@ internal class HeadlessModule(private val appDir: File) {
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideDatabaseConfig(): DatabaseConfig {
|
||||
val dbDir = File(appDir, "db_sqlite")
|
||||
val dbDir = File(appDir, "db")
|
||||
val keyDir = File(appDir, "key")
|
||||
return HeadlessDatabaseConfig(dbDir, keyDir)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ internal class HeadlessTestModule(private val appDir: File) {
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideDatabaseConfig(): DatabaseConfig {
|
||||
val dbDir = File(appDir, "db_sqlite")
|
||||
val dbDir = File(appDir, "db")
|
||||
val keyDir = File(appDir, "key")
|
||||
return HeadlessDatabaseConfig(dbDir, keyDir)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ buildscript {
|
||||
jmock_version = '2.12.0'
|
||||
mockwebserver_version = '4.10.0'
|
||||
onionwrapper_version = '0.0.5'
|
||||
sqlite_jdbc_crypt_version = '3.41.2.1'
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
|
||||
Reference in New Issue
Block a user