Add denormalised columns to statuses table.

This commit is contained in:
akwizgran
2018-02-02 16:44:35 +00:00
parent aa07d0cadd
commit 7fe502e3cc
6 changed files with 271 additions and 223 deletions

View File

@@ -96,9 +96,12 @@ interface Database<T> {
/** /**
* Stores a message. * 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) void addMessage(T txn, Message m, State state, boolean shared,
throws DbException; @Nullable ContactId sender) throws DbException;
/** /**
* Adds a dependency between two messages in the given group. * Adds a dependency between two messages in the given group.
@@ -111,16 +114,6 @@ interface Database<T> {
*/ */
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; 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. * Stores a transport.
*/ */
@@ -279,7 +272,7 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<ContactId> getGroupVisibility(T txn, GroupId g) Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException; throws DbException;
/** /**
@@ -573,13 +566,6 @@ interface Database<T> {
*/ */
void removeMessage(T txn, MessageId m) throws DbException; 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 * Removes the given offered messages that were offered by the given
* contact. * contact.
@@ -587,12 +573,6 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c, void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException; 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. * Removes a transport (and all associated state) from the database.
*/ */

View File

@@ -213,7 +213,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsGroup(txn, m.getGroupId())) if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
if (!db.containsMessage(txn, m.getId())) { 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 MessageAddedEvent(m, null));
transaction.attach(new MessageStateChangedEvent(m.getId(), true, transaction.attach(new MessageStateChangedEvent(m.getId(), true,
DELIVERED)); DELIVERED));
@@ -222,16 +222,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta); 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 @Override
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { int maxLatency) throws DbException {
@@ -673,7 +663,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.raiseSeenFlag(txn, c, m.getId()); db.raiseSeenFlag(txn, c, m.getId());
db.raiseAckFlag(txn, c, m.getId()); db.raiseAckFlag(txn, c, m.getId());
} else { } else {
addMessage(txn, m, UNKNOWN, false, c); db.addMessage(txn, m, UNKNOWN, false, c);
transaction.attach(new MessageAddedEvent(m, c)); transaction.attach(new MessageAddedEvent(m, c));
} }
transaction.attach(new MessageToAckEvent(c)); transaction.attach(new MessageToAckEvent(c));
@@ -741,7 +731,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
GroupId id = g.getId(); GroupId id = g.getId();
if (!db.containsGroup(txn, id)) if (!db.containsGroup(txn, id))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
Collection<ContactId> affected = db.getGroupVisibility(txn, id); Collection<ContactId> affected =
db.getGroupVisibility(txn, id).keySet();
db.removeGroup(txn, id); db.removeGroup(txn, id);
transaction.attach(new GroupRemovedEvent(g)); transaction.attach(new GroupRemovedEvent(g));
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
@@ -811,19 +802,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchGroupException(); throw new NoSuchGroupException();
Visibility old = db.getGroupVisibility(txn, c, g); Visibility old = db.getGroupVisibility(txn, c, g);
if (old == v) return; if (old == v) return;
if (old == INVISIBLE) { if (old == INVISIBLE) db.addGroupVisibility(txn, c, g, v == SHARED);
db.addGroupVisibility(txn, c, g, v == SHARED); else if (v == INVISIBLE) db.removeGroupVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g)) { else db.setGroupVisibility(txn, c, g, v == SHARED);
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);
}
List<ContactId> affected = Collections.singletonList(c); List<ContactId> affected = Collections.singletonList(c);
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
} }

View File

