Add denormalised columns to statuses table.

This commit is contained in:
akwizgran
2018-02-02 16:44:35 +00:00
parent 7f8e96a654
commit c7e496230b
5 changed files with 263 additions and 222 deletions

View File

@@ -97,9 +97,12 @@ interface Database<T> {
/**
* Stores a message.
*
* @param sender the contact from whom the message was received, or null
* if the message was created locally.
*/
void addMessage(T txn, Message m, State state, boolean shared)
throws DbException;
void addMessage(T txn, Message m, State state, boolean shared,
@Nullable ContactId sender) throws DbException;
/**
* Adds a dependency between two messages in the given group.
@@ -112,16 +115,6 @@ interface Database<T> {
*/
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
/**
* Initialises the status of the given message with respect to the given
* contact.
*
* @param ack whether the message needs to be acknowledged.
* @param seen whether the contact has seen the message.
*/
void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen)
throws DbException;
/**
* Stores a transport.
*/
@@ -280,7 +273,7 @@ interface Database<T> {
* <p/>
* Read-only.
*/
Collection<ContactId> getGroupVisibility(T txn, GroupId g)
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException;
/**
@@ -584,13 +577,6 @@ interface Database<T> {
*/
void removeMessage(T txn, MessageId m) throws DbException;
/**
* Removes an offered message that was offered by the given contact, or
* returns false if there is no such message.
*/
boolean removeOfferedMessage(T txn, ContactId c, MessageId m)
throws DbException;
/**
* Removes the given offered messages that were offered by the given
* contact.
@@ -598,12 +584,6 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException;
/**
* Removes the status of the given message with respect to the given
* contact.
*/
void removeStatus(T txn, ContactId c, MessageId m) throws DbException;
/**
* Removes a transport (and all associated state) from the database.
*/

View File

@@ -215,7 +215,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException();
if (!db.containsMessage(txn, m.getId())) {
addMessage(txn, m, DELIVERED, shared, null);
db.addMessage(txn, m, DELIVERED, shared, null);
transaction.attach(new MessageAddedEvent(m, null));
transaction.attach(new MessageStateChangedEvent(m.getId(), true,
DELIVERED));
@@ -224,16 +224,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta);
}
private void addMessage(T txn, Message m, State state, boolean shared,
@Nullable ContactId sender) throws DbException {
db.addMessage(txn, m, state, shared);
for (ContactId c : db.getGroupVisibility(txn, m.getGroupId())) {
boolean offered = db.removeOfferedMessage(txn, c, m.getId());
boolean seen = offered || (sender != null && c.equals(sender));
db.addStatus(txn, c, m.getId(), seen, seen);
}
}
@Override
public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException {
@@ -682,7 +672,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.raiseSeenFlag(txn, c, m.getId());
db.raiseAckFlag(txn, c, m.getId());
} else {
addMessage(txn, m, UNKNOWN, false, c);
db.addMessage(txn, m, UNKNOWN, false, c);
transaction.attach(new MessageAddedEvent(m, c));
}
transaction.attach(new MessageToAckEvent(c));
@@ -750,7 +740,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
GroupId id = g.getId();
if (!db.containsGroup(txn, id))
throw new NoSuchGroupException();
Collection<ContactId> affected = db.getGroupVisibility(txn, id);
Collection<ContactId> affected =
db.getGroupVisibility(txn, id).keySet();
db.removeGroup(txn, id);
transaction.attach(new GroupRemovedEvent(g));
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
@@ -820,19 +811,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchGroupException();
Visibility old = db.getGroupVisibility(txn, c, g);
if (old == v) return;
if (old == INVISIBLE) {
db.addGroupVisibility(txn, c, g, v == SHARED);
for (MessageId m : db.getMessageIds(txn, g)) {
boolean seen = db.removeOfferedMessage(txn, c, m);
db.addStatus(txn, c, m, seen, seen);
}
} else if (v == INVISIBLE) {
db.removeGroupVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g))
db.removeStatus(txn, c, m);
} else {
db.setGroupVisibility(txn, c, g, v == SHARED);
}
if (old == INVISIBLE) db.addGroupVisibility(txn, c, g, v == SHARED);
else if (v == INVISIBLE) db.removeGroupVisibility(txn, c, g);
else db.setGroupVisibility(txn, c, g, v == SHARED);
List<ContactId> affected = Collections.singletonList(c);
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
}

View File

@@ -72,7 +72,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
static final int CODE_SCHEMA_VERSION = 31;
static final int CODE_SCHEMA_VERSION = 32;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"
@@ -188,6 +188,13 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE statuses"
+ " (messageId HASH NOT NULL,"
+ " contactId INT NOT NULL,"
+ " groupId HASH NOT NULL," // Denormalised
+ " timestamp BIGINT NOT NULL," // Denormalised
+ " length INT NOT NULL," // Denormalised
+ " state INT NOT NULL," // Denormalised
+ " groupShared BOOLEAN NOT NULL," // Denormalised
+ " messageShared BOOLEAN NOT NULL," // Denormalised
+ " deleted BOOLEAN NOT NULL," // Denormalised
+ " ack BOOLEAN NOT NULL,"
+ " seen BOOLEAN NOT NULL,"
+ " requested BOOLEAN NOT NULL,"
@@ -199,6 +206,9 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (groupId)"
+ " REFERENCES groups (groupId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_TRANSPORTS =
@@ -252,6 +262,14 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState"
+ " ON messageMetadata (groupId, state)";
private static final String INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID =
"CREATE INDEX IF NOT EXISTS statusesByContactIdGroupId"
+ " ON statuses (contactId, groupId)";
private static final String INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP =
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
+ " ON statuses (contactId, timestamp)";
private static final Logger LOG =
Logger.getLogger(JdbcDatabase.class.getName());
@@ -401,6 +419,8 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID);
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
s.close();
} catch (SQLException e) {
tryToClose(s);
@@ -578,7 +598,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override
public void addGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException {
boolean groupShared) throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO groupVisibilities"
@@ -587,16 +607,50 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, g.getBytes());
ps.setBoolean(3, shared);
ps.setBoolean(3, groupShared);
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
// Create a status row for each message in the group
addStatus(txn, c, g, groupShared);
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
private void addStatus(Connection txn, ContactId c, GroupId g,
boolean groupShared) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId, timestamp, state, shared,"
+ " length, raw IS NULL"
+ " FROM messages"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
while (rs.next()) {
MessageId id = new MessageId(rs.getBytes(1));
long timestamp = rs.getLong(2);
State state = State.fromValue(rs.getInt(3));
boolean messageShared = rs.getBoolean(4);
int length = rs.getInt(5);
boolean deleted = rs.getBoolean(6);
boolean seen = removeOfferedMessage(txn, c, id);
addStatus(txn, id, c, g, timestamp, length, state, groupShared,
messageShared, deleted, seen);
}
rs.close();
ps.close();
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
@Override
public void addLocalAuthor(Connection txn, LocalAuthor a)
throws DbException {
@@ -622,7 +676,8 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override
public void addMessage(Connection txn, Message m, State state,
boolean shared) throws DbException {
boolean messageShared, @Nullable ContactId sender)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO messages (messageId, groupId, timestamp,"
@@ -633,13 +688,24 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(2, m.getGroupId().getBytes());
ps.setLong(3, m.getTimestamp());
ps.setInt(4, state.getValue());
ps.setBoolean(5, shared);
ps.setBoolean(5, messageShared);
byte[] raw = m.getRaw();
ps.setInt(6, raw.length);
ps.setBytes(7, raw);
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
// Create a status row for each contact that can see the group
Map<ContactId, Boolean> visibility =
getGroupVisibility(txn, m.getGroupId());
for (Entry<ContactId, Boolean> e : visibility.entrySet()) {
ContactId c = e.getKey();
boolean offered = removeOfferedMessage(txn, c, m.getId());
boolean seen = offered || (sender != null && c.equals(sender));
addStatus(txn, m.getId(), c, m.getGroupId(), m.getTimestamp(),
m.getLength(), state, e.getValue(), messageShared,
false, seen);
}
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -677,19 +743,28 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void addStatus(Connection txn, ContactId c, MessageId m, boolean ack,
boolean seen) throws DbException {
private void addStatus(Connection txn, MessageId m, ContactId c, GroupId g,
long timestamp, int length, State state, boolean groupShared,
boolean messageShared, boolean deleted, boolean seen)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO statuses (messageId, contactId, ack,"
+ " seen, requested, expiry, txCount)"
+ " VALUES (?, ?, ?, ?, FALSE, 0, 0)";
String sql = "INSERT INTO statuses (messageId, contactId, groupId,"
+ " timestamp, length, state, groupShared, messageShared,"
+ " deleted, ack, seen, requested, expiry, txCount)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, 0, 0)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setBoolean(3, ack);
ps.setBoolean(4, seen);
ps.setBytes(3, g.getBytes());
ps.setLong(4, timestamp);
ps.setInt(5, length);
ps.setInt(6, state.getValue());
ps.setBoolean(7, groupShared);
ps.setBoolean(8, messageShared);
ps.setBoolean(9, deleted);
ps.setBoolean(10, seen);
ps.setBoolean(11, seen);
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -941,12 +1016,9 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " WHERE messageId = ?"
+ " AND contactId = ?"
+ " AND m.shared = TRUE";
String sql = "SELECT NULL FROM statuses"
+ " WHERE messageId = ? AND contactId = ?"
+ " AND messageShared = TRUE";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
@@ -998,6 +1070,13 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException();
if (affected > 1) throw new DbStateException();
ps.close();
// Update denormalised column in statuses
sql = "UPDATE statuses SET deleted = TRUE WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -1220,18 +1299,19 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public Collection<ContactId> getGroupVisibility(Connection txn, GroupId g)
public Map<ContactId, Boolean> getGroupVisibility(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId FROM groupVisibilities"
String sql = "SELECT contactId, shared FROM groupVisibilities"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
List<ContactId> visible = new ArrayList<>();
while (rs.next()) visible.add(new ContactId(rs.getInt(1)));
Map<ContactId, Boolean> visible = new HashMap<>();
while (rs.next())
visible.put(new ContactId(rs.getInt(1)), rs.getBoolean(2));
rs.close();
ps.close();
return visible;
@@ -1509,12 +1589,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT m.messageId, txCount > 0, seen"
+ " FROM messages AS m"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE groupId = ?"
+ " AND contactId = ?";
String sql = "SELECT messageId, txCount > 0, seen FROM statuses"
+ " WHERE groupId = ? AND contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
ps.setInt(2, c.getInt());
@@ -1537,15 +1613,13 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public MessageStatus getMessageStatus(Connection txn,
ContactId c, MessageId m) throws DbException {
public MessageStatus getMessageStatus(Connection txn, ContactId c,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT txCount > 0, seen"
+ " FROM statuses"
+ " WHERE messageId = ?"
+ " AND contactId = ?";
String sql = "SELECT txCount > 0, seen FROM statuses"
+ " WHERE messageId = ? AND contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
@@ -1687,14 +1761,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId"
+ " WHERE gv.contactId = ? AND gv.shared = TRUE"
+ " AND state = ? AND m.shared = TRUE AND raw IS NOT NULL"
String sql = "SELECT messageId FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE"
+ " AND seen = FALSE AND requested = FALSE"
+ " AND expiry < ?"
+ " ORDER BY timestamp LIMIT ?";
@@ -1748,14 +1818,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT length, m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId"
+ " WHERE gv.contactId = ? AND gv.shared = TRUE"
+ " AND state = ? AND m.shared = TRUE AND raw IS NOT NULL"
String sql = "SELECT length, messageId FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE"
+ " AND seen = FALSE"
+ " AND expiry < ?"
+ " ORDER BY timestamp";
@@ -1819,8 +1885,8 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public Collection<MessageId> getMessagesToShare(
Connection txn, ClientId c) throws DbException {
public Collection<MessageId> getMessagesToShare(Connection txn, ClientId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -1854,15 +1920,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT expiry FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId"
+ " WHERE gv.contactId = ? AND gv.shared = TRUE"
+ " AND state = ? AND m.shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE"
String sql = "SELECT expiry FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE"
+ " ORDER BY expiry LIMIT 1";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
@@ -1914,14 +1975,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT length, m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId"
+ " WHERE gv.contactId = ? AND gv.shared = TRUE"
+ " AND state = ? AND m.shared = TRUE AND raw IS NOT NULL"
String sql = "SELECT length, messageId FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE"
+ " AND seen = FALSE AND requested = TRUE"
+ " AND expiry < ?"
+ " ORDER BY timestamp";
@@ -2397,6 +2454,8 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
// Remove status rows for the messages in the group
for (MessageId m : getMessageIds(txn, g)) removeStatus(txn, c, m);
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -2436,8 +2495,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public boolean removeOfferedMessage(Connection txn, ContactId c,
private boolean removeOfferedMessage(Connection txn, ContactId c,
MessageId m) throws DbException {
PreparedStatement ps = null;
try {
@@ -2481,16 +2539,15 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void removeStatus(Connection txn, ContactId c, MessageId m)
private void removeStatus(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM statuses"
+ " WHERE contactId = ? AND messageId = ?";
+ " WHERE messageId = ? AND contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, m.getBytes());
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -2586,6 +2643,16 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Update denormalised column in statuses
sql = "UPDATE statuses SET groupShared = ?"
+ " WHERE contactId = ? AND groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBoolean(1, shared);
ps.setInt(2, c.getInt());
ps.setBytes(3, g.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -2604,6 +2671,14 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Update denormalised column in statuses
sql = "UPDATE statuses SET messageShared = TRUE"
+ " WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -2630,6 +2705,14 @@ abstract class JdbcDatabase implements Database<Connection> {
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
// Update denormalised column in statuses
sql = "UPDATE statuses SET state = ? WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, state.getValue());
ps.setBytes(2, m.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);

View File

@@ -176,7 +176,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.emptyList()));
will(returnValue(Collections.emptyMap()));
oneOf(database).removeGroup(txn, groupId);
oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class)));
oneOf(eventBus).broadcast(with(any(
@@ -255,13 +255,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).addMessage(txn, message, DELIVERED, true, null);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
oneOf(database).commitTransaction(txn);
// The message was added, so the listeners should be called
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
@@ -1042,12 +1037,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(VISIBLE));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).addMessage(txn, message, UNKNOWN, false);
oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, true, true);
oneOf(database).addMessage(txn, message, UNKNOWN, false, contactId);
// Second time
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
@@ -1206,7 +1196,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
@Test
public void testChangingVisibilityCallsListeners() throws Exception {
public void testChangingVisibilityFromInvisibleToVisibleCallsListeners()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
@@ -1217,11 +1208,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(INVISIBLE)); // Not yet visible
oneOf(database).addGroupVisibility(txn, contactId, groupId, false);
oneOf(database).getMessageIds(txn, groupId);
will(returnValue(Collections.singletonList(messageId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(
GroupVisibilityUpdatedEvent.class)));
@@ -1238,6 +1224,35 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
}
@Test
public void testChangingVisibilityFromVisibleToInvisibleCallsListeners()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(VISIBLE)); // Not yet visible
oneOf(database).removeGroupVisibility(txn, contactId, groupId);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(
GroupVisibilityUpdatedEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction(false);
try {
db.setGroupVisibility(transaction, contactId, groupId, INVISIBLE);
db.commitTransaction(transaction);
} finally {
db.endTransaction(transaction);
}
}
@Test
public void testNotChangingVisibilityDoesNotCallListeners()
throws Exception {
@@ -1476,13 +1491,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).addMessage(txn, message, DELIVERED, true, null);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
// addMessageDependencies()
oneOf(database).containsMessage(txn, messageId);
will(returnValue(true));

View File

@@ -122,7 +122,7 @@ public class H2DatabaseTest extends BrambleTestCase {
db.addGroup(txn, group);
assertTrue(db.containsGroup(txn, groupId));
assertFalse(db.containsMessage(txn, messageId));
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
assertTrue(db.containsMessage(txn, messageId));
db.commitTransaction(txn);
db.close();
@@ -160,7 +160,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
// Removing the group should remove the message
assertTrue(db.containsMessage(txn, messageId));
@@ -182,18 +182,11 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
// The message has no status yet, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Adding a status with seen = false should make the message sendable
db.addStatus(txn, contactId, messageId, false, false);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
// The contact has not seen the message, so it should be sendable
Collection<MessageId> ids =
db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertEquals(Collections.singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100);
assertEquals(Collections.singletonList(messageId), ids);
@@ -220,8 +213,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, UNKNOWN, true, null);
// The message has not been validated, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -265,8 +257,7 @@ public class H2DatabaseTest extends BrambleTestCase {
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true));
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// The group is invisible, so the message should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -318,8 +309,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, false, null);
// The message is not shared, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -350,8 +340,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// The message is sendable, but too large to send
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -381,12 +370,8 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add some messages to ack
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new Message(messageId1, groupId, timestamp, raw);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, true);
db.raiseAckFlag(txn, contactId, messageId);
db.addMessage(txn, message1, DELIVERED, true);
db.addStatus(txn, contactId, messageId1, false, true);
db.raiseAckFlag(txn, contactId, messageId1);
db.addMessage(txn, message, DELIVERED, true, contactId);
db.addMessage(txn, message1, DELIVERED, true, contactId);
// Both message IDs should be returned
Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
@@ -399,6 +384,14 @@ public class H2DatabaseTest extends BrambleTestCase {
assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
contactId, 1234));
// Raise the ack flag again
db.raiseAckFlag(txn, contactId, messageId);
db.raiseAckFlag(txn, contactId, messageId1);
// Both message IDs should be returned
ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(Arrays.asList(messageId, messageId1), ids);
db.commitTransaction(txn);
db.close();
}
@@ -414,8 +407,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// Retrieve the message from the database and mark it as sent
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -456,7 +448,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Storing a message should reduce the free space
Connection txn = db.startTransaction();
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
db.commitTransaction(txn);
assertTrue(db.getFreeSpace() < free);
@@ -604,15 +596,14 @@ public class H2DatabaseTest extends BrambleTestCase {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, a group and a message
// Add a contact, an invisible group and a message
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true));
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// The group is not visible
// The group is not visible so the message should not be visible
assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
db.commitTransaction(txn);
@@ -632,31 +623,31 @@ public class H2DatabaseTest extends BrambleTestCase {
// The group should not be visible to the contact
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(),
assertEquals(Collections.emptyMap(),
db.getGroupVisibility(txn, groupId));
// Make the group visible to the contact
db.addGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId),
assertEquals(Collections.singletonMap(contactId, false),
db.getGroupVisibility(txn, groupId));
// Share the group with the contact
db.setGroupVisibility(txn, contactId, groupId, true);
assertEquals(SHARED, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId),
assertEquals(Collections.singletonMap(contactId, true),
db.getGroupVisibility(txn, groupId));
// Unshare the group with the contact
db.setGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId),
assertEquals(Collections.singletonMap(contactId, false),
db.getGroupVisibility(txn, groupId));
// Make the group invisible again
db.removeGroupVisibility(txn, contactId, groupId);
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(),
assertEquals(Collections.emptyMap(),
db.getGroupVisibility(txn, groupId));
db.commitTransaction(txn);
@@ -876,8 +867,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Remove some of the offered messages and count again
List<MessageId> half = ids.subList(0, 5);
db.removeOfferedMessages(txn, contactId, half);
assertTrue(db.removeOfferedMessage(txn, contactId, ids.get(5)));
assertEquals(4, db.countOfferedMessages(txn, contactId));
assertEquals(5, db.countOfferedMessages(txn, contactId));
db.commitTransaction(txn);
db.close();
@@ -928,7 +918,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message
Metadata metadata = new Metadata();
@@ -999,7 +989,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message
Metadata metadata = new Metadata();
@@ -1060,8 +1050,8 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and two messages
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message1, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages
Metadata metadata = new Metadata();
@@ -1164,8 +1154,8 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and two messages
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message1, DELIVERED, true);
db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages
Metadata metadata = new Metadata();
@@ -1239,9 +1229,9 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and some messages
db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true);
db.addMessage(txn, message1, DELIVERED, true);
db.addMessage(txn, message2, INVALID, true);
db.addMessage(txn, message, PENDING, true, contactId);
db.addMessage(txn, message1, DELIVERED, true, contactId);
db.addMessage(txn, message2, INVALID, true, contactId);
// Add dependencies
db.addMessageDependency(txn, groupId, messageId, messageId1);
@@ -1308,7 +1298,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true);
db.addMessage(txn, message, PENDING, true, contactId);
// Add a second group
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
@@ -1319,7 +1309,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a message to the second group
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new Message(messageId1, groupId1, timestamp, raw);
db.addMessage(txn, message1, DELIVERED, true);
db.addMessage(txn, message1, DELIVERED, true, contactId);
// Create an ID for a missing message
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
@@ -1327,7 +1317,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add another message to the first group
MessageId messageId3 = new MessageId(TestUtils.getRandomId());
Message message3 = new Message(messageId3, groupId, timestamp, raw);
db.addMessage(txn, message3, DELIVERED, true);
db.addMessage(txn, message3, DELIVERED, true, contactId);
// Add dependencies between the messages
db.addMessageDependency(txn, groupId, messageId, messageId1);
@@ -1374,10 +1364,10 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and some messages with different states
db.addGroup(txn, group);
db.addMessage(txn, m1, UNKNOWN, true);
db.addMessage(txn, m2, INVALID, true);
db.addMessage(txn, m3, PENDING, true);
db.addMessage(txn, m4, DELIVERED, true);
db.addMessage(txn, m1, UNKNOWN, true, contactId);
db.addMessage(txn, m2, INVALID, true, contactId);
db.addMessage(txn, m3, PENDING, true, contactId);
db.addMessage(txn, m4, DELIVERED, true, contactId);
Collection<MessageId> result;
@@ -1411,10 +1401,10 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and some messages
db.addGroup(txn, group);
db.addMessage(txn, m1, DELIVERED, true);
db.addMessage(txn, m2, DELIVERED, false);
db.addMessage(txn, m3, DELIVERED, false);
db.addMessage(txn, m4, DELIVERED, true);
db.addMessage(txn, m1, DELIVERED, true, contactId);
db.addMessage(txn, m2, DELIVERED, false, contactId);
db.addMessage(txn, m3, DELIVERED, false, contactId);
db.addMessage(txn, m4, DELIVERED, true, contactId);
// Introduce dependencies between the messages
db.addMessageDependency(txn, groupId, mId1, mId2);
@@ -1443,8 +1433,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// The message should not be sent or seen
MessageStatus status = db.getMessageStatus(txn, contactId, messageId);
@@ -1546,8 +1535,7 @@ public class H2DatabaseTest extends BrambleTestCase {
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
db.addMessage(txn, message, DELIVERED, true, null);
// The message should be visible to the contact
assertTrue(db.containsVisibleMessage(txn, contactId, messageId));
@@ -1621,7 +1609,7 @@ public class H2DatabaseTest extends BrambleTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false);
db.addMessage(txn, message, UNKNOWN, false, contactId);
// Walk the message through the validation and delivery states
assertEquals(UNKNOWN, db.getMessageState(txn, messageId));
@@ -1647,14 +1635,13 @@ public class H2DatabaseTest extends BrambleTestCase {
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false);
db.addMessage(txn, message, UNKNOWN, false, null);
// There should be no messages to send
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
// Share the group with the contact - still no messages to send
db.addGroupVisibility(txn, contactId, groupId, true);
db.addStatus(txn, contactId, messageId, false, false);
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
// Set the message's state to DELIVERED - still no messages to send