Add a Schema class, move lookup by name and internal-consistency checks there
This commit is contained in:
parent
5fa1ff21f3
commit
00d8bf4ef0
|
@ -0,0 +1,106 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2015 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "schema.hpp"
|
||||||
|
|
||||||
|
#include "object_schema.hpp"
|
||||||
|
#include "object_store.hpp"
|
||||||
|
#include "property.hpp"
|
||||||
|
|
||||||
|
using namespace realm;
|
||||||
|
|
||||||
|
static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) {
|
||||||
|
return lft.name < rgt.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::Schema(base types) : base(std::move(types)) {
|
||||||
|
std::sort(begin(), end(), compare_by_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::iterator Schema::find(std::string const& name)
|
||||||
|
{
|
||||||
|
ObjectSchema cmp;
|
||||||
|
cmp.name = name;
|
||||||
|
return find(cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::const_iterator Schema::find(std::string const& name) const
|
||||||
|
{
|
||||||
|
return const_cast<Schema *>(this)->find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::iterator Schema::find(ObjectSchema const& object) noexcept
|
||||||
|
{
|
||||||
|
auto it = std::lower_bound(begin(), end(), object, compare_by_name);
|
||||||
|
if (it != end() && it->name != object.name) {
|
||||||
|
it = end();
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept
|
||||||
|
{
|
||||||
|
return const_cast<Schema *>(this)->find(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schema::validate() const
|
||||||
|
{
|
||||||
|
std::vector<ObjectSchemaValidationException> exceptions;
|
||||||
|
for (auto const& object : *this) {
|
||||||
|
const Property *primary = nullptr;
|
||||||
|
for (auto const& prop : object.properties) {
|
||||||
|
// check object_type existence
|
||||||
|
if (!prop.object_type.empty() && find(prop.object_type) == end()) {
|
||||||
|
exceptions.emplace_back(MissingObjectTypeException(object.name, prop));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check nullablity
|
||||||
|
if (prop.is_nullable) {
|
||||||
|
#if REALM_NULL_STRINGS == 1
|
||||||
|
if (prop.type == PropertyTypeArray || prop.type == PropertyTypeAny) {
|
||||||
|
#else
|
||||||
|
if (prop.type != PropertyTypeObject) {
|
||||||
|
#endif
|
||||||
|
exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prop.type == PropertyTypeObject) {
|
||||||
|
exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check primary keys
|
||||||
|
if (prop.is_primary) {
|
||||||
|
if (primary) {
|
||||||
|
exceptions.emplace_back(DuplicatePrimaryKeysException(object.name));
|
||||||
|
}
|
||||||
|
primary = ∝
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indexable
|
||||||
|
if (prop.is_indexed) {
|
||||||
|
if (prop.type != PropertyTypeString && prop.type != PropertyTypeInt) {
|
||||||
|
exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptions.size()) {
|
||||||
|
throw SchemaValidationException(exceptions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2015 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef REALM_SCHEMA_HPP
|
||||||
|
#define REALM_SCHEMA_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace realm {
|
||||||
|
class ObjectSchema;
|
||||||
|
|
||||||
|
class Schema : private std::vector<ObjectSchema> {
|
||||||
|
private:
|
||||||
|
using base = std::vector<ObjectSchema>;
|
||||||
|
public:
|
||||||
|
// Create a schema from a vector of ObjectSchema
|
||||||
|
Schema(base types);
|
||||||
|
|
||||||
|
// find an ObjectSchema by name
|
||||||
|
iterator find(std::string const& name);
|
||||||
|
const_iterator find(std::string const& name) const;
|
||||||
|
|
||||||
|
// find an ObjectSchema with the same name as the passed in one
|
||||||
|
iterator find(ObjectSchema const& object) noexcept;
|
||||||
|
const_iterator find(ObjectSchema const& object) const noexcept;
|
||||||
|
|
||||||
|
// Verify that this schema is internally consistent (i.e. all properties are
|
||||||
|
// valid, links link to types that actually exist, etc.)
|
||||||
|
void validate() const;
|
||||||
|
|
||||||
|
using base::iterator;
|
||||||
|
using base::const_iterator;
|
||||||
|
using base::begin;
|
||||||
|
using base::end;
|
||||||
|
using base::empty;
|
||||||
|
using base::size;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(REALM_SCHEMA_HPP) */
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "object_store.hpp"
|
#include "object_store.hpp"
|
||||||
|
|
||||||
|
#include "schema.hpp"
|
||||||
|
|
||||||
#include <realm/group.hpp>
|
#include <realm/group.hpp>
|
||||||
#include <realm/link_view.hpp>
|
#include <realm/link_view.hpp>
|
||||||
#include <realm/table.hpp>
|
#include <realm/table.hpp>
|
||||||
|
@ -147,17 +149,11 @@ static inline bool property_has_changed(Property const& p1, Property const& p2)
|
||||||
|| p1.is_nullable != p2.is_nullable;
|
|| p1.is_nullable != p2.is_nullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) {
|
|
||||||
return lft.name < rgt.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) {
|
void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) {
|
||||||
std::sort(begin(target_schema), end(target_schema), compare_by_name);
|
|
||||||
|
|
||||||
std::vector<ObjectSchemaValidationException> errors;
|
std::vector<ObjectSchemaValidationException> errors;
|
||||||
for (auto &object_schema : target_schema) {
|
for (auto &object_schema : target_schema) {
|
||||||
auto matching_schema = std::lower_bound(begin(actual_schema), end(actual_schema), object_schema, compare_by_name);
|
auto matching_schema = actual_schema.find(object_schema);
|
||||||
if (matching_schema == end(actual_schema) || matching_schema->name != object_schema.name) {
|
if (matching_schema == actual_schema.end()) {
|
||||||
if (!allow_missing_tables) {
|
if (!allow_missing_tables) {
|
||||||
errors.emplace_back(ObjectSchemaValidationException(object_schema.name,
|
errors.emplace_back(ObjectSchemaValidationException(object_schema.name,
|
||||||
"Missing table for object type '" + object_schema.name + "'."));
|
"Missing table for object type '" + object_schema.name + "'."));
|
||||||
|
@ -165,7 +161,7 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_sche
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto more_errors = verify_object_schema(*matching_schema, object_schema, target_schema);
|
auto more_errors = verify_object_schema(*matching_schema, object_schema);
|
||||||
errors.insert(errors.end(), more_errors.begin(), more_errors.end());
|
errors.insert(errors.end(), more_errors.begin(), more_errors.end());
|
||||||
}
|
}
|
||||||
if (errors.size()) {
|
if (errors.size()) {
|
||||||
|
@ -174,18 +170,10 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_sche
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(ObjectSchema const& table_schema,
|
std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(ObjectSchema const& table_schema,
|
||||||
ObjectSchema& target_schema,
|
ObjectSchema& target_schema) {
|
||||||
Schema const& schema) {
|
|
||||||
std::vector<ObjectSchemaValidationException> exceptions;
|
std::vector<ObjectSchemaValidationException> exceptions;
|
||||||
|
|
||||||
ObjectSchema cmp;
|
|
||||||
auto schema_contains_table = [&](std::string const& name) {
|
|
||||||
cmp.name = name;
|
|
||||||
return std::binary_search(begin(schema), end(schema), cmp, compare_by_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
// check to see if properties are the same
|
// check to see if properties are the same
|
||||||
const Property *primary = nullptr;
|
|
||||||
for (auto& current_prop : table_schema.properties) {
|
for (auto& current_prop : table_schema.properties) {
|
||||||
auto target_prop = target_schema.property_for_name(current_prop.name);
|
auto target_prop = target_schema.property_for_name(current_prop.name);
|
||||||
|
|
||||||
|
@ -198,40 +186,6 @@ std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(O
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check object_type existence
|
|
||||||
if (current_prop.object_type.length() && !schema_contains_table(current_prop.object_type)) {
|
|
||||||
exceptions.emplace_back(MissingObjectTypeException(table_schema.name, current_prop));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check nullablity
|
|
||||||
if (current_prop.is_nullable) {
|
|
||||||
#if REALM_NULL_STRINGS == 1
|
|
||||||
if (current_prop.type == PropertyTypeArray || current_prop.type == PropertyTypeAny) {
|
|
||||||
#else
|
|
||||||
if (current_prop.type != PropertyTypeObject) {
|
|
||||||
#endif
|
|
||||||
exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (current_prop.type == PropertyTypeObject) {
|
|
||||||
exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check primary keys
|
|
||||||
if (current_prop.is_primary) {
|
|
||||||
if (primary) {
|
|
||||||
exceptions.emplace_back(DuplicatePrimaryKeysException(table_schema.name));
|
|
||||||
}
|
|
||||||
primary = ¤t_prop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check indexable
|
|
||||||
if (current_prop.is_indexed) {
|
|
||||||
if (current_prop.type != PropertyTypeString && current_prop.type != PropertyTypeInt) {
|
|
||||||
exceptions.emplace_back(PropertyTypeNotIndexableException(table_schema.name, current_prop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new property with aligned column
|
// create new property with aligned column
|
||||||
target_prop->table_column = current_prop.table_column;
|
target_prop->table_column = current_prop.table_column;
|
||||||
}
|
}
|
||||||
|
@ -347,8 +301,8 @@ bool ObjectStore::is_schema_at_version(const Group *group, uint64_t version) {
|
||||||
|
|
||||||
bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) {
|
bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) {
|
||||||
for (auto const& target_schema : schema) {
|
for (auto const& target_schema : schema) {
|
||||||
auto matching_schema = std::lower_bound(begin(old_schema), end(old_schema), target_schema, compare_by_name);
|
auto matching_schema = old_schema.find(target_schema);
|
||||||
if (matching_schema == end(old_schema) || matching_schema->name != target_schema.name) {
|
if (matching_schema == end(old_schema)) {
|
||||||
// Table doesn't exist
|
// Table doesn't exist
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -405,14 +359,13 @@ bool ObjectStore::update_realm_with_schema(Group *group, Schema const& old_schem
|
||||||
}
|
}
|
||||||
|
|
||||||
Schema ObjectStore::schema_from_group(const Group *group) {
|
Schema ObjectStore::schema_from_group(const Group *group) {
|
||||||
Schema schema;
|
std::vector<ObjectSchema> schema;
|
||||||
for (size_t i = 0; i < group->size(); i++) {
|
for (size_t i = 0; i < group->size(); i++) {
|
||||||
std::string object_type = object_type_for_table_name(group->get_table_name(i));
|
std::string object_type = object_type_for_table_name(group->get_table_name(i));
|
||||||
if (object_type.length()) {
|
if (object_type.length()) {
|
||||||
schema.emplace_back(group, object_type);
|
schema.emplace_back(group, object_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::sort(begin(schema), end(schema), compare_by_name);
|
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
class ObjectSchemaValidationException;
|
class ObjectSchemaValidationException;
|
||||||
using Schema = std::vector<ObjectSchema>;
|
class Schema;
|
||||||
|
|
||||||
class ObjectStore {
|
class ObjectStore {
|
||||||
public:
|
public:
|
||||||
|
@ -90,8 +90,7 @@ namespace realm {
|
||||||
// updates the column mapping on the target_schema
|
// updates the column mapping on the target_schema
|
||||||
// returns array of validation errors
|
// returns array of validation errors
|
||||||
static std::vector<ObjectSchemaValidationException> verify_object_schema(ObjectSchema const& expected,
|
static std::vector<ObjectSchemaValidationException> verify_object_schema(ObjectSchema const& expected,
|
||||||
ObjectSchema &target_schema,
|
ObjectSchema &target_schema);
|
||||||
Schema const& schema);
|
|
||||||
|
|
||||||
// get primary key property name for object type
|
// get primary key property name for object type
|
||||||
static StringData get_primary_key_for_object(const Group *group, StringData object_type);
|
static StringData get_primary_key_for_object(const Group *group, StringData object_type);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "external_commit_helper.hpp"
|
#include "external_commit_helper.hpp"
|
||||||
#include "realm_delegate.hpp"
|
#include "realm_delegate.hpp"
|
||||||
|
#include "schema.hpp"
|
||||||
#include "transact_log_handler.hpp"
|
#include "transact_log_handler.hpp"
|
||||||
|
|
||||||
#include <realm/commit_log.hpp>
|
#include <realm/commit_log.hpp>
|
||||||
|
@ -45,6 +46,8 @@ Realm::Config::Config(const Config& c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Realm::Config::~Config() = default;
|
||||||
|
|
||||||
Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c)
|
Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c)
|
||||||
{
|
{
|
||||||
if (&c != this) {
|
if (&c != this) {
|
||||||
|
@ -154,6 +157,7 @@ SharedRealm Realm::get_shared_realm(Config config)
|
||||||
if (realm->m_config.schema_version == ObjectStore::NotVersioned) {
|
if (realm->m_config.schema_version == ObjectStore::NotVersioned) {
|
||||||
throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema");
|
throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema");
|
||||||
}
|
}
|
||||||
|
target_schema->validate();
|
||||||
ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true);
|
ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true);
|
||||||
realm->m_config.schema = std::move(target_schema);
|
realm->m_config.schema = std::move(target_schema);
|
||||||
}
|
}
|
||||||
|
@ -171,6 +175,8 @@ SharedRealm Realm::get_shared_realm(Config config)
|
||||||
|
|
||||||
bool Realm::update_schema(std::unique_ptr<Schema> schema, uint64_t version)
|
bool Realm::update_schema(std::unique_ptr<Schema> schema, uint64_t version)
|
||||||
{
|
{
|
||||||
|
schema->validate();
|
||||||
|
|
||||||
bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema));
|
bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema));
|
||||||
if (!needs_update) {
|
if (!needs_update) {
|
||||||
ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only);
|
ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only);
|
||||||
|
|
|
@ -56,6 +56,7 @@ namespace realm {
|
||||||
Config() = default;
|
Config() = default;
|
||||||
Config(Config&&) = default;
|
Config(Config&&) = default;
|
||||||
Config(const Config& c);
|
Config(const Config& c);
|
||||||
|
~Config();
|
||||||
|
|
||||||
Config& operator=(Config const&);
|
Config& operator=(Config const&);
|
||||||
Config& operator=(Config&&) = default;
|
Config& operator=(Config&&) = default;
|
||||||
|
|
Loading…
Reference in New Issue