@@ -71,7 +71,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 34; static final int CODE_SCHEMA_VERSION = 35;
private static final String CREATE_SETTINGS = private static final String CREATE_SETTINGS =
"CREATE TABLE settings" "CREATE TABLE settings"
@@ -189,6 +189,13 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE statuses" "CREATE TABLE statuses"
+ " (messageId _HASH NOT NULL," + " (messageId _HASH NOT NULL,"
+ " contactId INT 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," + " ack BOOLEAN NOT NULL,"
+ " seen BOOLEAN NOT NULL," + " seen BOOLEAN NOT NULL,"
+ " requested BOOLEAN NOT NULL," + " requested BOOLEAN NOT NULL,"
@@ -200,6 +207,9 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " ON DELETE CASCADE," + " ON DELETE CASCADE,"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)" + " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (groupId)"
+ " REFERENCES groups (groupId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_TRANSPORTS = private static final String CREATE_TRANSPORTS =
@@ -253,6 +263,14 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState" "CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState"
+ " ON messageMetadata (groupId, state)"; + " 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 = private static final Logger LOG =
Logger.getLogger(JdbcDatabase.class.getName()); 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_CONTACTS_BY_AUTHOR_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID); s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID);
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE); 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(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s);
@@ -581,7 +601,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void addGroupVisibility(Connection txn, ContactId c, GroupId g, public void addGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException { boolean groupShared) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO groupVisibilities" String sql = "INSERT INTO groupVisibilities"
@@ -590,16 +610,50 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setBytes(2, g.getBytes()); ps.setBytes(2, g.getBytes());
ps.setBoolean(3, shared); ps.setBoolean(3, groupShared);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
// Create a status row for each message in the group
addStatus(txn, c, g, groupShared);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); 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 @Override
public void addLocalAuthor(Connection txn, LocalAuthor a) public void addLocalAuthor(Connection txn, LocalAuthor a)
throws DbException { throws DbException {
@@ -627,7 +681,8 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void addMessage(Connection txn, Message m, State state, public void addMessage(Connection txn, Message m, State state,
boolean shared) throws DbException { boolean messageShared, @Nullable ContactId sender)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO messages (messageId, groupId, timestamp," String sql = "INSERT INTO messages (messageId, groupId, timestamp,"
@@ -638,13 +693,24 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(2, m.getGroupId().getBytes()); ps.setBytes(2, m.getGroupId().getBytes());
ps.setLong(3, m.getTimestamp()); ps.setLong(3, m.getTimestamp());
ps.setInt(4, state.getValue()); ps.setInt(4, state.getValue());
ps.setBoolean(5, shared); ps.setBoolean(5, messageShared);
byte[] raw = m.getRaw(); byte[] raw = m.getRaw();
ps.setInt(6, raw.length); ps.setInt(6, raw.length);
ps.setBytes(7, raw); ps.setBytes(7, raw);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); 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) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -682,19 +748,28 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override private void addStatus(Connection txn, MessageId m, ContactId c, GroupId g,
public void addStatus(Connection txn, ContactId c, MessageId m, boolean ack, long timestamp, int length, State state, boolean groupShared,
boolean seen) throws DbException { boolean messageShared, boolean deleted, boolean seen)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO statuses (messageId, contactId, ack," String sql = "INSERT INTO statuses (messageId, contactId, groupId,"
+ " seen, requested, expiry, txCount)" + " timestamp, length, state, groupShared, messageShared,"
+ " VALUES (?, ?, ?, ?, FALSE, 0, 0)"; + " deleted, ack, seen, requested, expiry, txCount)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, 0, 0)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
ps.setBoolean(3, ack); ps.setBytes(3, g.getBytes());
ps.setBoolean(4, seen); 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(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -946,12 +1021,9 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT NULL FROM messages AS m" String sql = "SELECT NULL FROM statuses"
+ " JOIN groupVisibilities AS gv" + " WHERE messageId = ? AND contactId = ?"
+ " ON m.groupId = gv.groupId" + " AND messageShared = TRUE";
+ " WHERE messageId = ?"
+ " AND contactId = ?"
+ " AND m.shared = TRUE";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
@@ -1003,6 +1075,13 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
if (affected > 1) throw new DbStateException(); if (affected > 1) throw new DbStateException();
ps.close(); 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) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -1231,18 +1310,19 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public Collection<ContactId> getGroupVisibility(Connection txn, GroupId g) public Map<ContactId, Boolean> getGroupVisibility(Connection txn, GroupId g)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId FROM groupVisibilities" String sql = "SELECT contactId, shared FROM groupVisibilities"
+ " WHERE groupId = ?"; + " WHERE groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<ContactId> visible = new ArrayList<>(); Map<ContactId, Boolean> visible = new HashMap<>();
while (rs.next()) visible.add(new ContactId(rs.getInt(1))); while (rs.next())
visible.put(new ContactId(rs.getInt(1)), rs.getBoolean(2));
rs.close(); rs.close();
ps.close(); ps.close();
return visible; return visible;
@@ -1524,12 +1604,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT m.messageId, txCount > 0, seen" String sql = "SELECT messageId, txCount > 0, seen FROM statuses"
+ " FROM messages AS m" + " WHERE groupId = ? AND contactId = ?";
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE groupId = ?"
+ " AND contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
@@ -1552,15 +1628,13 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public MessageStatus getMessageStatus(Connection txn, public MessageStatus getMessageStatus(Connection txn, ContactId c,
ContactId c, MessageId m) throws DbException { MessageId m) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT txCount > 0, seen" String sql = "SELECT txCount > 0, seen FROM statuses"
+ " FROM statuses" + " WHERE messageId = ? AND contactId = ?";
+ " WHERE messageId = ?"
+ " AND contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
@@ -1702,14 +1776,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT m.messageId FROM messages AS m" String sql = "SELECT messageId FROM statuses"
+ " JOIN groupVisibilities AS gv" + " WHERE contactId = ? AND state = ?"
+ " ON m.groupId = gv.groupId" + " AND groupShared = TRUE AND messageShared = TRUE"
+ " JOIN statuses AS s" + " AND deleted = FALSE"
+ " 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 AND requested = FALSE" + " AND seen = FALSE AND requested = FALSE"
+ " AND expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp LIMIT ?"; + " ORDER BY timestamp LIMIT ?";
@@ -1763,14 +1833,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT length, m.messageId FROM messages AS m" String sql = "SELECT length, messageId FROM statuses"
+ " JOIN groupVisibilities AS gv" + " WHERE contactId = ? AND state = ?"
+ " ON m.groupId = gv.groupId" + " AND groupShared = TRUE AND messageShared = TRUE"
+ " JOIN statuses AS s" + " AND deleted = FALSE"
+ " 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" + " AND seen = FALSE"
+ " AND expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp"; + " ORDER BY timestamp";
@@ -1834,8 +1900,8 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public Collection<MessageId> getMessagesToShare( public Collection<MessageId> getMessagesToShare(Connection txn, ClientId c)
Connection txn, ClientId c) throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
@@ -1894,14 +1960,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT length, m.messageId FROM messages AS m" String sql = "SELECT length, messageId FROM statuses"
+ " JOIN groupVisibilities AS gv" + " WHERE contactId = ? AND state = ?"
+ " ON m.groupId = gv.groupId" + " AND groupShared = TRUE AND messageShared = TRUE"
+ " JOIN statuses AS s" + " AND deleted = FALSE"
+ " 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 AND requested = TRUE" + " AND seen = FALSE AND requested = TRUE"
+ " AND expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp"; + " ORDER BY timestamp";
@@ -2380,6 +2442,8 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
// Remove status rows for the messages in the group
for (MessageId m : getMessageIds(txn, g)) removeStatus(txn, c, m);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -2419,8 +2483,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override private boolean removeOfferedMessage(Connection txn, ContactId c,
public boolean removeOfferedMessage(Connection txn, ContactId c,
MessageId m) throws DbException { MessageId m) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
@@ -2464,16 +2527,15 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override private void removeStatus(Connection txn, ContactId c, MessageId m)
public void removeStatus(Connection txn, ContactId c, MessageId m)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "DELETE FROM statuses" String sql = "DELETE FROM statuses"
+ " WHERE contactId = ? AND messageId = ?"; + " WHERE messageId = ? AND contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setBytes(1, m.getBytes());
ps.setBytes(2, m.getBytes()); ps.setInt(2, c.getInt());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -2569,6 +2631,16 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); 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) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -2587,6 +2659,14 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); 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) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -2613,6 +2693,14 @@ abstract class JdbcDatabase implements Database<Connection> {
affected = ps.executeUpdate(); affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); 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) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);

View File

@@ -174,7 +174,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).getGroupVisibility(txn, groupId); oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.emptyList())); will(returnValue(Collections.emptyMap()));
oneOf(database).removeGroup(txn, groupId); oneOf(database).removeGroup(txn, groupId);
oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class))); oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class)));
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
@@ -252,13 +252,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); 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).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); oneOf(database).commitTransaction(txn);
// The message was added, so the listeners should be called // The message was added, so the listeners should be called
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
@@ -1041,12 +1036,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(VISIBLE)); will(returnValue(VISIBLE));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, UNKNOWN, false); oneOf(database).addMessage(txn, message, UNKNOWN, false, contactId);
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);
// Second time // Second time
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
@@ -1205,7 +1195,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testChangingVisibilityCallsListeners() throws Exception { public void testChangingVisibilityFromInvisibleToVisibleCallsListeners()
throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1216,11 +1207,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).getGroupVisibility(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(INVISIBLE)); // Not yet visible will(returnValue(INVISIBLE)); // Not yet visible
oneOf(database).addGroupVisibility(txn, contactId, groupId, false); 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(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
GroupVisibilityUpdatedEvent.class))); GroupVisibilityUpdatedEvent.class)));
@@ -1237,6 +1223,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 @Test
public void testNotChangingVisibilityDoesNotCallListeners() public void testNotChangingVisibilityDoesNotCallListeners()
throws Exception { throws Exception {
@@ -1476,13 +1491,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true); oneOf(database).addMessage(txn, message, DELIVERED, true, null);
oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
// addMessageDependencies() // addMessageDependencies()
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(true)); will(returnValue(true));

