Add the linking objects property type.
This commit is contained in:
parent
abca7c26e1
commit
152697d199
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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) */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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"}
|
||||||
}},
|
}},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue