diff --git a/CHANGELOG.md b/CHANGELOG.md index af566db7591..bce13fa54a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) +* Fixed an assertion failure when observing change notifications on a sectioned result, if the first modification was to a linked property that did not cause the state of the sections to change. ([#5912](https://github.com/realm/realm-core/issues/5912), since the introduction of sectioned results in v12.3.0) * CompensatingWriteErrorInfo reported string primary keys as boolean values instead ([PR #5938](https://github.com/realm/realm-core/pull/5938), since the introduction of CompensatingWriteErrorInfo in 12.1.0). * Fix a use-after-free if the last external reference to an encrypted Realm was closed between when a client reset error was received and when the download of the new Realm began. ([PR #5949](https://github.com/realm/realm-core/pull/5949), since 12.4.0). diff --git a/src/realm/object-store/sectioned_results.cpp b/src/realm/object-store/sectioned_results.cpp index 0a13a05016e..78204010b57 100644 --- a/src/realm/object-store/sectioned_results.cpp +++ b/src/realm/object-store/sectioned_results.cpp @@ -401,6 +401,14 @@ void SectionedResults::calculate_sections() m_row_to_index_path[i] = {section.index, section.indices.size() - 1}; } } + if (!has_performed_initial_evalutation) { + REALM_ASSERT_EX(m_previous_key_to_index_lookup.size() == 0, m_previous_key_to_index_lookup.size()); + REALM_ASSERT_EX(m_prev_section_index_to_key.size() == 0, m_prev_section_index_to_key.size()); + for (auto& [key, section] : m_sections) { + m_previous_key_to_index_lookup[key] = section.index; + m_prev_section_index_to_key[section.index] = section.key; + } + } } size_t SectionedResults::size() diff --git a/test/object-store/sectioned_results.cpp b/test/object-store/sectioned_results.cpp index 6dbc0532324..f5234600805 100644 --- a/test/object-store/sectioned_results.cpp +++ b/test/object-store/sectioned_results.cpp @@ -1554,6 +1554,75 @@ TEST_CASE("sectioned results", "[sectioned_results]") { } } +TEST_CASE("sectioned results link notification bug", "[sectioned_results]") { + _impl::RealmCoordinator::assert_no_open_realms(); + + InMemoryTestFile config; + config.automatic_change_notifications = false; + + auto r = Realm::get_shared_realm(config); + r->update_schema( + {{"Transaction", + {{"_id", PropertyType::String, Property::IsPrimary{true}}, + {"date", PropertyType::Date}, + {"account", PropertyType::Object | PropertyType::Nullable, "Account"}}}, + {"Account", {{"_id", PropertyType::String, Property::IsPrimary{true}}, {"name", PropertyType::String}}}}); + + auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path); + auto transaction_table = r->read_group().get_table("class_Transaction"); + transaction_table->get_column_key("date"); + auto account_col = transaction_table->get_column_key("account"); + auto account_table = r->read_group().get_table("class_Account"); + auto account_name_col = account_table->get_column_key("name"); + + r->begin_transaction(); + auto t1 = transaction_table->create_object_with_primary_key("t"); + auto a1 = account_table->create_object_with_primary_key("a"); + t1.set(account_col, a1.get_key()); + r->commit_transaction(); + + Results results(r, transaction_table); + auto sorted = results.sort({{"date", false}}); + auto sectioned_results = sorted.sectioned_results([](Mixed value, SharedRealm realm) { + auto obj = Object(realm, value.get_link()); + auto ts = obj.get_column_value("date"); + auto tp = ts.get_time_point(); + auto day = std::chrono::floor(tp); + return Timestamp{day}; + }); + + REQUIRE(sectioned_results.size() == 1); + REQUIRE(sectioned_results[0].size() == 1); + + SectionedResultsChangeSet changes; + size_t callback_count = 0; + auto token = sectioned_results.add_notification_callback([&](SectionedResultsChangeSet c) { + changes = c; + ++callback_count; + }); + coordinator->on_change(); + advance_and_notify(*r); + REQUIRE(callback_count == 1); + REQUIRE(changes.sections_to_insert.empty()); + REQUIRE(changes.sections_to_delete.empty()); + REQUIRE(changes.insertions.size() == 0); + REQUIRE(changes.deletions.size() == 0); + REQUIRE(changes.modifications.size() == 0); + + r->begin_transaction(); + a1.set(account_name_col, "a2"); + r->commit_transaction(); + advance_and_notify(*r); + + REQUIRE(callback_count == 2); + REQUIRE(changes.sections_to_insert.empty()); + REQUIRE(changes.sections_to_delete.empty()); + REQUIRE(changes.insertions.size() == 0); + REQUIRE(changes.deletions.size() == 0); + REQUIRE(changes.modifications.size() == 1); + REQUIRE_INDICES(changes.modifications[0], 0); +} + namespace cf = realm::sectioned_results_fixtures; TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", cf::MixedVal, cf::Int, cf::Bool,