View File

@@ -563,9 +563,9 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
Message m = getMessage(g.getId()); Message m = getMessage(g.getId());
messages.add(m); messages.add(m);
State state = State.fromValue(random.nextInt(4)); State state = State.fromValue(random.nextInt(4));
db.addMessage(txn, m, state, random.nextBoolean()); boolean shared = random.nextBoolean();
db.addStatus(txn, c, m.getId(), random.nextBoolean(), ContactId sender = random.nextBoolean() ? c : null;
random.nextBoolean()); db.addMessage(txn, m, state, shared, sender);
if (random.nextBoolean()) if (random.nextBoolean())
db.raiseRequestedFlag(txn, c, m.getId()); db.raiseRequestedFlag(txn, c, m.getId());
Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE); Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE);
@@ -593,7 +593,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
for (int j = 0; j < MESSAGES_PER_GROUP; j++) { for (int j = 0; j < MESSAGES_PER_GROUP; j++) {
Message m = getMessage(g.getId()); Message m = getMessage(g.getId());
messages.add(m); messages.add(m);
db.addMessage(txn, m, DELIVERED, false); db.addMessage(txn, m, DELIVERED, false, null);
Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE); Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE);
messageMeta.get(g.getId()).add(mm); messageMeta.get(g.getId()).add(mm);
db.mergeMessageMetadata(txn, m.getId(), mm); db.mergeMessageMetadata(txn, m.getId(), mm);

