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 "schema.hpp"
|
||||
|
||||
#include <realm/group.hpp>
|
||||
#include <realm/link_view.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;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::sort(begin(target_schema), end(target_schema), compare_by_name);
|
||||
|
||||
std::vector<ObjectSchemaValidationException> errors;
|
||||
for (auto &object_schema : target_schema) {
|
||||
auto matching_schema = std::lower_bound(begin(actual_schema), end(actual_schema), object_schema, compare_by_name);
|
||||
if (matching_schema == end(actual_schema) || matching_schema->name != object_schema.name) {
|
||||
auto matching_schema = actual_schema.find(object_schema);
|
||||
if (matching_schema == actual_schema.end()) {
|
||||
if (!allow_missing_tables) {
|
||||
errors.emplace_back(ObjectSchemaValidationException(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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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,
|
||||
ObjectSchema& target_schema,
|
||||
Schema const& schema) {
|
||||
ObjectSchema& target_schema) {
|
||||
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
|
||||
const Property *primary = nullptr;
|
||||
for (auto& current_prop : table_schema.properties) {
|
||||
auto target_prop = target_schema.property_for_name(current_prop.name);
|
||||
|
||||
|
@ -198,40 +186,6 @@ std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(O
|
|||
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
|
||||
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) {
|
||||
for (auto const& target_schema : schema) {
|
||||
auto matching_schema = std::lower_bound(begin(old_schema), end(old_schema), target_schema, compare_by_name);
|
||||
if (matching_schema == end(old_schema) || matching_schema->name != target_schema.name) {
|
||||
auto matching_schema = old_schema.find(target_schema);
|
||||
if (matching_schema == end(old_schema)) {
|
||||
// Table doesn't exist
|
||||
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 schema;
|
||||
std::vector<ObjectSchema> schema;
|
||||
for (size_t i = 0; i < group->size(); i++) {
|
||||
std::string object_type = object_type_for_table_name(group->get_table_name(i));
|
||||
if (object_type.length()) {
|
||||
schema.emplace_back(group, object_type);
|
||||
}
|
||||
}
|
||||
std::sort(begin(schema), end(schema), compare_by_name);
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
namespace realm {
|
||||
class ObjectSchemaValidationException;
|
||||
using Schema = std::vector<ObjectSchema>;
|
||||
class Schema;
|
||||
|
||||
class ObjectStore {
|
||||
public:
|
||||
|
@ -90,8 +90,7 @@ namespace realm {
|
|||
// updates the column mapping on the target_schema
|
||||
// returns array of validation errors
|
||||
static std::vector<ObjectSchemaValidationException> verify_object_schema(ObjectSchema const& expected,
|
||||
ObjectSchema &target_schema,
|
||||
Schema const& schema);
|
||||
ObjectSchema &target_schema);
|
||||
|
||||
// get primary key property name for 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 "realm_delegate.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "transact_log_handler.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)
|
||||
{
|
||||
if (&c != this) {
|
||||
|
@ -154,6 +157,7 @@ SharedRealm Realm::get_shared_realm(Config config)
|
|||
if (realm->m_config.schema_version == ObjectStore::NotVersioned) {
|
||||
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);
|
||||
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)
|
||||
{
|
||||
schema->validate();
|
||||
|
||||
bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema));
|
||||
if (!needs_update) {
|
||||
ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only);
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace realm {
|
|||
Config() = default;
|
||||
Config(Config&&) = default;
|
||||
Config(const Config& c);
|
||||
~Config();
|
||||
|
||||
Config& operator=(Config const&);
|
||||
Config& operator=(Config&&) = default;
|
||||
|
|
Loading…
Reference in New Issue