Add the linking objects property type.

This commit is contained in:
Mark Rowe 2016-04-05 18:07:12 -07:00 committed by Thomas Goyne
parent abca7c26e1
commit 152697d199
7 changed files with 112 additions and 17 deletions

View File

@ -22,10 +22,13 @@
#include "list.hpp" #include "list.hpp"
#include "object_schema.hpp" #include "object_schema.hpp"
#include "object_store.hpp" #include "object_store.hpp"
#include "results.hpp"
#include "schema.hpp" #include "schema.hpp"
#include "shared_realm.hpp" #include "shared_realm.hpp"
#include "util/format.hpp"
#include <realm/link_view.hpp> #include <realm/link_view.hpp>
#include <realm/table_view.hpp>
namespace realm { namespace realm {
@ -103,6 +106,8 @@ namespace realm {
static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index); static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index);
static ValueType from_list(ContextType ctx, List); static ValueType from_list(ContextType ctx, List);
static ValueType from_results(ContextType ctx, Results);
// //
// Deprecated // Deprecated
// //
@ -125,8 +130,15 @@ namespace realm {
const std::string property_name; const std::string property_name;
}; };
class MutationOutsideTransactionException : public std::runtime_error class ReadOnlyPropertyValueException : public std::runtime_error {
{ public:
ReadOnlyPropertyValueException(const std::string& object_type, const std::string& property_name, const std::string& message)
: std::runtime_error(message), object_type(object_type), property_name(property_name) {}
const std::string object_type;
const std::string property_name;
};
class MutationOutsideTransactionException : public std::runtime_error {
public: public:
MutationOutsideTransactionException(std::string message) : std::runtime_error(message) {} MutationOutsideTransactionException(std::string message) : std::runtime_error(message) {}
}; };
@ -217,6 +229,10 @@ namespace realm {
} }
break; break;
} }
case PropertyType::LinkingObjects:
throw ReadOnlyPropertyValueException(m_object_schema->name, property.name,
util::format("Cannot modify read-only property '%1.%2'",
m_object_schema->name, property.name));
} }
} }
@ -259,6 +275,14 @@ namespace realm {
auto arrayObjectSchema = m_realm->config().schema->find(property.object_type); auto arrayObjectSchema = m_realm->config().schema->find(property.object_type);
return Accessor::from_list(ctx, std::move(List(m_realm, *arrayObjectSchema, static_cast<LinkViewRef>(m_row.get_linklist(column))))); return Accessor::from_list(ctx, std::move(List(m_realm, *arrayObjectSchema, static_cast<LinkViewRef>(m_row.get_linklist(column)))));
} }
case PropertyType::LinkingObjects: {
auto target_object_schema = m_realm->config().schema->find(property.object_type);
auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name);
auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column);
Results results(m_realm, *m_object_schema, std::move(tv), {});
return Accessor::from_results(ctx, std::move(results));
}
} }
} }
@ -303,7 +327,7 @@ namespace realm {
// populate // populate
Object object(realm, object_schema, table->get(row_index)); Object object(realm, object_schema, table->get(row_index));
for (const Property &prop : object_schema.properties) { for (const Property& prop : object_schema.persisted_properties) {
if (created || !prop.is_primary) { if (created || !prop.is_primary) {
if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) {
object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update);

View File

@ -565,11 +565,24 @@ MissingPropertyException::MissingPropertyException(std::string const& object_typ
InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) : InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property) ObjectSchemaPropertyException(object_type, property)
{ {
if (property.type == PropertyType::Object) { switch (property.type) {
case PropertyType::Object:
m_what = util::format("'Object' property '%1' must be nullable.", property.name); m_what = util::format("'Object' property '%1' must be nullable.", property.name);
} break;
else { case PropertyType::Any:
m_what = util::format("Array or Mixed property '%1' cannot be nullable", property.name); case PropertyType::Array:
case PropertyType::LinkingObjects:
m_what = util::format("Property '%1' of type '%2' cannoy be nullable",
property.name, string_for_property_type(property.type));
break;
case PropertyType::Int:
case PropertyType::Bool:
case PropertyType::Data:
case PropertyType::Date:
case PropertyType::Float:
case PropertyType::Double:
case PropertyType::String:
REALM_ASSERT(false);
} }
} }
@ -627,3 +640,24 @@ DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const&
m_what = util::format("Duplicate primary keys for object '%1'.", object_type); m_what = util::format("Duplicate primary keys for object '%1'.", object_type);
} }
InvalidLinkingObjectsPropertyException::InvalidLinkingObjectsPropertyException(Type error_type, std::string const& object_type, Property const& property)
: ObjectSchemaPropertyException(object_type, property)
{
switch (error_type) {
case Type::OriginPropertyDoesNotExist:
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist",
property.object_type, property.link_origin_property_name,
object_type, property.name);
break;
case Type::OriginPropertyIsNotALink:
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link",
property.object_type, property.link_origin_property_name,
object_type, property.name);
break;
case Type::OriginPropertyInvalidLinkTarget:
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to a different class",
property.object_type, property.link_origin_property_name,
object_type, property.name);
break;
}
}

View File

@ -240,6 +240,16 @@ namespace realm {
private: private:
std::string m_primary_key; std::string m_primary_key;
}; };
class InvalidLinkingObjectsPropertyException : public ObjectSchemaPropertyException {
public:
enum class Type {
OriginPropertyDoesNotExist,
OriginPropertyIsNotALink,
OriginPropertyInvalidLinkTarget,
};
InvalidLinkingObjectsPropertyException(Type error_type, std::string const& object_type, Property const& property);
};
} }
#endif /* defined(REALM_OBJECT_STORE_HPP) */ #endif /* defined(REALM_OBJECT_STORE_HPP) */

