Rework handling of mixed move_last_over() and modifications to actually work

This commit is contained in:
Thomas Goyne 2016-03-04 11:19:33 -08:00
parent d46f2c65ba
commit e25e4c2dcd
6 changed files with 68 additions and 64 deletions

View File

@ -150,14 +150,7 @@ void CollectionChangeIndices::merge(realm::CollectionChangeIndices&& c)
void CollectionChangeIndices::modify(size_t ndx) void CollectionChangeIndices::modify(size_t ndx)
{ {
if (!insertions.contains(ndx))
modifications.add(ndx); modifications.add(ndx);
// FIXME: this breaks mapping old row indices to new
// FIXME: is that a problem?
// If this row was previously moved, unmark it as a move
moves.erase(remove_if(begin(moves), end(moves),
[&](auto move) { return move.to == ndx; }),
end(moves));
} }
void CollectionChangeIndices::insert(size_t index, size_t count) void CollectionChangeIndices::insert(size_t index, size_t count)
@ -221,31 +214,30 @@ void CollectionChangeIndices::move(size_t from, size_t to)
// Collapse A -> B, B -> C into a single A -> C move // Collapse A -> B, B -> C into a single A -> C move
move.to = to; move.to = to;
modifications.erase_at(from);
insertions.erase_at(from);
modifications.shift_for_insert_at(to);
insertions.insert_at(to);
updated_existing_move = true; updated_existing_move = true;
insertions.erase_at(from);
insertions.insert_at(to);
} }
if (updated_existing_move)
return;
if (!insertions.contains(from)) { if (!updated_existing_move) {
auto shifted_from = insertions.unshift(from); auto shifted_from = insertions.erase_and_unshift(from);
insertions.insert_at(to);
// Don't report deletions/moves for newly inserted rows
if (shifted_from != npos) {
shifted_from = deletions.add_shifted(shifted_from); shifted_from = deletions.add_shifted(shifted_from);
// Don't record it as a move if the source row was newly inserted or
// was previously changed
if (!modifications.contains(from))
moves.push_back({shifted_from, to}); moves.push_back({shifted_from, to});
} }
}
bool modified = modifications.contains(from);
modifications.erase_at(from); modifications.erase_at(from);
insertions.erase_at(from);
if (modified)
modifications.insert_at(to);
else
modifications.shift_for_insert_at(to); modifications.shift_for_insert_at(to);
insertions.insert_at(to);
} }
void CollectionChangeIndices::move_over(size_t row_ndx, size_t last_row) void CollectionChangeIndices::move_over(size_t row_ndx, size_t last_row)
@ -255,6 +247,9 @@ void CollectionChangeIndices::move_over(size_t row_ndx, size_t last_row)
erase(row_ndx); erase(row_ndx);
return; return;
} }
move(last_row, row_ndx);
erase(row_ndx + 1);
return;
bool updated_existing_move = false; bool updated_existing_move = false;
for (size_t i = 0; i < moves.size(); ++i) { for (size_t i = 0; i < moves.size(); ++i) {
@ -278,24 +273,19 @@ void CollectionChangeIndices::move_over(size_t row_ndx, size_t last_row)
moves.push_back({last_row, row_ndx}); moves.push_back({last_row, row_ndx});
} }
if (insertions.contains(row_ndx)) {
insertions.remove(row_ndx); insertions.remove(row_ndx);
}
else {
if (modifications.contains(row_ndx)) {
modifications.remove(row_ndx); modifications.remove(row_ndx);
}
deletions.add(row_ndx);
}
if (insertions.contains(last_row)) { // not add_shifted() because unordered removal does not shift
insertions.remove(last_row); // mixed ordered/unordered removal currently not supported
insertions.add(row_ndx); deletions.add(row_ndx);
}
else if (modifications.contains(last_row)) { if (modifications.contains(last_row)) {
modifications.remove(last_row); modifications.remove(last_row);
modifications.add(row_ndx); modifications.add(row_ndx);
} }
insertions.add(row_ndx);
} }
void CollectionChangeIndices::verify() void CollectionChangeIndices::verify()
@ -305,10 +295,10 @@ void CollectionChangeIndices::verify()
REALM_ASSERT(deletions.contains(move.from)); REALM_ASSERT(deletions.contains(move.from));
REALM_ASSERT(insertions.contains(move.to)); REALM_ASSERT(insertions.contains(move.to));
} }
for (auto index : modifications.as_indexes()) // for (auto index : modifications.as_indexes())
REALM_ASSERT(!insertions.contains(index)); // REALM_ASSERT(!insertions.contains(index));
for (auto index : insertions.as_indexes()) // for (auto index : insertions.as_indexes())
REALM_ASSERT(!modifications.contains(index)); // REALM_ASSERT(!modifications.contains(index));
#endif #endif
} }

