diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index c09508ad2..5363496f6 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -383,8 +383,24 @@ abstract class JdbcDatabase implements Database { public void addBatchToAck(Connection txn, ContactId c, BatchId b) throws DbException { PreparedStatement ps = null; + ResultSet rs = null; try { - String sql = "INSERT INTO batchesToAck (batchId, contactId)" + String sql = "SELECT COUNT(batchId) FROM batchesToAck" + + " WHERE batchId = ? AND contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, b.getBytes()); + ps.setInt(2, c.getInt()); + rs = ps.executeQuery(); + boolean found = rs.next(); + assert found; + int count = rs.getInt(1); + assert count <= 1; + boolean more = rs.next(); + assert !more; + rs.close(); + ps.close(); + if(count == 1) return; + sql = "INSERT INTO batchesToAck (batchId, contactId)" + " VALUES (?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, b.getBytes()); @@ -393,6 +409,7 @@ abstract class JdbcDatabase implements Database { assert rowsAffected == 1; ps.close(); } catch(SQLException e) { + tryToClose(rs); tryToClose(ps); tryToClose(txn); throw new DbException(e); @@ -1249,7 +1266,8 @@ abstract class JdbcDatabase implements Database { ps.setInt(5, 1); rs = ps.executeQuery(); boolean found = rs.next(); - assert !rs.next(); + boolean more = rs.next(); + assert !more; rs.close(); ps.close(); return found; diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index c66258ad9..9f56e9303 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -404,6 +404,33 @@ public class H2DatabaseTest extends TestCase { db.close(); } + @Test + public void testDuplicateBatchesReceived() throws DbException { + Database db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and receive the same batch twice + assertEquals(contactId, db.addContact(txn, null)); + db.addBatchToAck(txn, contactId, batchId); + db.addBatchToAck(txn, contactId, batchId); + db.commitTransaction(txn); + + // The batch ID should only be returned once + Collection acks = db.getBatchesToAck(txn, contactId); + assertEquals(1, acks.size()); + assertTrue(acks.contains(batchId)); + + // Remove the batch ID + db.removeBatchesToAck(txn, contactId, acks); + + // The batch ID should have been removed + acks = db.getBatchesToAck(txn, contactId); + assertEquals(0, acks.size()); + + db.commitTransaction(txn); + db.close(); + } + @Test public void testRemoveAckedBatch() throws DbException { Database db = open(false);