cleaup schema initialization

This commit is contained in:
Ari Lazier 2015-07-28 11:25:04 -07:00
parent e9379491d6
commit 373375fa1b
5 changed files with 70 additions and 43 deletions

View File

@ -43,8 +43,6 @@ namespace realm {
Property *primary_key_property() { Property *primary_key_property() {
return property_for_name(primary_key); return property_for_name(primary_key);
} }
void validate();
}; };
} }

View File

@ -140,7 +140,26 @@ static inline bool property_has_changed(Property &p1, Property &p2) {
return p1.type != p2.type || p1.name != p2.name || p1.object_type != p2.object_type || p1.is_nullable != p2.is_nullable; return p1.type != p2.type || p1.name != p2.name || p1.object_type != p2.object_type || p1.is_nullable != p2.is_nullable;
} }
std::vector<ObjectSchemaValidationException> ObjectStore::validate_object_schema(Group *group, ObjectSchema &target_schema) { void ObjectStore::verify_schema(Group *group, Schema &target_schema, bool allow_missing_tables) {
std::vector<ObjectSchemaValidationException> errors;
for (auto &object_schema : target_schema) {
if (!table_for_object_type(group, object_schema.first)) {
if (!allow_missing_tables) {
errors.emplace_back(ObjectSchemaValidationException(object_schema.first,
"Missing table for object type '" + object_schema.first + "'."));
}
continue;
}
auto more_errors = verify_object_schema(group, object_schema.second, target_schema);
errors.insert(errors.end(), more_errors.begin(), more_errors.end());
}
if (errors.size()) {
throw SchemaValidationException(errors);
}
}
std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(Group *group, ObjectSchema &target_schema, Schema &schema) {
std::vector<ObjectSchemaValidationException> exceptions; std::vector<ObjectSchemaValidationException> exceptions;
ObjectSchema table_schema(group, target_schema.name); ObjectSchema table_schema(group, target_schema.name);
@ -156,6 +175,10 @@ std::vector<ObjectSchemaValidationException> ObjectStore::validate_object_schema
exceptions.emplace_back(MismatchedPropertiesException(table_schema.name, current_prop, *target_prop)); exceptions.emplace_back(MismatchedPropertiesException(table_schema.name, current_prop, *target_prop));
continue; continue;
} }
if (current_prop.object_type.length() && schema.find(current_prop.object_type) == schema.end()) {
exceptions.emplace_back(InvalidPropertyException(table_schema.name, current_prop,
"Target type '" + current_prop.object_type + "' doesn't exist for property '" + current_prop.name + "',"));
}
// 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;
@ -300,12 +323,7 @@ bool ObjectStore::update_realm_with_schema(Group *group,
bool changed = create_metadata_tables(group); bool changed = create_metadata_tables(group);
changed = create_tables(group, schema, migrating) || changed; changed = create_tables(group, schema, migrating) || changed;
for (auto& target_schema : schema) { verify_schema(group, schema);
auto errors = validate_object_schema(group, target_schema.second);
if (errors.size()) {
throw ObjectSchemaValidationException(target_schema.first, errors);
}
}
changed = update_indexes(group, schema) || changed; changed = update_indexes(group, schema) || changed;
@ -419,10 +437,10 @@ DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string
}; };
ObjectSchemaValidationException::ObjectSchemaValidationException(std::string object_type, std::vector<ObjectSchemaValidationException> errors) : SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> errors) :
m_object_type(object_type), m_validation_errors(errors) m_validation_errors(errors)
{ {
m_what ="Migration is required for object type '" + object_type + "' due to the following errors: "; m_what ="Migration is required due to the following errors: ";
for (auto error : errors) { for (auto error : errors) {
m_what += std::string("\n- ") + error.what(); m_what += std::string("\n- ") + error.what();
} }

View File

@ -43,11 +43,10 @@ namespace realm {
// checks if the schema in the group is at the given version // checks if the schema in the group is at the given version
static bool is_schema_at_version(realm::Group *group, uint64_t version); static bool is_schema_at_version(realm::Group *group, uint64_t version);
// verify a target schema against its table, setting the table_column property on each schema object // verify a target schema against tables in the given group
// updates the column mapping on the target_schema // updates the column mapping on all ObjectSchema properties
// if no table is provided it is fetched from the group // throws if the schema is invalid or does not match tables in the given group
// returns array of validation errors static void verify_schema(Group *group, Schema &target_schema, bool allow_missing_tables = false);
static std::vector<ObjectSchemaValidationException> validate_object_schema(Group *group, ObjectSchema &target_schema);
// updates the target_column member for all properties based on the column indexes in the passed in group // updates the target_column member for all properties based on the column indexes in the passed in group
static void update_column_mapping(Group *group, ObjectSchema &target_schema); static void update_column_mapping(Group *group, ObjectSchema &target_schema);
@ -90,6 +89,11 @@ namespace realm {
// if update existing is true, updates existing tables, otherwise only adds and initializes new tables // if update existing is true, updates existing tables, otherwise only adds and initializes new tables
static bool create_tables(realm::Group *group, Schema &target_schema, bool update_existing); static bool create_tables(realm::Group *group, Schema &target_schema, bool update_existing);
// verify a target schema against its table, setting the table_column property on each schema object
// updates the column mapping on the target_schema
// returns array of validation errors
static std::vector<ObjectSchemaValidationException> verify_object_schema(Group *group, ObjectSchema &target_schema, Schema &schema);
// get primary key property name for object type // get primary key property name for object type
static StringData get_primary_key_for_object(Group *group, StringData object_type); static StringData get_primary_key_for_object(Group *group, StringData object_type);
@ -142,12 +146,18 @@ namespace realm {
}; };
// Schema validation exceptions // Schema validation exceptions
class SchemaValidationException : public ObjectStoreException {
public:
SchemaValidationException(std::vector<ObjectSchemaValidationException> errors);
private:
std::vector<ObjectSchemaValidationException> m_validation_errors;
};
class ObjectSchemaValidationException : public ObjectStoreException { class ObjectSchemaValidationException : public ObjectStoreException {
public: public:
ObjectSchemaValidationException(std::string object_type) : m_object_type(object_type) {} ObjectSchemaValidationException(std::string object_type) : m_object_type(object_type) {}
ObjectSchemaValidationException(std::string object_type, std::vector<ObjectSchemaValidationException> errors); ObjectSchemaValidationException(std::string object_type, std::string message) :
private: m_object_type(object_type) { m_what = message; }
std::vector<ObjectSchemaValidationException> m_validation_errors;
protected: protected:
std::string m_object_type; std::string m_object_type;
}; };
@ -193,6 +203,14 @@ namespace realm {
private: private:
std::string m_primary; std::string m_primary;
}; };
class InvalidPropertyException : public ObjectSchemaValidationException {
public:
InvalidPropertyException(std::string object_type, Property &property, std::string message) :
ObjectSchemaValidationException(object_type), m_property(property) { m_what = message; }
private:
Property m_property;
};
} }
#endif /* defined(REALM_OBJECT_STORE_HPP) */ #endif /* defined(REALM_OBJECT_STORE_HPP) */