View File

@ -121,13 +121,15 @@ void AsyncQuery::run()
if (changes) { if (changes) {
for (auto& idx : m_previous_rows) { for (auto& idx : m_previous_rows) {
if (!map_moves(idx, *changes)) {
if (changes->deletions.contains(idx)) if (changes->deletions.contains(idx))
idx = npos; idx = npos;
else else {
map_moves(idx, *changes);
REALM_ASSERT_DEBUG(!changes->insertions.contains(idx)); REALM_ASSERT_DEBUG(!changes->insertions.contains(idx));
} }
} }
}
}
m_changes = CollectionChangeIndices::calculate(m_previous_rows, next_rows, m_changes = CollectionChangeIndices::calculate(m_previous_rows, next_rows,
[&](size_t row) { return m_info->row_did_change(*m_query->get_table(), row); }, [&](size_t row) { return m_info->row_did_change(*m_query->get_table(), row); },

View File

@ -97,6 +97,11 @@ void ListNotifier::run()
return; return;
} }
m_change.moves.erase(remove_if(begin(m_change.moves), end(m_change.moves),
[&](auto move) { return m_change.modifications.contains(move.to); }),
end(m_change.moves));
m_change.modifications.remove(m_change.insertions);
for (size_t i = 0; i < m_lv->size(); ++i) { for (size_t i = 0; i < m_lv->size(); ++i) {
if (m_change.insertions.contains(i) || m_change.modifications.contains(i)) if (m_change.insertions.contains(i) || m_change.modifications.contains(i))
continue; continue;

View File

@ -39,10 +39,11 @@ TEST_CASE("collection change indices") {
REQUIRE_INDICES(c.modifications, 3, 4); REQUIRE_INDICES(c.modifications, 3, 4);
} }
SECTION("modify() on an inserted row is a no-op") { SECTION("modify() on an inserted row marks it as both inserted and modified") {
c.insert(3); c.insert(3);
c.modify(3); c.modify(3);
REQUIRE(c.modifications.empty()); REQUIRE_INDICES(c.insertions, 3);
REQUIRE_INDICES(c.modifications, 3);
} }
SECTION("modify() doesn't interact with deleted rows") { SECTION("modify() doesn't interact with deleted rows") {
@ -133,13 +134,13 @@ TEST_CASE("collection change indices") {
REQUIRE_MOVES(c, {8, 5}); REQUIRE_MOVES(c, {8, 5});
} }
SECTION("move_over() removes previous insertions for that row") { SECTION("move_over() does not mark the old last row as moved if it was newly inserted") {
c.insert(5); c.insert(8);
c.move_over(5, 8); c.move_over(5, 8);
REQUIRE(c.insertions.empty()); REQUIRE(c.moves.empty());
} }
SECTION("move_over() removes previous modifications for that row") { SECTION("move_over() removes previous modifications for the removed row") {
c.modify(5); c.modify(5);
c.move_over(5, 8); c.move_over(5, 8);
REQUIRE(c.modifications.empty()); REQUIRE(c.modifications.empty());
@ -160,7 +161,7 @@ TEST_CASE("collection change indices") {
SECTION("move_over() removes moves to the target") { SECTION("move_over() removes moves to the target") {
c.move(3, 5); c.move(3, 5);
c.move_over(5, 8); c.move_over(5, 8);
REQUIRE(c.moves.empty()); REQUIRE_MOVES(c, {8, 5});
} }
SECTION("move_over() updates moves to the source") { SECTION("move_over() updates moves to the source") {
@ -172,7 +173,8 @@ TEST_CASE("collection change indices") {
SECTION("move_over() is not shifted by previous calls to move_over()") { SECTION("move_over() is not shifted by previous calls to move_over()") {
c.move_over(5, 10); c.move_over(5, 10);
c.move_over(6, 9); c.move_over(6, 9);
REQUIRE_INDICES(c.deletions, 5, 6); REQUIRE_INDICES(c.deletions, 5, 6, 9, 10);
REQUIRE_INDICES(c.insertions, 5, 6);
REQUIRE_MOVES(c, {10, 5}, {9, 6}); REQUIRE_MOVES(c, {10, 5}, {9, 6});
} }
} }

View File

@ -204,12 +204,13 @@ TEST_CASE("Results") {
REQUIRE_INDICES(change.deletions, 1); REQUIRE_INDICES(change.deletions, 1);
} }
SECTION("modifying a non-matching row to match marks that row as inserted") { SECTION("modifying a non-matching row to match marks that row as inserted, but not modified") {
write([&] { write([&] {
table->set_int(0, 7, 3); table->set_int(0, 7, 3);
}); });
REQUIRE(notification_calls == 2); REQUIRE(notification_calls == 2);
REQUIRE_INDICES(change.insertions, 4); REQUIRE_INDICES(change.insertions, 4);
REQUIRE(change.modifications.empty());
} }
SECTION("deleting a matching row marks that row as deleted") { SECTION("deleting a matching row marks that row as deleted") {

View File

@ -97,9 +97,12 @@ private:
CHECK(m_initial[i] == m_linkview->get(i).get_int(0)); CHECK(m_initial[i] == m_linkview->get(i).get_int(0));
// Verify that everything marked as a move actually is one // Verify that everything marked as a move actually is one
for (size_t i = 0; i < move_sources.size(); ++i) for (size_t i = 0; i < move_sources.size(); ++i) {
if (!info.modifications.contains(info.moves[i].to)) {
CHECK(m_linkview->get(info.moves[i].to).get_int(0) == move_sources[i]); CHECK(m_linkview->get(info.moves[i].to).get_int(0) == move_sources[i]);
} }
}
}
}; };
TEST_CASE("Transaction log parsing") { TEST_CASE("Transaction log parsing") {
@ -386,14 +389,14 @@ TEST_CASE("Transaction log parsing") {
REQUIRE(info.tables[2].deletions.empty()); REQUIRE(info.tables[2].deletions.empty());
} }
SECTION("modifying newly added rows is not reported as a modification") { SECTION("modifying newly added rows is reported as a modification") {
auto info = track_changes({false, false, true}, [&] { auto info = track_changes({false, false, true}, [&] {
table.add_empty_row(); table.add_empty_row();
table.set_int(0, 10, 10); table.set_int(0, 10, 10);
}); });
REQUIRE(info.tables.size() == 3); REQUIRE(info.tables.size() == 3);
REQUIRE_INDICES(info.tables[2].insertions, 10); REQUIRE_INDICES(info.tables[2].insertions, 10);
REQUIRE(info.tables[2].modifications.empty()); REQUIRE_INDICES(info.tables[2].modifications, 10);
} }
SECTION("move_last_over() does not shift rows other than the last one") { SECTION("move_last_over() does not shift rows other than the last one") {
@ -402,7 +405,8 @@ TEST_CASE("Transaction log parsing") {
table.move_last_over(3); table.move_last_over(3);
}); });
REQUIRE(info.tables.size() == 3); REQUIRE(info.tables.size() == 3);
REQUIRE_INDICES(info.tables[2].deletions, 2, 3); REQUIRE_INDICES(info.tables[2].deletions, 2, 3, 8, 9);
REQUIRE_INDICES(info.tables[2].insertions, 2, 3);
REQUIRE_MOVES(info.tables[2], {9, 2}, {8, 3}); REQUIRE_MOVES(info.tables[2], {9, 2}, {8, 3});
} }
} }
@ -651,7 +655,7 @@ TEST_CASE("Transaction log parsing") {
lv->set(5, 1); lv->set(5, 1);
} }
REQUIRE_INDICES(changes.insertions, 5); REQUIRE_INDICES(changes.insertions, 5);
REQUIRE(changes.modifications.size() == 0); REQUIRE_INDICES(changes.modifications, 5);
VALIDATE_CHANGES(changes) { VALIDATE_CHANGES(changes) {
lv->insert(5, 0); lv->insert(5, 0);
@ -808,7 +812,7 @@ TEST_CASE("Transaction log parsing") {
REQUIRE_INDICES(changes.deletions, 5); REQUIRE_INDICES(changes.deletions, 5);
REQUIRE_INDICES(changes.insertions, 0); REQUIRE_INDICES(changes.insertions, 0);
REQUIRE(changes.moves.empty()); REQUIRE_MOVES(changes, {5, 0});
} }
SECTION("set after move is just insert+delete") { SECTION("set after move is just insert+delete") {
@ -819,7 +823,7 @@ TEST_CASE("Transaction log parsing") {
REQUIRE_INDICES(changes.deletions, 5); REQUIRE_INDICES(changes.deletions, 5);
REQUIRE_INDICES(changes.insertions, 0); REQUIRE_INDICES(changes.insertions, 0);
REQUIRE(changes.moves.empty()); REQUIRE_MOVES(changes, {5, 0});
} }
SECTION("delete after move removes original row") { SECTION("delete after move removes original row") {