diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 7065e02332376062b6edba2dbc4854f540fb8d59..f43a98cde8019f4edafc83bcb659d99604dd6732 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -191,11 +191,13 @@ interface Database<T> {
 	long getFreeSpace() throws DbException;
 
 	/**
-	 * Returns the group that contains the given message.
+	 * Returns the parent of the given group message, or null if either the
+	 * message has no parent, or the parent is absent from the database, or the
+	 * parent belongs to a different group.
 	 * <p>
 	 * Locking: messages read.
 	 */
-	GroupId getGroup(T txn, MessageId m) throws DbException;
+	MessageId getGroupMessageParent(T txn, MessageId m) throws DbException;
 
 	/**
 	 * Returns the IDs of any batches sent to the given contact that should now
@@ -248,14 +250,6 @@ interface Database<T> {
 	 */
 	Collection<MessageId> getOldMessages(T txn, int size) throws DbException;
 
-	/**
-	 * Returns the parent of the given message, or null if the message has
-	 * no parent.
-	 * <p>
-	 * Locking: messages read.
-	 */
-	MessageId getParent(T txn, MessageId m) throws DbException;
-
 	/**
 	 * Returns the user's rating for the given author.
 	 * <p>
diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index 11ad89ca39fcb034f340d8243eff2c09920bcd1e..042b714f21392c90bb7af9db73c990af95e1f955 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -278,19 +278,13 @@ DatabaseCleaner.Callback {
 	 */
 	private int updateAncestorSendability(T txn, MessageId m, boolean increment)
 	throws DbException {
-		GroupId group = db.getGroup(txn, m);
 		int affected = 0;
 		boolean changed = true;
 		while(changed) {
-			// Stop if the message has no parent
-			MessageId parent = db.getParent(txn, m);
+			// Stop if the message has no parent, or the parent isn't in the
+			// database, or the parent belongs to a different group
+			MessageId parent = db.getGroupMessageParent(txn, m);
 			if(parent == null) break;
-			// Stop if the parent isn't in the database
-			if(!db.containsMessage(txn, parent)) break;
-			// Stop if the message and the parent aren't in the same group
-			assert group != null;
-			GroupId parentGroup = db.getGroup(txn, parent);
-			if(!group.equals(parentGroup)) break;
 			// Increment or decrement the parent's sendability
 			int parentSendability = db.getSendability(txn, parent);
 			if(increment) {
@@ -306,7 +300,6 @@ DatabaseCleaner.Callback {
 			db.setSendability(txn, parent, parentSendability);
 			// Move on to the parent's parent
 			m = parent;
-			group = parentGroup;
 		}
 		return affected;
 	}
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 3b0b235ccf6e9a869e042ca86ceed7bb13bf1c19..c33d6fcc874d0f60c05ec7f2c6b353857679ea1c 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -1098,20 +1098,27 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public MessageId getParent(Connection txn, MessageId m) throws DbException {
+	public MessageId getGroupMessageParent(Connection txn, MessageId m)
+	throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
-			String sql = "SELECT parentId FROM messages WHERE messageId = ?";
+			String sql = "SELECT m1.parentId FROM messages AS m1"
+				+ " JOIN messages AS m2"
+				+ " ON m1.parentId = m2.messageId"
+				+ " AND m1.groupId = m2.groupId"
+				+ " WHERE m1.messageId = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, m.getBytes());
 			rs = ps.executeQuery();
-			if(!rs.next()) throw new DbStateException();
-			byte[] parent = rs.getBytes(1);
-			if(rs.next()) throw new DbStateException();
+			MessageId parent = null;
+			if(rs.next()) {
+				parent = new MessageId(rs.getBytes(1));
+				if(rs.next()) throw new DbStateException();
+			}
 			rs.close();
 			ps.close();
-			return parent == null ? null : new MessageId(parent);
+			return parent;
 		} catch(SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);
diff --git a/test/net/sf/briar/db/DatabaseComponentImplTest.java b/test/net/sf/briar/db/DatabaseComponentImplTest.java
index 6578ed1c48a7128dad18ead81cc6dc6b86317ad6..a7771e174eb9272ef8bdda1193a7dbc9d2b8138b 100644
--- a/test/net/sf/briar/db/DatabaseComponentImplTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentImplTest.java
@@ -105,9 +105,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
 			will(returnValue(Collections.singleton(messageId)));
 			oneOf(database).getSendability(txn, messageId);
 			will(returnValue(1));
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(null));
 			oneOf(database).removeMessage(txn, messageId);
 			oneOf(database).commitTransaction(txn);
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 13f49901e1d3904980b487c4836fe0f316f4389f..1e8d39f07eda0b8491a63cd6f50be6885a6a7346 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -200,117 +200,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(0));
 			oneOf(database).setSendability(txn, messageId, 1);
 			// Backward inclusion stops when the message has no parent
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
-			will(returnValue(null));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner);
-
-		db.setRating(authorId, Rating.GOOD);
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testMissingParentStopsBackwardInclusion() throws DbException {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).setRating(txn, authorId, Rating.GOOD);
-			// The sendability of the author's messages should be incremented
-			oneOf(database).getMessagesByAuthor(txn, authorId);
-			will(returnValue(Collections.singletonList(messageId)));
-			oneOf(database).getSendability(txn, messageId);
-			will(returnValue(0));
-			oneOf(database).setSendability(txn, messageId, 1);
-			// The parent exists
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
-			will(returnValue(parentId));
-			// The parent isn't in the DB
-			oneOf(database).containsMessage(txn, parentId);
-			will(returnValue(false));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner);
-
-		db.setRating(authorId, Rating.GOOD);
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testParentInAnotherGroupStopsBackwardInclusion()
-	throws DbException {
-		final GroupId groupId1 = new GroupId(TestUtils.getRandomId());
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).setRating(txn, authorId, Rating.GOOD);
-			// The sendability of the author's messages should be incremented
-			oneOf(database).getMessagesByAuthor(txn, authorId);
-			will(returnValue(Collections.singletonList(messageId)));
-			oneOf(database).getSendability(txn, messageId);
-			will(returnValue(0));
-			oneOf(database).setSendability(txn, messageId, 1);
-			// The parent exists and is in the database
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
-			will(returnValue(parentId));
-			oneOf(database).containsMessage(txn, parentId);
-			will(returnValue(true));
-			// The parent is in a different group
-			oneOf(database).getGroup(txn, parentId);
-			will(returnValue(groupId1));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner);
-
-		db.setRating(authorId, Rating.GOOD);
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testPrivateParentStopsBackwardInclusion() throws DbException {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).setRating(txn, authorId, Rating.GOOD);
-			// The sendability of the author's messages should be incremented
-			oneOf(database).getMessagesByAuthor(txn, authorId);
-			will(returnValue(Collections.singletonList(messageId)));
-			oneOf(database).getSendability(txn, messageId);
-			will(returnValue(0));
-			oneOf(database).setSendability(txn, messageId, 1);
-			// The parent exists and is in the database
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
-			will(returnValue(parentId));
-			oneOf(database).containsMessage(txn, parentId);
-			will(returnValue(true));
-			// The parent is a private message
-			oneOf(database).getGroup(txn, parentId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(null));
 			oneOf(database).commitTransaction(txn);
 		}});
