diff --git a/src/object_store.cpp b/src/object_store.cpp index dde618a46..55c8c490f 100644 --- a/src/object_store.cpp +++ b/src/object_store.cpp @@ -124,7 +124,7 @@ void insert_column(Group& group, Table& table, Property const& property, size_t TableRef link_table = group.get_table(target_name); REALM_ASSERT(link_table); table.insert_column_link(col_ndx, is_array(property.type) ? type_LinkList : type_Link, - property.name, *link_table); + property.name, *link_table, property.link_type()); } else if (is_array(property.type)) { DescriptorRef desc; @@ -408,6 +408,21 @@ struct SchemaDifferenceExplainer { { errors.emplace_back("Property '%1.%2' has been made unindexed.", op.object->name, op.property->name); } + void operator()(schema_change::ChangeRelationshipType op) + { + std::string type; + switch (op.property->relationship) { + case Relationship::Strong: + type = "'Strong'"; + break; + + case Relationship::Weak: + type = "'Weak'"; + break; + } + + errors.emplace_back("Property '%1.%2' has changed the type of the relationship to %3.", op.object->name, op.property->name, type); + } }; class TableHelper { @@ -458,6 +473,7 @@ bool ObjectStore::needs_migration(std::vector const& changes) bool operator()(MakePropertyRequired) { return true; } bool operator()(RemoveIndex) { return false; } bool operator()(RemoveProperty) { return true; } + bool operator()(ChangeRelationshipType) { return true; } }; return std::any_of(begin(changes), end(changes), @@ -586,11 +602,9 @@ static void create_initial_tables(Group& group, std::vector const& void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); } void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); } + void operator()(ChangeRelationshipType op) { table(op.object).get_descriptor()->set_link_type(op.property->table_column, op.property->link_type()); } - void operator()(ChangePropertyType op) - { - replace_column(group, table(op.object), *op.old_property, *op.new_property); - } } applier{group}; for (auto& change : changes) { @@ -621,6 +635,7 @@ void ObjectStore::apply_additive_changes(Group& group, std::vector void operator()(ChangePropertyType) { } void operator()(MakePropertyNullable) { } void operator()(MakePropertyRequired) { } + void operator()(ChangeRelationshipType) { } } applier{group, update_indexes}; for (auto& change : changes) { @@ -647,6 +662,8 @@ static void apply_pre_migration_changes(Group& group, std::vector void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name.c_str(), op.property ? op.property->name.c_str() : ""); } void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + void operator()(ChangeRelationshipType op) { table(op.object).get_descriptor()->set_link_type(op.property->table_column, op.property->link_type()); } + } applier{group}; for (auto& change : changes) { @@ -704,6 +721,7 @@ static void apply_post_migration_changes(Group& group, std::vector void operator()(MakePropertyNullable) { } void operator()(MakePropertyRequired) { } void operator()(AddProperty) { } + void operator()(ChangeRelationshipType) { } } applier{group, initial_schema, did_reread_schema}; for (auto& change : changes) { @@ -749,6 +767,7 @@ static void create_default_permissions(Group& group, std::vector c void operator()(AddIndex) { } void operator()(RemoveIndex) { } void operator()(ChangePropertyType) { } + void operator()(ChangeRelationshipType) { } } applier{group}; for (auto& change : changes) { @@ -890,6 +909,7 @@ util::Optional ObjectStore::property_for_column_index(ConstTableRef& t // set link type for objects and arrays ConstTableRef linkTable = table->get_link_target(column_index); property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); + property.relationship = (table->get_link_type(column_index) == LinkType::link_Strong) ? Relationship::Strong : Relationship::Weak; } return property; } diff --git a/src/property.hpp b/src/property.hpp index ad76b9d05..07a9cae9c 100644 --- a/src/property.hpp +++ b/src/property.hpp @@ -21,6 +21,7 @@ #include "util/tagged_bool.hpp" +#include #include #include @@ -58,6 +59,11 @@ enum class PropertyType : unsigned char { Flags = Nullable | Array }; +enum class Relationship : bool { + Strong = true, + Weak = false +}; + struct Property { using IsPrimary = util::TaggedBool; using IsIndexed = util::TaggedBool; @@ -65,7 +71,10 @@ struct Property { std::string name; PropertyType type = PropertyType::Int; std::string object_type; - std::string link_origin_property_name; + // Only used with LinkingObjects. `relationship` is ignored if this is different than "" + std::string link_origin_property_name = ""; + // Only useful if the property is Link or LinkList property and `link_origin_property_name` is "" + Relationship relationship = Relationship::Weak; IsPrimary is_primary = false; IsIndexed is_indexed = false; @@ -74,9 +83,8 @@ struct Property { Property() = default; Property(std::string name, PropertyType type, IsPrimary primary = false, IsIndexed indexed = false); - - Property(std::string name, PropertyType type, std::string object_type, - std::string link_origin_property_name = ""); + Property(std::string name, PropertyType type, std::string object_type, std::string link_origin_property_name = ""); + Property(std::string name, PropertyType type, std::string object_type, Relationship relationship); Property(Property const&) = default; Property(Property&&) = default; @@ -85,6 +93,14 @@ struct Property { bool requires_index() const { return is_primary || is_indexed; } + // Return the underlying LinkType. Only useful for Object or Array properties. + LinkType link_type() const { + switch (relationship) { + case Relationship::Strong: return LinkType::link_Strong; + case Relationship::Weak: return LinkType::link_Weak; + } + } + bool type_is_indexable() const; bool type_is_nullable() const; @@ -214,6 +230,16 @@ inline Property::Property(std::string name, PropertyType type, { } +inline Property::Property(std::string name, PropertyType type, + std::string object_type, + Relationship relationship) +: name(std::move(name)) +, type(type) +, object_type(std::move(object_type)) +, relationship(relationship) +{ +} + inline bool Property::type_is_indexable() const { return type == PropertyType::Int diff --git a/src/schema.cpp b/src/schema.cpp index 7916fee57..efd208b56 100644 --- a/src/schema.cpp +++ b/src/schema.cpp @@ -148,6 +148,9 @@ static void compare(ObjectSchema const& existing_schema, else if (current_prop.requires_index()) { changes.emplace_back(schema_change::RemoveIndex{&existing_schema, ¤t_prop}); } + if (target_prop->relationship != current_prop.relationship) { + changes.emplace_back(schema_change::ChangeRelationshipType{&existing_schema, ¤t_prop}); + } } if (existing_schema.primary_key != target_schema.primary_key) { @@ -262,6 +265,7 @@ bool operator==(SchemaChange const& lft, SchemaChange const& rgt) REALM_SC_COMPARE(RemoveTable, v.object) REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property) REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property) + REALM_SC_COMPARE(ChangeRelationshipType, v.object, v.property) REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property) REALM_SC_COMPARE(MakePropertyRequired, v.object, v.property) REALM_SC_COMPARE(RemoveIndex, v.object, v.property) diff --git a/src/schema.hpp b/src/schema.hpp index 0454deca0..cf02fda0d 100644 --- a/src/schema.hpp +++ b/src/schema.hpp @@ -130,6 +130,11 @@ struct ChangePrimaryKey { const ObjectSchema* object; const Property* property; }; + +struct ChangeRelationshipType { + const ObjectSchema* object; + const Property* property; +}; } #define REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(macro) \ @@ -144,6 +149,7 @@ struct ChangePrimaryKey { macro(AddIndex) \ macro(RemoveIndex) \ macro(ChangePrimaryKey) \ + macro(ChangeRelationshipType) \ class SchemaChange { public: diff --git a/tests/migrations.cpp b/tests/migrations.cpp index 07226cb75..7e52799e3 100644 --- a/tests/migrations.cpp +++ b/tests/migrations.cpp @@ -146,6 +146,12 @@ Schema set_target(Schema schema, StringData object_name, StringData property_nam return schema; } +Schema set_relationship(Schema schema, StringData object_name, StringData property_name, Relationship new_relationship_type) +{ + schema.find(object_name)->property_for_name(property_name)->relationship = new_relationship_type; + return schema; +} + Schema set_primary_key(Schema schema, StringData object_name, StringData new_primary_property) { auto& object_schema = *schema.find(object_name); @@ -1735,7 +1741,7 @@ TEST_CASE("migration: Manual") { {"link origin", { {"not a pk", PropertyType::Int}, {"object", PropertyType::Object|PropertyType::Nullable, "object"}, - {"array", PropertyType::Array|PropertyType::Object, "object"}, + {"array", PropertyType::Array|PropertyType::Object, "object", Relationship::Strong}, }} }; realm->update_schema(schema); @@ -1814,7 +1820,7 @@ TEST_CASE("migration: Manual") { [](SharedRealm, SharedRealm realm, Schema&) { auto table = get_table(realm, "link origin"); table->remove_column(2); - table->add_column_link(type_LinkList, "array", *table); + table->add_column_link(type_LinkList, "array", *table, LinkType::link_Strong); }); } SECTION("make property optional") { @@ -1873,4 +1879,21 @@ TEST_CASE("migration: Manual") { Schema new_schema = remove_property(schema, "object", "value"); REQUIRE_THROWS_AS(realm->update_schema(new_schema, 1, nullptr), SchemaMismatchException); } + + SECTION("change relationship type from Weak to Strong") { + REQUIRE_MIGRATION(set_relationship(schema, "link origin", "object", Relationship::Strong), + [](SharedRealm, SharedRealm realm, Schema&) { + auto table = get_table(realm, "link origin"); + table->get_descriptor()->set_link_type(1, LinkType::link_Strong); + }); + } + + SECTION("change relationship type from Strong to Weak") { + REQUIRE_MIGRATION(set_relationship(schema, "link origin", "array", Relationship::Weak), + [](SharedRealm, SharedRealm realm, Schema&) { + auto table = get_table(realm, "link origin"); + table->get_descriptor()->set_link_type(2, LinkType::link_Weak); + }); + } + } diff --git a/tests/schema.cpp b/tests/schema.cpp index c5d11c319..36612664c 100644 --- a/tests/schema.cpp +++ b/tests/schema.cpp @@ -60,6 +60,7 @@ struct SchemaChangePrinter { REALM_SC_PRINT(AddInitialProperties, v.object) REALM_SC_PRINT(ChangePrimaryKey, v.object, v.property) REALM_SC_PRINT(ChangePropertyType, v.object, v.old_property, v.new_property) + REALM_SC_PRINT(ChangeRelationshipType, v.object, v.property) REALM_SC_PRINT(MakePropertyNullable, v.object, v.property) REALM_SC_PRINT(MakePropertyRequired, v.object, v.property) REALM_SC_PRINT(RemoveIndex, v.object, v.property)