View File

@ -107,23 +107,17 @@ SharedRealm Realm::get_shared_realm(Config &config)
// we want to ensure we are only initializing a single realm at a time // we want to ensure we are only initializing a single realm at a time
std::lock_guard<std::mutex> lock(s_init_mutex); std::lock_guard<std::mutex> lock(s_init_mutex);
if (!config.schema) { uint64_t old_version = ObjectStore::get_schema_version(realm->read_group());
uint64_t version = ObjectStore::get_schema_version(realm->read_group()); if (!realm->m_config.schema) {
if (version == ObjectStore::NotVersioned) {
InvalidSchemaVersionException(ObjectStore::NotVersioned, ObjectStore::NotVersioned);
}
// get schema from group and skip validation // get schema from group and skip validation
realm->m_config.schema_version = version; realm->m_config.schema_version = old_version;
realm->m_config.schema = std::make_unique<Schema>(ObjectStore::schema_from_group(realm->read_group())); realm->m_config.schema = std::make_unique<Schema>(ObjectStore::schema_from_group(realm->read_group()));
} }
else if (config.read_only) { else if (realm->m_config.read_only) {
// for read-only validate all existing tables if (old_version == ObjectStore::NotVersioned) {
for (auto &object_schema : *realm->m_config.schema) { throw UnitializedRealmException("Can't open an un-initizliazed Realm without a Schema");
if (ObjectStore::table_for_object_type(realm->read_group(), object_schema.first)) {
ObjectStore::validate_object_schema(realm->read_group(), object_schema.second);
}
} }
ObjectStore::verify_schema(realm->read_group(), *realm->m_config.schema, true);
} }
else if(auto existing = s_global_cache.get_any_realm(realm->config().path)) { else if(auto existing = s_global_cache.get_any_realm(realm->config().path)) {
// if there is an existing realm at the current path steal its schema/column mapping // if there is an existing realm at the current path steal its schema/column mapping
@ -132,7 +126,7 @@ SharedRealm Realm::get_shared_realm(Config &config)
} }
else { else {
// its a non-cached realm so update/migrate if needed // its a non-cached realm so update/migrate if needed
realm->update_schema(*realm->m_config.schema, config.schema_version); realm->update_schema(*realm->m_config.schema, realm->m_config.schema_version);
} }
s_global_cache.cache_realm(realm, realm->m_thread_id); s_global_cache.cache_realm(realm, realm->m_thread_id);
@ -151,7 +145,7 @@ bool Realm::update_schema(Schema &schema, uint64_t version)
m_config.schema_version = version; m_config.schema_version = version;
try { try {
if (ObjectStore::realm_requires_update(read_group(), version, schema)) { if (!m_config.read_only && ObjectStore::realm_requires_update(read_group(), version, schema)) {
// keep old copy to pass to migration function // keep old copy to pass to migration function
old_config.read_only = true; old_config.read_only = true;
SharedRealm old_realm = SharedRealm(new Realm(old_config)), updated_realm = shared_from_this(); SharedRealm old_realm = SharedRealm(new Realm(old_config)), updated_realm = shared_from_this();
@ -164,13 +158,7 @@ bool Realm::update_schema(Schema &schema, uint64_t version)
commit_transaction(); commit_transaction();
} }
else { else {
// verify all types ObjectStore::verify_schema(read_group(), *m_config.schema, m_config.read_only);
for (auto& target_schema : *m_config.schema) {
auto errors = ObjectStore::validate_object_schema(read_group(), target_schema.second);
if (errors.size()) {
throw ObjectSchemaValidationException(target_schema.first, errors);
}
}
} }
} }
catch (...) { catch (...) {
@ -409,4 +397,3 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id)
} }
} }

View File

@ -183,6 +183,12 @@ namespace realm {
public: public:
IncorrectThreadException(std::string message) : std::runtime_error(message) {} IncorrectThreadException(std::string message) : std::runtime_error(message) {}
}; };
class UnitializedRealmException : public std::runtime_error
{
public:
UnitializedRealmException(std::string message) : std::runtime_error(message) {}
};
} }
#endif /* defined(REALM_REALM_HPP) */ #endif /* defined(REALM_REALM_HPP) */