@@ -340,14 +230,8 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(0));
 			oneOf(database).setSendability(txn, messageId, 1);
 			// The parent exists, is in the DB, and is in the same group
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(parentId));
-			oneOf(database).containsMessage(txn, parentId);
-			will(returnValue(true));
-			oneOf(database).getGroup(txn, parentId);
-			will(returnValue(groupId));
 			// The parent is already sendable
 			oneOf(database).getSendability(txn, parentId);
 			will(returnValue(1));
@@ -380,19 +264,14 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(0));
 			oneOf(database).setSendability(txn, messageId, 1);
 			// The parent exists, is in the DB, and is in the same group
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(parentId));
-			oneOf(database).containsMessage(txn, parentId);
-			will(returnValue(true));
-			oneOf(database).getGroup(txn, parentId);
-			will(returnValue(groupId));
 			// The parent is not already sendable
 			oneOf(database).getSendability(txn, parentId);
 			will(returnValue(0));
 			oneOf(database).setSendability(txn, parentId, 1);
-			oneOf(database).getParent(txn, parentId);
+			// The parent has no parent
+			oneOf(database).getGroupMessageParent(txn, parentId);
 			will(returnValue(null));
 			oneOf(database).commitTransaction(txn);
 		}});
@@ -505,9 +384,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(2));
 			oneOf(database).setSendability(txn, messageId, 3);
 			// The sendability of the message's ancestors should be updated
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(null));
 			oneOf(database).commitTransaction(txn);
 		}});
@@ -1155,9 +1032,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			oneOf(database).getNumberOfSendableChildren(txn, messageId);
 			will(returnValue(1));
 			oneOf(database).setSendability(txn, messageId, 2);