View File

@@ -57,6 +57,7 @@ import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -124,7 +125,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addGroup(txn, group); db.addGroup(txn, group);
assertTrue(db.containsGroup(txn, groupId)); assertTrue(db.containsGroup(txn, groupId));
assertFalse(db.containsMessage(txn, messageId)); assertFalse(db.containsMessage(txn, messageId));
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -162,7 +163,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
// Removing the group should remove the message // Removing the group should remove the message
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
@@ -184,18 +185,11 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); 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 // The contact has not seen the message, so it should be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids =
ONE_MEGABYTE); 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);
assertEquals(Collections.singletonList(messageId), ids); assertEquals(Collections.singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100); ids = db.getMessagesToOffer(txn, contactId, 100);
assertEquals(Collections.singletonList(messageId), ids); assertEquals(Collections.singletonList(messageId), ids);
@@ -222,8 +216,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true); db.addMessage(txn, message, UNKNOWN, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The message has not been validated, so it should not be sendable // The message has not been validated, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -267,8 +260,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The group is invisible, so the message should not be sendable // The group is invisible, so the message should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -320,8 +312,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false); db.addMessage(txn, message, DELIVERED, false, null);
db.addStatus(txn, contactId, messageId, false, false);
// The message is not shared, so it should not be sendable // The message is not shared, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -352,8 +343,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The message is sendable, but too large to send // The message is sendable, but too large to send
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -383,12 +373,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add some messages to ack // Add some messages to ack
MessageId messageId1 = new MessageId(getRandomId()); MessageId messageId1 = new MessageId(getRandomId());
Message message1 = new Message(messageId1, groupId, timestamp, raw); Message message1 = new Message(messageId1, groupId, timestamp, raw);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, contactId);
db.addStatus(txn, contactId, messageId, false, true); db.addMessage(txn, message1, DELIVERED, true, contactId);
db.raiseAckFlag(txn, contactId, messageId);
db.addMessage(txn, message1, DELIVERED, true);
db.addStatus(txn, contactId, messageId1, false, true);
db.raiseAckFlag(txn, contactId, messageId1);
// Both message IDs should be returned // Both message IDs should be returned
Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234); Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
@@ -401,6 +387,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(Collections.emptyList(), db.getMessagesToAck(txn, assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
contactId, 1234)); 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.commitTransaction(txn);
db.close(); db.close();
} }
@@ -416,8 +410,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// Retrieve the message from the database and mark it as sent // Retrieve the message from the database and mark it as sent
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -458,7 +451,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Storing a message should reduce the free space // Storing a message should reduce the free space
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.commitTransaction(txn); db.commitTransaction(txn);
assertTrue(db.getFreeSpace() < free); assertTrue(db.getFreeSpace() < free);
@@ -606,15 +599,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); 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); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The group is not visible // The group is not visible so the message should not be visible
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -634,31 +626,31 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// The group should not be visible to the contact // The group should not be visible to the contact
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId)); assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(), assertEquals(Collections.emptyMap(),
db.getGroupVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
// Make the group visible to the contact // Make the group visible to the contact
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId)); assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId), assertEquals(Collections.singletonMap(contactId, false),
db.getGroupVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
// Share the group with the contact // Share the group with the contact
db.setGroupVisibility(txn, contactId, groupId, true); db.setGroupVisibility(txn, contactId, groupId, true);
assertEquals(SHARED, db.getGroupVisibility(txn, contactId, groupId)); assertEquals(SHARED, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId), assertEquals(Collections.singletonMap(contactId, true),
db.getGroupVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
// Unshare the group with the contact // Unshare the group with the contact
db.setGroupVisibility(txn, contactId, groupId, false); db.setGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId)); assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId), assertEquals(Collections.singletonMap(contactId, false),
db.getGroupVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
// Make the group invisible again // Make the group invisible again
db.removeGroupVisibility(txn, contactId, groupId); db.removeGroupVisibility(txn, contactId, groupId);
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId)); assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(), assertEquals(Collections.emptyMap(),
db.getGroupVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -879,8 +871,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Remove some of the offered messages and count again // Remove some of the offered messages and count again
List<MessageId> half = ids.subList(0, 5); List<MessageId> half = ids.subList(0, 5);
db.removeOfferedMessages(txn, contactId, half); db.removeOfferedMessages(txn, contactId, half);
assertTrue(db.removeOfferedMessage(txn, contactId, ids.get(5))); assertEquals(5, db.countOfferedMessages(txn, contactId));
assertEquals(4, db.countOfferedMessages(txn, contactId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -931,7 +922,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message // Attach some metadata to the message
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1002,7 +993,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message // Attach some metadata to the message
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1063,8 +1054,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and two messages // Add a group and two messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true); db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages // Attach some metadata to the messages
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1167,8 +1158,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and two messages // Add a group and two messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true); db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages // Attach some metadata to the messages
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1242,9 +1233,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages // Add a group and some messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true); db.addMessage(txn, message, PENDING, true, contactId);
db.addMessage(txn, message1, DELIVERED, true); db.addMessage(txn, message1, DELIVERED, true, contactId);
db.addMessage(txn, message2, INVALID, true); db.addMessage(txn, message2, INVALID, true, contactId);
// Add dependencies // Add dependencies
db.addMessageDependency(txn, groupId, messageId, messageId1); db.addMessageDependency(txn, groupId, messageId, messageId1);
@@ -1311,7 +1302,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true); db.addMessage(txn, message, PENDING, true, contactId);
// Add a second group // Add a second group
GroupId groupId1 = new GroupId(getRandomId()); GroupId groupId1 = new GroupId(getRandomId());
@@ -1322,7 +1313,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a message to the second group // Add a message to the second group
MessageId messageId1 = new MessageId(getRandomId()); MessageId messageId1 = new MessageId(getRandomId());
Message message1 = new Message(messageId1, groupId1, timestamp, raw); 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 // Create an ID for a missing message
MessageId messageId2 = new MessageId(getRandomId()); MessageId messageId2 = new MessageId(getRandomId());
@@ -1330,7 +1321,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add another message to the first group // Add another message to the first group
MessageId messageId3 = new MessageId(getRandomId()); MessageId messageId3 = new MessageId(getRandomId());
Message message3 = new Message(messageId3, groupId, timestamp, raw); 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 // Add dependencies between the messages
db.addMessageDependency(txn, groupId, messageId, messageId1); db.addMessageDependency(txn, groupId, messageId, messageId1);
@@ -1377,10 +1368,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages with different states // Add a group and some messages with different states
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, m1, UNKNOWN, true); db.addMessage(txn, m1, UNKNOWN, true, contactId);
db.addMessage(txn, m2, INVALID, true); db.addMessage(txn, m2, INVALID, true, contactId);
db.addMessage(txn, m3, PENDING, true); db.addMessage(txn, m3, PENDING, true, contactId);
db.addMessage(txn, m4, DELIVERED, true); db.addMessage(txn, m4, DELIVERED, true, contactId);
Collection<MessageId> result; Collection<MessageId> result;
@@ -1414,10 +1405,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages // Add a group and some messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, m1, DELIVERED, true); db.addMessage(txn, m1, DELIVERED, true, contactId);
db.addMessage(txn, m2, DELIVERED, false); db.addMessage(txn, m2, DELIVERED, false, contactId);
db.addMessage(txn, m3, DELIVERED, false); db.addMessage(txn, m3, DELIVERED, false, contactId);
db.addMessage(txn, m4, DELIVERED, true); db.addMessage(txn, m4, DELIVERED, true, contactId);
// Introduce dependencies between the messages // Introduce dependencies between the messages
db.addMessageDependency(txn, groupId, mId1, mId2); db.addMessageDependency(txn, groupId, mId1, mId2);
@@ -1446,8 +1437,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The message should not be sent or seen // The message should not be sent or seen
MessageStatus status = db.getMessageStatus(txn, contactId, messageId); MessageStatus status = db.getMessageStatus(txn, contactId, messageId);
@@ -1547,8 +1537,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true, null);
db.addStatus(txn, contactId, messageId, false, false);
// The message should be visible to the contact // The message should be visible to the contact
assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); assertTrue(db.containsVisibleMessage(txn, contactId, messageId));
@@ -1622,7 +1611,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); 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 // Walk the message through the validation and delivery states
assertEquals(UNKNOWN, db.getMessageState(txn, messageId)); assertEquals(UNKNOWN, db.getMessageState(txn, messageId));
@@ -1662,20 +1651,20 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
} }
private TransportKeys createTransportKeys() { private TransportKeys createTransportKeys() {
SecretKey inPrevTagKey = TestUtils.getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = TestUtils.getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
1, 123, new byte[4]); 1, 123, new byte[4]);
SecretKey inCurrTagKey = TestUtils.getSecretKey(); SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = TestUtils.getSecretKey(); SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey, IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
2, 234, new byte[4]); 2, 234, new byte[4]);
SecretKey inNextTagKey = TestUtils.getSecretKey(); SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = TestUtils.getSecretKey(); SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey, IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
3, 345, new byte[4]); 3, 345, new byte[4]);
SecretKey outCurrTagKey = TestUtils.getSecretKey(); SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = TestUtils.getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456); 2, 456);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);