View File

@ -33,12 +33,14 @@ namespace realm {
Date = 8, Date = 8,
Object = 12, Object = 12,
Array = 13, Array = 13,
LinkingObjects = 14,
}; };
struct Property { struct Property {
std::string name; std::string name;
PropertyType type; PropertyType type;
std::string object_type; std::string object_type;
std::string link_origin_property_name;
bool is_primary = false; bool is_primary = false;
bool is_indexed = false; bool is_indexed = false;
bool is_nullable = false; bool is_nullable = false;
@ -55,11 +57,13 @@ namespace realm {
#if __GNUC__ < 5 #if __GNUC__ < 5
// GCC 4.9 does not support C++14 braced-init with NSDMIs // GCC 4.9 does not support C++14 braced-init with NSDMIs
Property(std::string name="", PropertyType type=PropertyType::Int, std::string object_type="", Property(std::string name="", PropertyType type=PropertyType::Int,
std::string object_type="", std::string link_origin_property_name="",
bool is_primary=false, bool is_indexed=false, bool is_nullable=false) bool is_primary=false, bool is_indexed=false, bool is_nullable=false)
: name(std::move(name)) : name(std::move(name))
, type(type) , type(type)
, object_type(std::move(object_type)) , object_type(std::move(object_type))
, link_origin_property_name(std::move(link_origin_property_name))
, is_primary(is_primary) , is_primary(is_primary)
, is_indexed(is_indexed) , is_indexed(is_indexed)
, is_nullable(is_nullable) , is_nullable(is_nullable)

View File

@ -72,13 +72,36 @@ void Schema::validate() const
for (auto const& prop : all_properties) { for (auto const& prop : all_properties) {
// check object_type existence // check object_type existence
if (!prop.object_type.empty() && find(prop.object_type) == end()) { if (!prop.object_type.empty()) {
auto it = find(prop.object_type);
if (it == end()) {
exceptions.emplace_back(MissingObjectTypeException(object.name, prop)); exceptions.emplace_back(MissingObjectTypeException(object.name, prop));
} }
// validate linking objects property.
else if (!prop.link_origin_property_name.empty()) {
using ErrorType = InvalidLinkingObjectsPropertyException::Type;
util::Optional<ErrorType> error;
const Property *origin_property = it->property_for_name(prop.link_origin_property_name);
if (!origin_property) {
error = ErrorType::OriginPropertyDoesNotExist;
}
else if (origin_property->type != PropertyType::Object && origin_property->type != PropertyType::Array) {
error = ErrorType::OriginPropertyIsNotALink;
}
else if (origin_property->object_type != object.name) {
error = ErrorType::OriginPropertyInvalidLinkTarget;
}
if (error) {
exceptions.emplace_back(InvalidLinkingObjectsPropertyException(*error, object.name, prop));
}
}
}
// check nullablity // check nullablity
if (prop.is_nullable) { if (prop.is_nullable) {
if (prop.type == PropertyType::Array || prop.type == PropertyType::Any) { if (prop.type == PropertyType::Array || prop.type == PropertyType::Any || prop.type == PropertyType::LinkingObjects) {
exceptions.emplace_back(InvalidNullabilityException(object.name, prop)); exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
} }
} }

View File

@ -24,13 +24,13 @@ TEST_CASE("[results] notifications") {
config.schema = std::make_unique<Schema>(Schema{ config.schema = std::make_unique<Schema>(Schema{
{"object", "", { {"object", "", {
{"value", PropertyType::Int}, {"value", PropertyType::Int},
{"link", PropertyType::Object, "linked to object", false, false, true} {"link", PropertyType::Object, "linked to object", "", false, false, true}
}}, }},
{"other object", "", { {"other object", "", {
{"value", PropertyType::Int} {"value", PropertyType::Int}
}}, }},
{"linking object", "", { {"linking object", "", {
{"link", PropertyType::Object, "object", false, false, true} {"link", PropertyType::Object, "object", "", false, false, true}
}}, }},
{"linked to object", "", { {"linked to object", "", {
{"value", PropertyType::Int} {"value", PropertyType::Int}

View File

@ -114,7 +114,7 @@ TEST_CASE("Transaction log parsing") {
config.schema = std::make_unique<Schema>(Schema{ config.schema = std::make_unique<Schema>(Schema{
{"table", "", { {"table", "", {
{"unindexed", PropertyType::Int}, {"unindexed", PropertyType::Int},
{"indexed", PropertyType::Int, "", false, true} {"indexed", PropertyType::Int, "", "", false, true}
}}, }},
}); });
auto r = Realm::get_shared_realm(config); auto r = Realm::get_shared_realm(config);
@ -807,7 +807,7 @@ TEST_CASE("DeepChangeChecker") {
config.schema = std::make_unique<Schema>(Schema{ config.schema = std::make_unique<Schema>(Schema{
{"table", "", { {"table", "", {
{"int", PropertyType::Int}, {"int", PropertyType::Int},
{"link", PropertyType::Object, "table", false, false, true}, {"link", PropertyType::Object, "table", "", false, false, true},
{"array", PropertyType::Array, "table"} {"array", PropertyType::Array, "table"}
}}, }},
}); });