mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
WIP: Create indexes on foreign key columns if needed.
This commit is contained in:
@@ -413,6 +413,9 @@ 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
|
||||
|
||||
@@ -4,14 +4,16 @@ 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 counterType, String stringType, String explainCommand) {
|
||||
this.hashType = hashType;
|
||||
this.secretType = secretType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.stringType = stringType;
|
||||
this.explainCommand = explainCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,6 +24,7 @@ class DatabaseTypes {
|
||||
* <li> _BINARY
|
||||
* <li> _COUNTER
|
||||
* <li> _STRING
|
||||
* <li> _EXPLAIN
|
||||
*/
|
||||
String replaceTypes(String s) {
|
||||
s = s.replaceAll("_HASH", hashType);
|
||||
@@ -29,6 +32,7 @@ class DatabaseTypes {
|
||||
s = s.replaceAll("_BINARY", binaryType);
|
||||
s = s.replaceAll("_COUNTER", counterType);
|
||||
s = s.replaceAll("_STRING", stringType);
|
||||
s = s.replaceAll("_EXPLAIN", explainCommand);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,10 @@ class H2Database extends JdbcDatabase {
|
||||
private static final String COUNTER_TYPE =
|
||||
"INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
|
||||
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);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
@@ -74,7 +76,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, key, listener);
|
||||
super.open("org.h2.Driver", reopen, false, key, listener);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory after opening DB:");
|
||||
logFileOrDir(LOG, INFO, dir.getParentFile());
|
||||
|
||||
@@ -41,8 +41,10 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
private static final String COUNTER_TYPE = "INTEGER NOT NULL"
|
||||
+ " PRIMARY KEY 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);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
@@ -70,7 +72,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, key, listener);
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, true, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -359,10 +359,84 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " ON messages (cleanupDeadline)";
|
||||
|
||||
// FIXME: Migration needs to add new index
|
||||
private static final String INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID_KEYSET_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByTransportIdKeysetId"
|
||||
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());
|
||||
|
||||
@@ -398,6 +472,7 @@ 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
|
||||
@@ -424,7 +499,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("db dirty? " + wasDirtyOnInitialisation);
|
||||
}
|
||||
createIndexes(txn);
|
||||
createIndexes(txn, createForeignKeyIndexes);
|
||||
setDirty(txn, true);
|
||||
commitTransaction(txn);
|
||||
} catch (DbException e) {
|
||||
@@ -557,7 +632,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
private void createIndexes(Connection txn) throws DbException {
|
||||
private void createIndexes(Connection txn, boolean createForeignKeyIndexes)
|
||||
throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
@@ -569,7 +645,31 @@ 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_KEYSET_ID);
|
||||
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);
|
||||
@@ -1920,6 +2020,38 @@ 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 {
|
||||
|
||||
@@ -41,8 +41,10 @@ class SqliteDatabase extends JdbcDatabase {
|
||||
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);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
EXPLAIN_COMMAND);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
@@ -71,7 +73,7 @@ class SqliteDatabase 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.sqlite.JDBC", reopen, key, listener);
|
||||
super.open("org.sqlite.JDBC", reopen, true, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
return new HyperSqlDatabase(config, messageFactory ,clock);
|
||||
return new HyperSqlDatabase(config, messageFactory, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testExplainGetMessageIds() {
|
||||
// Ugh, HSQLDB can't handle EXPLAIN PLAN FOR in prepared statements
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2500,6 +2500,21 @@ 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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user