diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e114f2a6d2..7ebc7145919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,8 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) -* None. - +* Fixed an incorrect detection of multiple incoming links in a migration when changing a table to embedded and removing a link to it at the same time. ([#4694](https://github.com/realm/realm-core/issues/4694) since 10.0.0-beta.2) + ### Breaking changes * None. diff --git a/src/realm/object-store/schema.cpp b/src/realm/object-store/schema.cpp index 8f8a64e17ea..0121971b9f0 100644 --- a/src/realm/object-store/schema.cpp +++ b/src/realm/object-store/schema.cpp @@ -322,8 +322,6 @@ std::vector Schema::compare(Schema const& target_schema, bool incl if (include_table_removals) changes.emplace_back(schema_change::RemoveTable{existing}); } - else if (existing->is_embedded != target->is_embedded) - changes.emplace_back(schema_change::ChangeTableType{target}); }); // Modify columns @@ -336,6 +334,14 @@ std::vector Schema::compare(Schema const& target_schema, bool incl } // nothing for tables in existing but not target }); + + // Detect embedded table changes last, in case column property changes affect link counts + zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) { + if (existing && target && existing->is_embedded != target->is_embedded) { + changes.emplace_back(schema_change::ChangeTableType{target}); + } + }); + return changes; } diff --git a/test/object-store/migrations.cpp b/test/object-store/migrations.cpp index f5f3db2fae9..2563eee331c 100644 --- a/test/object-store/migrations.cpp +++ b/test/object-store/migrations.cpp @@ -926,6 +926,64 @@ TEST_CASE("migration: Automatic") { } } + SECTION("change table to embedded - multiple incoming links per object resolved by removing a column") { + Schema schema = { + {"child_table", + { + {"value", PropertyType::Int}, + }}, + {"parent_table", + { + {"child_property", PropertyType::Object | PropertyType::Nullable, "child_table"}, + {"child_property_duplicate", PropertyType::Object | PropertyType::Nullable, "child_table"}, + }}, + }; + Schema schema2 = { + {"child_table", + IsEmbedded{true}, + { + {"value", PropertyType::Int}, + }}, + {"parent_table", + { + {"child_property", PropertyType::Object | PropertyType::Nullable, "child_table"}, + }}, + }; + + auto realm = Realm::get_shared_realm(config); + realm->update_schema(schema, 1); + realm->begin_transaction(); + auto child_table = ObjectStore::table_for_object_type(realm->read_group(), "child_table"); + Obj child_object1 = child_table->create_object(); + child_object1.set("value", 42); + Obj child_object2 = child_table->create_object(); + child_object2.set("value", 43); + auto parent_table = ObjectStore::table_for_object_type(realm->read_group(), "parent_table"); + auto child_object_key1 = child_object1.get_key(); + auto child_object_key2 = child_object2.get_key(); + parent_table->create_object().set_all(child_object_key1, child_object_key1); + parent_table->create_object().set_all(child_object_key2, child_object_key2); + realm->commit_transaction(); + REQUIRE(parent_table->size() == 2); + REQUIRE(child_table->size() == 2); + REQUIRE_FALSE(child_table->is_embedded()); + + REQUIRE_NOTHROW(realm->update_schema(schema2, 2, nullptr)); + + REQUIRE(realm->schema_version() == 2); + REQUIRE(parent_table->size() == 2); + REQUIRE(child_table->size() == 2); + REQUIRE(child_table->is_embedded()); + CppContext context(realm); + for (int i = 0; i < 2; i++) { + Object parent_object(realm, "parent_table", i); + Object child_object = + any_cast(parent_object.get_property_value(context, "child_property")); + Int value = any_cast(child_object.get_property_value(context, "value")); + REQUIRE(value == 42 + i); + } + } + SECTION("change table to embedded - multiple incoming links - resolved in migration block") { Schema schema = { {"child_table",