-			oneOf(database).getGroup(txn, messageId);
-			will(returnValue(groupId));
-			oneOf(database).getParent(txn, messageId);
+			oneOf(database).getGroupMessageParent(txn, messageId);
 			will(returnValue(null));
 			// The batch needs to be acknowledged
 			oneOf(batch).getId();
diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java
index bef871e2d3bba4d542bc814cf4946a4cdc9a37bb..131fd465cfba34455d62b7689d9e002571f18d6a 100644
--- a/test/net/sf/briar/db/H2DatabaseTest.java
+++ b/test/net/sf/briar/db/H2DatabaseTest.java
@@ -1430,6 +1430,127 @@ public class H2DatabaseTest extends TestCase {
 		db.close();
 	}
 
+	@Test
+	public void testGetGroupMessageParentWithNoParent() throws DbException {
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Subscribe to a group
+		db.addSubscription(txn, group);
+
+		// A message with no parent should return null
+		MessageId childId = new MessageId(TestUtils.getRandomId());
+		Message child = new TestMessage(childId, null, groupId, null,
+				timestamp, raw);
+		db.addGroupMessage(txn, child);
+		assertTrue(db.containsMessage(txn, childId));
+		assertNull(db.getGroupMessageParent(txn, childId));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
+	@Test
+	public void testGetGroupMessageParentWithAbsentParent() throws DbException {
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Subscribe to a group
+		db.addSubscription(txn, group);
+
+		// A message with an absent parent should return null
+		MessageId childId = new MessageId(TestUtils.getRandomId());
+		MessageId parentId = new MessageId(TestUtils.getRandomId());
+		Message child = new TestMessage(childId, parentId, groupId, null,
+				timestamp, raw);
+		db.addGroupMessage(txn, child);
+		assertTrue(db.containsMessage(txn, childId));
+		assertFalse(db.containsMessage(txn, parentId));
+		assertNull(db.getGroupMessageParent(txn, childId));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
+	@Test
+	public void testGetGroupMessageParentWithParentInAnotherGroup()
+	throws DbException {
+		GroupId groupId1 = new GroupId(TestUtils.getRandomId());
+		Group group1 = groupFactory.createGroup(groupId1, "Group name", null);
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Subscribe to two groups
+		db.addSubscription(txn, group);
+		db.addSubscription(txn, group1);
+
+		// A message with a parent in another group should return null
+		MessageId childId = new MessageId(TestUtils.getRandomId());
+		MessageId parentId = new MessageId(TestUtils.getRandomId());
+		Message child = new TestMessage(childId, parentId, groupId, null,
+				timestamp, raw);
+		Message parent = new TestMessage(parentId, null, groupId1, null,
+				timestamp, raw);
+		db.addGroupMessage(txn, child);
+		db.addGroupMessage(txn, parent);
+		assertTrue(db.containsMessage(txn, childId));
+		assertTrue(db.containsMessage(txn, parentId));
+		assertNull(db.getGroupMessageParent(txn, childId));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
+	@Test
+	public void testGetGroupMessageParentWithPrivateParent()
+	throws DbException {
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Add a contact and subscribe to a group
+		assertEquals(contactId, db.addContact(txn, transports, secret));
+		db.addSubscription(txn, group);
+
+		// A message with a private parent should return null
+		MessageId childId = new MessageId(TestUtils.getRandomId());
+		Message child = new TestMessage(childId, privateMessageId, groupId, null,
+				timestamp, raw);
+		db.addGroupMessage(txn, child);
+		db.addPrivateMessage(txn, privateMessage, contactId);
+		assertTrue(db.containsMessage(txn, childId));
+		assertTrue(db.containsMessage(txn, privateMessageId));
+		assertNull(db.getGroupMessageParent(txn, childId));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
+	@Test
+	public void testGetGroupMessageParentWithParentInSameGroup()
+	throws DbException {
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Subscribe to a group
+		db.addSubscription(txn, group);
+
+		// A message with a parent in the same group should return the parent
+		MessageId childId = new MessageId(TestUtils.getRandomId());
+		MessageId parentId = new MessageId(TestUtils.getRandomId());
+		Message child = new TestMessage(childId, parentId, groupId, null,
+				timestamp, raw);
+		Message parent = new TestMessage(parentId, null, groupId, null,
+				timestamp, raw);
+		db.addGroupMessage(txn, child);
+		db.addGroupMessage(txn, parent);
+		assertTrue(db.containsMessage(txn, childId));
+		assertTrue(db.containsMessage(txn, parentId));
+		assertEquals(parentId, db.getGroupMessageParent(txn, childId));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
 	@Test
 	public void testExceptionHandling() throws DbException {
 		Database<Connection> db = open(false);