Add a Schema class, move lookup by name and internal-consistency checks there

This commit is contained in:
Thomas Goyne 2015-09-04 14:03:58 -07:00 committed by Ari Lazier
parent 5fa1ff21f3
commit 00d8bf4ef0
6 changed files with 179 additions and 59 deletions

106
schema.cpp Normal file
View File

@ -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 = &prop;
}
// 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);
}
}

55
schema.hpp Normal file
View File

@ -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) */

View File

@ -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 = &current_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;
}

View File

@ -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);

View File

@ -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);

View File

@ -56,6 +56,7 @@ namespace realm {
Config() = default;
Config(Config&&) = default;
Config(const Config& c);
~Config();
Config& operator=(Config const&);
Config& operator=(Config&&) = default;