update to newest object store apis
This commit is contained in:
parent
eb2b079e2a
commit
94c7ea512f
|
@ -19,8 +19,8 @@
|
|||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
|
||||
#include <realm/group.hpp>
|
||||
#include <realm/table.hpp>
|
||||
#include <realm/group_shared.hpp>
|
||||
#include <realm/link_view.hpp>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
|
@ -36,11 +36,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) {
|
|||
property.type = (PropertyType)table->get_column_type(col);
|
||||
property.is_indexed = table->has_search_index(col);
|
||||
property.is_primary = false;
|
||||
#ifdef REALM_ENABLE_NULL
|
||||
property.is_nullable = table->is_nullable(col) || property.type == PropertyTypeObject;
|
||||
#else
|
||||
property.is_nullable = property.type == PropertyTypeObject;
|
||||
#endif
|
||||
property.table_column = col;
|
||||
if (property.type == PropertyTypeObject || property.type == PropertyTypeArray) {
|
||||
// set link type for objects and arrays
|
||||
|
@ -54,7 +50,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) {
|
|||
if (primary_key.length()) {
|
||||
auto primary_key_prop = primary_key_property();
|
||||
if (!primary_key_prop) {
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey, name, primary_key);
|
||||
throw InvalidPrimaryKeyException(name, primary_key);
|
||||
}
|
||||
primary_key_prop->is_primary = true;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ namespace realm {
|
|||
Property *primary_key_property() {
|
||||
return property_for_name(primary_key);
|
||||
}
|
||||
|
||||
void validate();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
233
object_store.cpp
233
object_store.cpp
|
@ -28,30 +28,6 @@
|
|||
|
||||
using namespace realm;
|
||||
|
||||
template <typename T>
|
||||
static inline
|
||||
T get_value(TableRef table, size_t row, size_t column);
|
||||
|
||||
template <>
|
||||
StringData get_value(TableRef table, size_t row, size_t column) {
|
||||
return table->get_string(column, row);
|
||||
}
|
||||
|
||||
static inline
|
||||
void set_value(TableRef table, size_t row, size_t column, StringData value) {
|
||||
table->set_string(column, row, value);
|
||||
}
|
||||
|
||||
template <>
|
||||
BinaryData get_value(TableRef table, size_t row, size_t column) {
|
||||
return table->get_binary(column, row);
|
||||
}
|
||||
|
||||
static inline
|
||||
void set_value(TableRef table, size_t row, size_t column, BinaryData value) {
|
||||
table->set_binary(column, row, value);
|
||||
}
|
||||
|
||||
const char * const c_metadataTableName = "metadata";
|
||||
const char * const c_versionColumnName = "version";
|
||||
const size_t c_versionColumnIndex = 0;
|
||||
|
@ -160,8 +136,12 @@ TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, const
|
|||
return group->get_or_add_table(table_name_for_object_type(object_type), &created);
|
||||
}
|
||||
|
||||
std::vector<ObjectStoreException> ObjectStore::validate_object_schema(Group *group, ObjectSchema &target_schema) {
|
||||
std::vector<ObjectStoreException> exceptions;
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<ObjectSchemaValidationException> ObjectStore::validate_object_schema(Group *group, ObjectSchema &target_schema) {
|
||||
std::vector<ObjectSchemaValidationException> exceptions;
|
||||
ObjectSchema table_schema(group, target_schema.name);
|
||||
|
||||
// check to see if properties are the same
|
||||
|
@ -169,30 +149,13 @@ std::vector<ObjectStoreException> ObjectStore::validate_object_schema(Group *gro
|
|||
auto target_prop = target_schema.property_for_name(current_prop.name);
|
||||
|
||||
if (!target_prop) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMissingProperty,
|
||||
table_schema.name, current_prop));
|
||||
exceptions.emplace_back(MissingPropertyException(table_schema.name, current_prop));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_prop.type != target_prop->type) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedTypes,
|
||||
table_schema.name, current_prop, *target_prop));
|
||||
if (property_has_changed(current_prop, *target_prop)) {
|
||||
exceptions.emplace_back(MismatchedPropertiesException(table_schema.name, current_prop, *target_prop));
|
||||
continue;
|
||||
}
|
||||
if (current_prop.type == PropertyTypeObject || target_prop->type == PropertyTypeArray) {
|
||||
if (current_prop.object_type != target_prop->object_type) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedObjectTypes,
|
||||
table_schema.name, current_prop, *target_prop));
|
||||
}
|
||||
}
|
||||
if (current_prop.is_nullable != target_prop->is_nullable) {
|
||||
if (current_prop.is_nullable) {
|
||||
exceptions.emplace_back(ObjectStoreException::Kind::ObjectSchemaChangedOptionalProperty, table_schema.name, current_prop, *target_prop);
|
||||
}
|
||||
else {
|
||||
exceptions.emplace_back(ObjectStoreException::Kind::ObjectSchemaNewOptionalProperty, table_schema.name, current_prop, *target_prop);
|
||||
}
|
||||
}
|
||||
|
||||
// create new property with aligned column
|
||||
target_prop->table_column = current_prop.table_column;
|
||||
|
@ -200,21 +163,13 @@ std::vector<ObjectStoreException> ObjectStore::validate_object_schema(Group *gro
|
|||
|
||||
// check for change to primary key
|
||||
if (table_schema.primary_key != target_schema.primary_key) {
|
||||
if (table_schema.primary_key.length()) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey,
|
||||
table_schema.name, table_schema.primary_key));
|
||||
}
|
||||
else {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaNewPrimaryKey,
|
||||
target_schema.name, target_schema.primary_key));
|
||||
}
|
||||
exceptions.emplace_back(ChangedPrimaryKeyException(table_schema.name, table_schema.primary_key, target_schema.primary_key));
|
||||
}
|
||||
|
||||
// check for new missing properties
|
||||
for (auto& target_prop : target_schema.properties) {
|
||||
if (!table_schema.property_for_name(target_prop.name)) {
|
||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaNewProperty,
|
||||
table_schema.name, target_prop));
|
||||
exceptions.emplace_back(ExtraPropertyException(table_schema.name, target_prop));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,41 +186,21 @@ void ObjectStore::update_column_mapping(Group *group, ObjectSchema &target_schem
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool property_can_be_migrated_to_nullable(Property &old_property, Property &new_property) {
|
||||
return old_property.type == new_property.type &&
|
||||
!old_property.is_nullable && new_property.is_nullable &&
|
||||
new_property.name == old_property.name;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void copy_property_to_property(Property &old_property, Property &new_property, TableRef table) {
|
||||
size_t old_column = old_property.table_column, new_column = new_property.table_column;
|
||||
size_t count = table->size();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
T old_value = get_value<T>(table, i, old_column);
|
||||
set_value(table, i, new_column, old_value);
|
||||
}
|
||||
}
|
||||
|
||||
// set references to tables on targetSchema and create/update any missing or out-of-date tables
|
||||
// if update existing is true, updates existing tables, otherwise validates existing tables
|
||||
// NOTE: must be called from within write transaction
|
||||
bool ObjectStore::create_tables(Group *group, ObjectStore::Schema &target_schema, bool update_existing) {
|
||||
bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update_existing) {
|
||||
bool changed = false;
|
||||
|
||||
// first pass to create missing tables
|
||||
std::vector<ObjectSchema *> to_update;
|
||||
for (auto& object_schema : target_schema) {
|
||||
bool created = false;
|
||||
ObjectStore::table_for_object_type_create_if_needed(group, object_schema.name, created);
|
||||
ObjectStore::table_for_object_type_create_if_needed(group, object_schema.first, created);
|
||||
|
||||
// we will modify tables for any new objectSchema (table was created) or for all if update_existing is true
|
||||
if (update_existing || created) {
|
||||
to_update.push_back(&object_schema);
|
||||
to_update.push_back(&object_schema.second);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -291,27 +226,10 @@ bool ObjectStore::create_tables(Group *group, ObjectStore::Schema &target_schema
|
|||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef REALM_ENABLE_NULL
|
||||
target_prop.table_column = table->add_column(DataType(target_prop.type), target_prop.name, target_prop.is_nullable);
|
||||
#else
|
||||
target_prop.table_column = table->add_column(DataType(target_prop.type), target_prop.name);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (current_prop && property_can_be_migrated_to_nullable(*current_prop, target_prop)) {
|
||||
switch (target_prop.type) {
|
||||
case PropertyTypeString:
|
||||
copy_property_to_property<StringData>(*current_prop, target_prop, table);
|
||||
break;
|
||||
case PropertyTypeData:
|
||||
copy_property_to_property<BinaryData>(*current_prop, target_prop, table);
|
||||
break;
|
||||
default:
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -348,11 +266,26 @@ bool ObjectStore::create_tables(Group *group, ObjectStore::Schema &target_schema
|
|||
bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) {
|
||||
uint64_t old_version = get_schema_version(group);
|
||||
if (old_version > version && old_version != NotVersioned) {
|
||||
throw ObjectStoreException(old_version, version);
|
||||
throw InvalidSchemaVersionException(old_version, version);
|
||||
}
|
||||
return old_version == version;
|
||||
}
|
||||
|
||||
bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema &schema) {
|
||||
if (!is_schema_at_version(group, version)) {
|
||||
return true;
|
||||
}
|
||||
for (auto& target_schema : schema) {
|
||||
TableRef table = table_for_object_type(group, target_schema.first);
|
||||
if (!table) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!indexes_are_up_to_date(group, schema)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectStore::update_realm_with_schema(Group *group,
|
||||
uint64_t version,
|
||||
|
@ -368,13 +301,9 @@ bool ObjectStore::update_realm_with_schema(Group *group,
|
|||
changed = create_tables(group, schema, migrating) || changed;
|
||||
|
||||
for (auto& target_schema : schema) {
|
||||
// read-only realms may be missing tables entirely
|
||||
TableRef table = table_for_object_type(group, target_schema.name);
|
||||
if (table) {
|
||||
auto errors = validate_object_schema(group, target_schema);
|
||||
if (errors.size()) {
|
||||
throw ObjectStoreException(errors, target_schema.name);
|
||||
}
|
||||
auto errors = validate_object_schema(group, target_schema.second);
|
||||
if (errors.size()) {
|
||||
throw ObjectSchemaValidationException(target_schema.first, errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,12 +324,12 @@ bool ObjectStore::update_realm_with_schema(Group *group,
|
|||
return true;
|
||||
}
|
||||
|
||||
ObjectStore::Schema ObjectStore::schema_from_group(Group *group) {
|
||||
ObjectStore::Schema schema;
|
||||
Schema ObjectStore::schema_from_group(Group *group) {
|
||||
Schema 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, move(object_type));
|
||||
schema.emplace(object_type, std::move(ObjectSchema(group, object_type)));
|
||||
}
|
||||
}
|
||||
return schema;
|
||||
|
@ -408,13 +337,13 @@ ObjectStore::Schema ObjectStore::schema_from_group(Group *group) {
|
|||
|
||||
bool ObjectStore::indexes_are_up_to_date(Group *group, Schema &schema) {
|
||||
for (auto &object_schema : schema) {
|
||||
TableRef table = table_for_object_type(group, object_schema.name);
|
||||
TableRef table = table_for_object_type(group, object_schema.first);
|
||||
if (!table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
update_column_mapping(group, object_schema);
|
||||
for (auto& property : object_schema.properties) {
|
||||
update_column_mapping(group, object_schema.second);
|
||||
for (auto& property : object_schema.second.properties) {
|
||||
if (property.requires_index() != table->has_search_index(property.table_column)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -426,12 +355,12 @@ bool ObjectStore::indexes_are_up_to_date(Group *group, Schema &schema) {
|
|||
bool ObjectStore::update_indexes(Group *group, Schema &schema) {
|
||||
bool changed = false;
|
||||
for (auto& object_schema : schema) {
|
||||
TableRef table = table_for_object_type(group, object_schema.name);
|
||||
TableRef table = table_for_object_type(group, object_schema.first);
|
||||
if (!table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& property : object_schema.properties) {
|
||||
for (auto& property : object_schema.second.properties) {
|
||||
if (property.requires_index() == table->has_search_index(property.table_column)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -442,7 +371,7 @@ bool ObjectStore::update_indexes(Group *group, Schema &schema) {
|
|||
table->add_search_index(property.table_column);
|
||||
}
|
||||
catch (LogicError const&) {
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::RealmPropertyTypeNotIndexable, object_schema.name, property);
|
||||
throw PropertyTypeNotIndexableException(object_schema.first, property);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -455,14 +384,14 @@ bool ObjectStore::update_indexes(Group *group, Schema &schema) {
|
|||
|
||||
void ObjectStore::validate_primary_column_uniqueness(Group *group, Schema &schema) {
|
||||
for (auto& object_schema : schema) {
|
||||
auto primary_prop = object_schema.primary_key_property();
|
||||
auto primary_prop = object_schema.second.primary_key_property();
|
||||
if (!primary_prop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TableRef table = table_for_object_type(group, object_schema.name);
|
||||
TableRef table = table_for_object_type(group, object_schema.first);
|
||||
if (table->get_distinct_view(primary_prop->table_column).size() != table->size()) {
|
||||
throw ObjectStoreException(ObjectStoreException::Kind::RealmDuplicatePrimaryKeyValue, object_schema.name, *primary_prop);
|
||||
throw DuplicatePrimaryKeyValueException(object_schema.first, *primary_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,3 +404,75 @@ void ObjectStore::delete_data_for_object(Group *group, const StringData &object_
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) :
|
||||
m_old_version(old_version), m_new_version(new_version)
|
||||
{
|
||||
m_what = "Provided schema version " + std::to_string(old_version) + " is less than last set version " + std::to_string(new_version) + ".";
|
||||
}
|
||||
|
||||
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, Property &property) :
|
||||
m_object_type(object_type), m_property(property)
|
||||
{
|
||||
m_what = "Primary key property '" + property.name + "' has duplicate values after migration.";
|
||||
};
|
||||
|
||||
|
||||
ObjectSchemaValidationException::ObjectSchemaValidationException(std::string object_type, std::vector<ObjectSchemaValidationException> errors) :
|
||||
m_object_type(object_type), m_validation_errors(errors)
|
||||
{
|
||||
m_what ="Migration is required for object type '" + object_type + "' due to the following errors: ";
|
||||
for (auto error : errors) {
|
||||
m_what += std::string("\n- ") + error.what();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string object_type, Property &property) :
|
||||
ObjectSchemaValidationException(object_type), m_property(property)
|
||||
{
|
||||
m_what = "Can't index property " + object_type + "." + property.name + ": indexing a property of type '" + string_for_property_type(property.type) + "' is currently not supported";
|
||||
}
|
||||
|
||||
ExtraPropertyException::ExtraPropertyException(std::string object_type, Property &property) :
|
||||
ObjectSchemaValidationException(object_type), m_property(property)
|
||||
{
|
||||
m_what = "Property '" + property.name + "' has been added to latest object model.";
|
||||
}
|
||||
|
||||
MissingPropertyException::MissingPropertyException(std::string object_type, Property &property) :
|
||||
ObjectSchemaValidationException(object_type), m_property(property)
|
||||
{
|
||||
m_what = "Property '" + property.name + "' is missing from latest object model.";
|
||||
}
|
||||
|
||||
MismatchedPropertiesException::MismatchedPropertiesException(std::string object_type, Property &old_property, Property &new_property) :
|
||||
ObjectSchemaValidationException(object_type), m_old_property(old_property), m_new_property(new_property)
|
||||
{
|
||||
if (new_property.type != old_property.type) {
|
||||
m_what = "Property types for '" + old_property.name + "' property do not match. Old type '" + string_for_property_type(old_property.type) +
|
||||
"', new type '" + string_for_property_type(new_property.type) + "'";
|
||||
}
|
||||
else if (new_property.object_type != old_property.object_type) {
|
||||
m_what = "Target object type for property '" + old_property.name + "' do not match. Old type '" + old_property.object_type + "', new type '" + new_property.object_type + "'";
|
||||
}
|
||||
else if (new_property.is_nullable != old_property.is_nullable) {
|
||||
m_what = "Nullability for property '" + old_property.name + "' has changed from '" + std::to_string(old_property.is_nullable) + "' to '" + std::to_string(new_property.is_nullable) + "'.";
|
||||
}
|
||||
}
|
||||
|
||||
ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string object_type, std::string old_primary, std::string new_primary) : ObjectSchemaValidationException(object_type), m_old_primary(old_primary), m_new_primary(new_primary)
|
||||
{
|
||||
if (old_primary.size()) {
|
||||
m_what = "Property '" + old_primary + "' is no longer a primary key.";
|
||||
}
|
||||
else {
|
||||
m_what = "Property '" + new_primary + "' has been made a primary key.";
|
||||
}
|
||||
}
|
||||
|
||||
InvalidPrimaryKeyException::InvalidPrimaryKeyException(std::string object_type, std::string primary) :
|
||||
ObjectSchemaValidationException(object_type), m_primary(primary)
|
||||
{
|
||||
m_what = "Specified primary key property '" + primary + "' does not exist.";
|
||||
}
|
||||
|
|
108
object_store.hpp
108
object_store.hpp
|
@ -22,16 +22,15 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <realm/link_view.hpp>
|
||||
#include <realm/group.hpp>
|
||||
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store_exceptions.hpp"
|
||||
|
||||
namespace realm {
|
||||
class Group;
|
||||
class StringData;
|
||||
class Table;
|
||||
template<typename T> class BasicTableRef;
|
||||
typedef BasicTableRef<Table> TableRef;
|
||||
class ObjectSchemaValidationException;
|
||||
class Schema : public std::map<std::string, ObjectSchema> {
|
||||
};
|
||||
|
||||
class ObjectStore {
|
||||
public:
|
||||
|
@ -48,17 +47,21 @@ namespace realm {
|
|||
// updates the column mapping on the target_schema
|
||||
// if no table is provided it is fetched from the group
|
||||
// returns array of validation errors
|
||||
static std::vector<ObjectStoreException> validate_object_schema(Group *group, ObjectSchema &target_schema);
|
||||
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
|
||||
static void update_column_mapping(Group *group, ObjectSchema &target_schema);
|
||||
|
||||
// determines if you must call update_realm_with_schema for a given realm.
|
||||
// returns true if there is a schema version mismatch, if there tables which still need to be created,
|
||||
// or if file format or other changes/updates need to be made
|
||||
static bool realm_requires_update(Group *group, uint64_t version, Schema &schema);
|
||||
|
||||
// updates a Realm to a given target schema/version creating tables and updating indexes as necessary
|
||||
// returns if any changes were made
|
||||
// passed in schema ar updated with the correct column mapping
|
||||
// optionally runs migration function/lambda if schema is out of date
|
||||
// NOTE: must be performed within a write transaction
|
||||
typedef std::vector<ObjectSchema> Schema;
|
||||
typedef std::function<void(Group *, Schema &)> MigrationFunction;
|
||||
static bool update_realm_with_schema(Group *group, uint64_t version, Schema &schema, MigrationFunction migration);
|
||||
|
||||
|
@ -68,9 +71,6 @@ namespace realm {
|
|||
// get existing Schema from a group
|
||||
static Schema schema_from_group(Group *group);
|
||||
|
||||
// check if indexes are up to date - if false you need to call update_realm_with_schema
|
||||
static bool indexes_are_up_to_date(Group *group, Schema &schema);
|
||||
|
||||
// deletes the table for the given type
|
||||
static void delete_data_for_object(Group *group, const StringData &object_type);
|
||||
|
||||
|
@ -88,7 +88,7 @@ namespace realm {
|
|||
|
||||
// set references to tables on targetSchema and create/update any missing or out-of-date tables
|
||||
// if update existing is true, updates existing tables, otherwise only adds and initializes new tables
|
||||
static bool create_tables(realm::Group *group, ObjectStore::Schema &target_schema, bool update_existing);
|
||||
static bool create_tables(realm::Group *group, Schema &target_schema, bool update_existing);
|
||||
|
||||
// get primary key property name for object type
|
||||
static StringData get_primary_key_for_object(Group *group, StringData object_type);
|
||||
|
@ -101,6 +101,9 @@ namespace realm {
|
|||
static std::string table_name_for_object_type(const std::string &class_name);
|
||||
static std::string object_type_for_table_name(const std::string &table_name);
|
||||
|
||||
// check if indexes are up to date - if false you need to call update_realm_with_schema
|
||||
static bool indexes_are_up_to_date(Group *group, Schema &schema);
|
||||
|
||||
// returns if any indexes were changed
|
||||
static bool update_indexes(Group *group, Schema &schema);
|
||||
|
||||
|
@ -109,6 +112,87 @@ namespace realm {
|
|||
|
||||
friend ObjectSchema;
|
||||
};
|
||||
|
||||
// Base exception
|
||||
class ObjectStoreException : public std::exception {
|
||||
public:
|
||||
ObjectStoreException() = default;
|
||||
ObjectStoreException(const std::string &what) : m_what(what) {}
|
||||
virtual const char* what() const noexcept { return m_what.c_str(); }
|
||||
protected:
|
||||
std::string m_what;
|
||||
};
|
||||
|
||||
// Migration exceptions
|
||||
class MigrationException : public ObjectStoreException {};
|
||||
|
||||
class InvalidSchemaVersionException : public MigrationException {
|
||||
public:
|
||||
InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version);
|
||||
private:
|
||||
uint64_t m_old_version, m_new_version;
|
||||
};
|
||||
|
||||
class DuplicatePrimaryKeyValueException : public MigrationException {
|
||||
public:
|
||||
DuplicatePrimaryKeyValueException(std::string object_type, Property &property);
|
||||
private:
|
||||
std::string m_object_type;
|
||||
Property m_property;
|
||||
};
|
||||
|
||||
// Schema validation exceptions
|
||||
class ObjectSchemaValidationException : public ObjectStoreException {
|
||||
public:
|
||||
ObjectSchemaValidationException(std::string object_type) : m_object_type(object_type) {}
|
||||
ObjectSchemaValidationException(std::string object_type, std::vector<ObjectSchemaValidationException> errors);
|
||||
private:
|
||||
std::vector<ObjectSchemaValidationException> m_validation_errors;
|
||||
protected:
|
||||
std::string m_object_type;
|
||||
};
|
||||
|
||||
class PropertyTypeNotIndexableException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
PropertyTypeNotIndexableException(std::string object_type, Property &property);
|
||||
private:
|
||||
Property m_property;
|
||||
};
|
||||
|
||||
class ExtraPropertyException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
ExtraPropertyException(std::string object_type, Property &property);
|
||||
private:
|
||||
Property m_property;
|
||||
};
|
||||
|
||||
class MissingPropertyException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
MissingPropertyException(std::string object_type, Property &property);
|
||||
private:
|
||||
Property m_property;
|
||||
};
|
||||
|
||||
class MismatchedPropertiesException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
MismatchedPropertiesException(std::string object_type, Property &old_property, Property &new_property);
|
||||
private:
|
||||
Property m_old_property, m_new_property;
|
||||
};
|
||||
|
||||
class ChangedPrimaryKeyException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
ChangedPrimaryKeyException(std::string object_type, std::string old_primary, std::string new_primary);
|
||||
private:
|
||||
std::string m_old_primary, m_new_primary;
|
||||
};
|
||||
|
||||
class InvalidPrimaryKeyException : public ObjectSchemaValidationException {
|
||||
public:
|
||||
InvalidPrimaryKeyException(std::string object_type, std::string primary);
|
||||
private:
|
||||
std::string m_primary;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* defined(REALM_OBJECT_STORE_HPP) */
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "object_store_exceptions.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
#include <realm/util/assert.hpp>
|
||||
#include <regex>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, Info info) : m_kind(kind), m_info(info), m_what(generate_what()) {}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) {
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPropertyName] = prop.name;
|
||||
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) :
|
||||
m_kind(kind) {
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPropertyName] = prop.name;
|
||||
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||
m_info[InfoKeyOldPropertyType] = string_for_property_type(oldProp.type);
|
||||
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||
m_info[InfoKeyOldPropertyObjectType] = oldProp.object_type;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key) : m_kind(kind) {
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_info[InfoKeyPrimaryKey] = primary_key;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(uint64_t old_version, uint64_t new_version) : m_kind(Kind::RealmVersionGreaterThanSchemaVersion) {
|
||||
m_info[InfoKeyOldVersion] = std::to_string(old_version);
|
||||
m_info[InfoKeyNewVersion] = std::to_string(new_version);
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
ObjectStoreException::ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type) :
|
||||
m_validation_errors(validation_errors), m_kind(Kind::ObjectStoreValidationFailure) {
|
||||
m_info[InfoKeyObjectType] = object_type;
|
||||
m_what = generate_what();
|
||||
}
|
||||
|
||||
std::string ObjectStoreException::generate_what() const {
|
||||
auto format_string = s_custom_format_strings.find(m_kind);
|
||||
if (format_string != s_custom_format_strings.end()) {
|
||||
return populate_format_string(format_string->second);
|
||||
}
|
||||
return populate_format_string(s_default_format_strings.at(m_kind));
|
||||
}
|
||||
|
||||
std::string ObjectStoreException::validation_errors_string() const {
|
||||
std::string errors_string;
|
||||
for (auto error : m_validation_errors) {
|
||||
errors_string += std::string("\n- ") + error.what();
|
||||
}
|
||||
return errors_string;
|
||||
}
|
||||
|
||||
std::string ObjectStoreException::populate_format_string(const std::string & format_string) const {
|
||||
std::string out_string, current(format_string);
|
||||
std::smatch sm;
|
||||
std::regex re("\\{(\\w+)\\}");
|
||||
while(regex_search(current, sm, re)) {
|
||||
out_string += sm.prefix();
|
||||
const std::string &key = sm[1];
|
||||
if (key == "ValidationErrors") {
|
||||
out_string += validation_errors_string();
|
||||
}
|
||||
else {
|
||||
out_string += m_info.at(key);
|
||||
}
|
||||
current = sm.suffix();
|
||||
}
|
||||
out_string += current;
|
||||
return out_string;
|
||||
}
|
||||
|
||||
ObjectStoreException::FormatStrings ObjectStoreException::s_custom_format_strings;
|
||||
const ObjectStoreException::FormatStrings ObjectStoreException::s_default_format_strings = {
|
||||
{Kind::RealmVersionGreaterThanSchemaVersion,
|
||||
"Provided schema version {InfoKeyOldVersion} is less than last set version {InfoKeyNewVersion}."},
|
||||
{Kind::RealmPropertyTypeNotIndexable,
|
||||
"Can't index property {InfoKeyObjectType}.{InfoKeyPropertyName}: indexing a property of type '{InfoKeyPropertyType}' is currently not supported"},
|
||||
{Kind::RealmDuplicatePrimaryKeyValue,
|
||||
"Primary key property '{InfoKeyPropertyType}' has duplicate values after migration."},
|
||||
{Kind::ObjectSchemaMissingProperty,
|
||||
"Property '{InfoKeyPropertyName}' is missing from latest object model."},
|
||||
{Kind::ObjectSchemaNewProperty,
|
||||
"Property '{InfoKeyPropertyName}' has been added to latest object model."},
|
||||
{Kind::ObjectSchemaMismatchedTypes,
|
||||
"Property types for '{InfoKeyPropertyName}' property do not match. Old type '{InfoKeyOldPropertyType}', new type '{InfoKeyPropertyType}'"},
|
||||
{Kind::ObjectSchemaMismatchedObjectTypes,
|
||||
"Target object type for property '{InfoKeyPropertyName}' does not match. Old type '{InfoKeyOldPropertyObjectType}', new type '{InfoKeyPropertyObjectType}'."},
|
||||
{Kind::ObjectSchemaChangedPrimaryKey,
|
||||
"Property '{InfoKeyPrimaryKey}' is no longer a primary key."},
|
||||
{Kind::ObjectSchemaNewPrimaryKey,
|
||||
"Property '{InfoKeyPrimaryKey}' has been made a primary key."},
|
||||
{Kind::ObjectSchemaChangedOptionalProperty,
|
||||
"Property '{InfoKeyPrimaryKey}' is no longer optional."},
|
||||
{Kind::ObjectSchemaNewOptionalProperty,
|
||||
"Property '{InfoKeyPrimaryKey}' has been made optional."},
|
||||
{Kind::ObjectStoreValidationFailure,
|
||||
"Migration is required for object type '{InfoKeyObjectType}' due to the following errors: {ValidationErrors}"}
|
||||
};
|
|
@ -1,102 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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_OBJECT_STORE_EXCEPTIONS_HPP
|
||||
#define REALM_OBJECT_STORE_EXCEPTIONS_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace realm {
|
||||
class Property;
|
||||
|
||||
class ObjectStoreException : public std::exception {
|
||||
public:
|
||||
enum class Kind {
|
||||
RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion
|
||||
RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType
|
||||
RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType
|
||||
ObjectSchemaMissingProperty, // ObjectType, PropertyName, PropertyType
|
||||
ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType
|
||||
ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType
|
||||
ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType
|
||||
ObjectSchemaChangedPrimaryKey, // ObjectType, PrimaryKey
|
||||
ObjectSchemaNewPrimaryKey, // ObjectType, PrimaryKey
|
||||
ObjectSchemaChangedOptionalProperty,
|
||||
ObjectSchemaNewOptionalProperty,
|
||||
ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException>
|
||||
};
|
||||
|
||||
using InfoKey = std::string;
|
||||
using Info = std::map<InfoKey, std::string>;
|
||||
|
||||
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop);
|
||||
|
||||
// ObjectSchemaMismatchedTypes, ObjectSchemaMismatchedObjectTypes
|
||||
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp);
|
||||
|
||||
// ObjectSchemaChangedPrimaryKey, ObjectSchemaNewPrimaryKey
|
||||
ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key);
|
||||
|
||||
// RealmVersionGreaterThanSchemaVersion
|
||||
ObjectStoreException(uint64_t old_version, uint64_t new_version);
|
||||
|
||||
// ObjectStoreValidationFailure
|
||||
ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type);
|
||||
|
||||
ObjectStoreException::Kind kind() const { return m_kind; }
|
||||
const ObjectStoreException::Info &info() const { return m_info; }
|
||||
|
||||
const char *what() const noexcept override { return m_what.c_str(); }
|
||||
|
||||
// implement CustomWhat to customize exception messages per platform/language
|
||||
typedef std::map<Kind, std::string> FormatStrings;
|
||||
static void set_custom_format_strings(FormatStrings custom_format_strings) { s_custom_format_strings = custom_format_strings; }
|
||||
|
||||
private:
|
||||
ObjectStoreException(Kind kind, Info info = Info());
|
||||
|
||||
Kind m_kind;
|
||||
Info m_info;
|
||||
std::vector<ObjectStoreException> m_validation_errors;
|
||||
|
||||
std::string m_what;
|
||||
std::string generate_what() const;
|
||||
std::string validation_errors_string() const;
|
||||
std::string populate_format_string(const std::string &format_string) const;
|
||||
|
||||
static const FormatStrings s_default_format_strings;
|
||||
static FormatStrings s_custom_format_strings;
|
||||
|
||||
public:
|
||||
#define INFO_KEY(key) InfoKey InfoKey##key = "InfoKey" #key;
|
||||
INFO_KEY(OldVersion);
|
||||
INFO_KEY(NewVersion);
|
||||
INFO_KEY(ObjectType);
|
||||
INFO_KEY(PropertyName);
|
||||
INFO_KEY(PropertyType);
|
||||
INFO_KEY(OldPropertyType);
|
||||
INFO_KEY(PropertyObjectType);
|
||||
INFO_KEY(OldPropertyObjectType);
|
||||
INFO_KEY(PrimaryKey);
|
||||
#undef INFO_KEY
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* defined(REALM_OBJECT_STORE_EXCEPTIONS_HPP) */
|
|
@ -47,6 +47,8 @@ namespace realm {
|
|||
|
||||
struct Property {
|
||||
public:
|
||||
Property() : object_type(""), is_primary(false), is_indexed(false), is_nullable(false) {}
|
||||
|
||||
std::string name;
|
||||
PropertyType type;
|
||||
std::string object_type;
|
||||
|
|
107
shared_realm.cpp
107
shared_realm.cpp
|
@ -17,8 +17,8 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
|
@ -28,7 +28,7 @@ std::mutex Realm::s_init_mutex;
|
|||
Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), in_memory(c.in_memory), schema_version(c.schema_version), encryption_key(c.encryption_key), migration_function(c.migration_function)
|
||||
{
|
||||
if (c.schema) {
|
||||
schema = std::make_unique<ObjectStore::Schema>(*c.schema);
|
||||
schema = std::make_unique<Schema>(*c.schema);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,22 +48,28 @@ Realm::Realm(Config &config) : m_config(config), m_thread_id(std::this_thread::g
|
|||
}
|
||||
}
|
||||
catch (util::File::PermissionDenied const& ex) {
|
||||
throw RealmException(RealmException::Kind::FilePermissionDenied, "Unable to open a realm at path '" + config.path +
|
||||
throw RealmFileException(RealmFileException::Kind::PermissionDenied, "Unable to open a realm at path '" + config.path +
|
||||
"'. Please use a path where your app has " + (config.read_only ? "read" : "read-write") + " permissions.");
|
||||
}
|
||||
catch (util::File::Exists const& ex) {
|
||||
throw RealmException(RealmException::Kind::FileExists, "Unable to open a realm at path '" + config.path + "'");
|
||||
throw RealmFileException(RealmFileException::Kind::Exists, "Unable to open a realm at path '" + config.path + "'");
|
||||
}
|
||||
catch (util::File::AccessError const& ex) {
|
||||
throw RealmException(RealmException::Kind::FileAccessError, "Unable to open a realm at path '" + config.path + "'");
|
||||
throw RealmFileException(RealmFileException::Kind::AccessError, "Unable to open a realm at path '" + config.path + "'");
|
||||
}
|
||||
catch (IncompatibleLockFile const&) {
|
||||
throw RealmException(RealmException::Kind::IncompatibleLockFile, "Realm file is currently open in another process "
|
||||
throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, "Realm file is currently open in another process "
|
||||
"which cannot share access with this process. All processes sharing a single file must be the same architecture.");
|
||||
}
|
||||
}
|
||||
|
||||
Group *Realm::read_group() {
|
||||
Realm::~Realm()
|
||||
{
|
||||
s_global_cache.remove(m_config.path, m_thread_id);
|
||||
}
|
||||
|
||||
Group *Realm::read_group()
|
||||
{
|
||||
if (!m_group) {
|
||||
m_group = &const_cast<Group&>(m_shared_group->begin_read());
|
||||
}
|
||||
|
@ -75,11 +81,24 @@ SharedRealm Realm::get_shared_realm(Config &config)
|
|||
SharedRealm realm = s_global_cache.get_realm(config.path);
|
||||
if (realm) {
|
||||
if (realm->config().read_only != config.read_only) {
|
||||
throw RealmException(RealmException::Kind::MismatchedConfig, "Realm at path already opened with different read permissions");
|
||||
throw MismatchedConfigException("Realm at path already opened with different read permissions.");
|
||||
}
|
||||
if (realm->config().in_memory != config.in_memory) {
|
||||
throw RealmException(RealmException::Kind::MismatchedConfig, "Realm at path already opened with different inMemory settings");
|
||||
throw MismatchedConfigException("Realm at path already opened with different inMemory settings.");
|
||||
}
|
||||
if (realm->config().encryption_key != config.encryption_key) {
|
||||
throw MismatchedConfigException("Realm at path already opened with a different encryption key.");
|
||||
}
|
||||
if (realm->config().schema_version != config.schema_version && config.schema_version != ObjectStore::NotVersioned) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different schema version.");
|
||||
}
|
||||
// FIXME - enable schma comparison
|
||||
/*if (realm->config().schema != config.schema) {
|
||||
throw MismatchedConfigException("Realm at path already opened with different schema");
|
||||
}*/
|
||||
|
||||
realm->m_config.migration_function = config.migration_function;
|
||||
|
||||
return realm;
|
||||
}
|
||||
|
||||
|
@ -89,22 +108,27 @@ SharedRealm Realm::get_shared_realm(Config &config)
|
|||
std::lock_guard<std::mutex> lock(s_init_mutex);
|
||||
|
||||
if (!config.schema) {
|
||||
uint64_t version = ObjectStore::get_schema_version(realm->read_group());
|
||||
if (version == ObjectStore::NotVersioned) {
|
||||
InvalidSchemaVersionException(ObjectStore::NotVersioned, ObjectStore::NotVersioned);
|
||||
}
|
||||
|
||||
// get schema from group and skip validation
|
||||
realm->m_config.schema_version = ObjectStore::get_schema_version(realm->read_group());
|
||||
realm->m_config.schema = std::make_unique<ObjectStore::Schema>(ObjectStore::schema_from_group(realm->read_group()));
|
||||
realm->m_config.schema_version = version;
|
||||
realm->m_config.schema = std::make_unique<Schema>(ObjectStore::schema_from_group(realm->read_group()));
|
||||
}
|
||||
else if (config.read_only) {
|
||||
// for read-only validate all existing tables
|
||||
for (auto &object_schema : *realm->m_config.schema) {
|
||||
if (ObjectStore::table_for_object_type(realm->read_group(), object_schema.name)) {
|
||||
ObjectStore::validate_object_schema(realm->read_group(), object_schema);
|
||||
if (ObjectStore::table_for_object_type(realm->read_group(), object_schema.first)) {
|
||||
ObjectStore::validate_object_schema(realm->read_group(), object_schema.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
// FIXME - need to validate that schemas match
|
||||
realm->m_config.schema = std::make_unique<ObjectStore::Schema>(*existing->m_config.schema);
|
||||
realm->m_config.schema = std::make_unique<Schema>(*existing->m_config.schema);
|
||||
}
|
||||
else {
|
||||
// its a non-cached realm so update/migrate if needed
|
||||
|
@ -115,39 +139,62 @@ SharedRealm Realm::get_shared_realm(Config &config)
|
|||
return realm;
|
||||
}
|
||||
|
||||
bool Realm::update_schema(ObjectStore::Schema &schema, uint64_t version)
|
||||
bool Realm::update_schema(Schema &schema, uint64_t version)
|
||||
{
|
||||
bool changed = false;
|
||||
Config old_config(m_config);
|
||||
|
||||
// set new version/schema
|
||||
if (m_config.schema.get() != &schema) {
|
||||
m_config.schema = std::make_unique<Schema>(schema);
|
||||
}
|
||||
m_config.schema_version = version;
|
||||
|
||||
try {
|
||||
begin_transaction();
|
||||
changed = ObjectStore::update_realm_with_schema(read_group(), version, schema, m_config.migration_function);
|
||||
commit_transaction();
|
||||
m_config.schema_version = version;
|
||||
if (m_config.schema.get() != &schema) {
|
||||
m_config.schema = std::make_unique<ObjectStore::Schema>(schema);
|
||||
if (ObjectStore::realm_requires_update(read_group(), version, schema)) {
|
||||
// keep old copy to pass to migration function
|
||||
old_config.read_only = true;
|
||||
SharedRealm old_realm = SharedRealm(new Realm(old_config)), updated_realm = shared_from_this();
|
||||
|
||||
// update and migrate
|
||||
begin_transaction();
|
||||
changed = ObjectStore::update_realm_with_schema(read_group(), version, *m_config.schema, [=](__unused Group *group, __unused Schema &target_schema) {
|
||||
m_config.migration_function(old_realm, updated_realm);
|
||||
});
|
||||
commit_transaction();
|
||||
}
|
||||
else {
|
||||
// verify all types
|
||||
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 (...) {
|
||||
if (is_in_transaction()) {
|
||||
cancel_transaction();
|
||||
}
|
||||
m_config.schema_version = old_config.schema_version;
|
||||
m_config.schema = std::move(old_config.schema);
|
||||
throw;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void check_read_write(Realm *realm)
|
||||
{
|
||||
if (realm->config().read_only) {
|
||||
throw RealmException(RealmException::Kind::InvalidTransaction, "Can't perform transactions on read-only Realms.");
|
||||
throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::verify_thread()
|
||||
{
|
||||
if (m_thread_id != std::this_thread::get_id()) {
|
||||
throw RealmException(RealmException::Kind::IncorrectThread, "Realm accessed from incorrect thread.");
|
||||
throw IncorrectThreadException("Realm accessed from incorrect thread.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +204,7 @@ void Realm::begin_transaction()
|
|||
verify_thread();
|
||||
|
||||
if (m_in_transaction) {
|
||||
throw RealmException(RealmException::Kind::InvalidTransaction, "The Realm is already in a write transaction");
|
||||
throw InvalidTransactionException("The Realm is already in a write transaction");
|
||||
}
|
||||
|
||||
// if the upgrade to write will move the transaction forward, announce the change after promoting
|
||||
|
@ -180,7 +227,7 @@ void Realm::commit_transaction()
|
|||
verify_thread();
|
||||
|
||||
if (!m_in_transaction) {
|
||||
throw RealmException(RealmException::Kind::InvalidTransaction, "Can't commit a non-existing write transaction");
|
||||
throw InvalidTransactionException("Can't commit a non-existing write transaction");
|
||||
}
|
||||
|
||||
LangBindHelper::commit_and_continue_as_read(*m_shared_group);
|
||||
|
@ -196,7 +243,7 @@ void Realm::cancel_transaction()
|
|||
verify_thread();
|
||||
|
||||
if (!m_in_transaction) {
|
||||
throw RealmException(RealmException::Kind::InvalidTransaction, "Can't cancel a non-existing write transaction");
|
||||
throw InvalidTransactionException("Can't cancel a non-existing write transaction");
|
||||
}
|
||||
|
||||
LangBindHelper::rollback_and_continue_as_read(*m_shared_group, *m_history);
|
||||
|
@ -226,11 +273,11 @@ bool Realm::compact()
|
|||
|
||||
bool success = false;
|
||||
if (m_in_transaction) {
|
||||
throw RealmException(RealmException::Kind::InvalidTransaction, "Can't compact a Realm within a write transaction");
|
||||
throw InvalidTransactionException("Can't compact a Realm within a write transaction");
|
||||
}
|
||||
|
||||
for (auto &object_schema : *m_config.schema) {
|
||||
ObjectStore::table_for_object_type(read_group(), object_schema.name)->optimize();
|
||||
ObjectStore::table_for_object_type(read_group(), object_schema.first)->optimize();
|
||||
}
|
||||
|
||||
m_shared_group->end_read();
|
||||
|
@ -340,7 +387,7 @@ void RealmCache::remove(const std::string &path, std::thread::id thread_id)
|
|||
}
|
||||
|
||||
auto thread_iter = path_iter->second.find(thread_id);
|
||||
if (thread_iter == path_iter->second.end()) {
|
||||
if (thread_iter != path_iter->second.end()) {
|
||||
path_iter->second.erase(thread_iter);
|
||||
}
|
||||
|
||||
|
|
104
shared_realm.hpp
104
shared_realm.hpp
|
@ -25,8 +25,8 @@
|
|||
#include <mutex>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "object_store.hpp"
|
||||
#include <realm/group.hpp>
|
||||
|
||||
namespace realm {
|
||||
class RealmCache;
|
||||
|
@ -35,9 +35,11 @@ namespace realm {
|
|||
typedef std::weak_ptr<Realm> WeakRealm;
|
||||
class ClientHistory;
|
||||
|
||||
class Realm
|
||||
class Realm : public std::enable_shared_from_this<Realm>
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(SharedRealm old_realm, SharedRealm realm)> MigrationFunction;
|
||||
|
||||
struct Config
|
||||
{
|
||||
std::string path;
|
||||
|
@ -45,11 +47,12 @@ namespace realm {
|
|||
bool in_memory;
|
||||
StringData encryption_key;
|
||||
|
||||
std::unique_ptr<ObjectStore::Schema> schema;
|
||||
std::unique_ptr<Schema> schema;
|
||||
uint64_t schema_version;
|
||||
ObjectStore::MigrationFunction migration_function;
|
||||
|
||||
Config() = default;
|
||||
MigrationFunction migration_function;
|
||||
|
||||
Config() : read_only(false), in_memory(false), schema_version(ObjectStore::NotVersioned) {};
|
||||
Config(const Config& c);
|
||||
};
|
||||
|
||||
|
@ -65,7 +68,7 @@ namespace realm {
|
|||
// Uses the existing migration function on the Config, and the resulting Schema and version with updated
|
||||
// column mappings are set on the realms config upon success.
|
||||
// returns if any changes were made
|
||||
bool update_schema(ObjectStore::Schema &schema, uint64_t version);
|
||||
bool update_schema(Schema &schema, uint64_t version);
|
||||
|
||||
const Config &config() const { return m_config; }
|
||||
|
||||
|
@ -94,7 +97,6 @@ namespace realm {
|
|||
|
||||
private:
|
||||
Realm(Config &config);
|
||||
//Realm(const Realm& r) = delete;
|
||||
|
||||
Config m_config;
|
||||
std::thread::id m_thread_id;
|
||||
|
@ -113,49 +115,15 @@ namespace realm {
|
|||
|
||||
Group *m_group;
|
||||
|
||||
public: // FIXME private
|
||||
Group *read_group();
|
||||
|
||||
ExternalNotificationFunction m_external_notifier;
|
||||
|
||||
static std::mutex s_init_mutex;
|
||||
static RealmCache s_global_cache;
|
||||
};
|
||||
|
||||
class RealmException : public std::exception
|
||||
{
|
||||
public:
|
||||
enum class Kind
|
||||
{
|
||||
/** Options specified in the config do not match other Realm instances opened on the same thread */
|
||||
MismatchedConfig,
|
||||
/** Thrown for any I/O related exception scenarios when a realm is opened. */
|
||||
FileAccessError,
|
||||
/** Thrown if the user does not have permission to open or create
|
||||
the specified file in the specified access mode when the realm is opened. */
|
||||
FilePermissionDenied,
|
||||
/** Thrown if no_create was specified and the file did already exist when the realm is opened. */
|
||||
FileExists,
|
||||
/** Thrown if no_create was specified and the file was not found when the realm is opened. */
|
||||
FileNotFound,
|
||||
/** Thrown if the database file is currently open in another
|
||||
process which cannot share with the current process due to an
|
||||
architecture mismatch. */
|
||||
IncompatibleLockFile,
|
||||
InvalidTransaction,
|
||||
IncorrectThread,
|
||||
/** Thrown when trying to open an unitialized Realm without a target schema or with a mismatching
|
||||
schema version **/
|
||||
InvalidSchemaVersion
|
||||
};
|
||||
RealmException(Kind kind, std::string message) : m_kind(kind), m_what(message) {}
|
||||
~Realm();
|
||||
ExternalNotificationFunction m_external_notifier;
|
||||
|
||||
virtual const char *what() noexcept { return m_what.c_str(); }
|
||||
Kind kind() const { return m_kind; }
|
||||
|
||||
private:
|
||||
Kind m_kind;
|
||||
std::string m_what;
|
||||
// FIXME private
|
||||
Group *read_group();
|
||||
};
|
||||
|
||||
class RealmCache
|
||||
|
@ -167,9 +135,53 @@ namespace realm {
|
|||
void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id());
|
||||
|
||||
private:
|
||||
std::map<const std::string, std::map<std::thread::id, WeakRealm>> m_cache;
|
||||
std::map<std::string, std::map<std::thread::id, WeakRealm>> m_cache;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
class RealmFileException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
enum class Kind
|
||||
{
|
||||
/** Thrown for any I/O related exception scenarios when a realm is opened. */
|
||||
AccessError,
|
||||
/** Thrown if the user does not have permission to open or create
|
||||
the specified file in the specified access mode when the realm is opened. */
|
||||
PermissionDenied,
|
||||
/** Thrown if no_create was specified and the file did already exist when the realm is opened. */
|
||||
Exists,
|
||||
/** Thrown if no_create was specified and the file was not found when the realm is opened. */
|
||||
NotFound,
|
||||
/** Thrown if the database file is currently open in another
|
||||
process which cannot share with the current process due to an
|
||||
architecture mismatch. */
|
||||
IncompatibleLockFile,
|
||||
};
|
||||
RealmFileException(Kind kind, std::string message) : std::runtime_error(message), m_kind(kind) {}
|
||||
Kind kind() const { return m_kind; }
|
||||
|
||||
private:
|
||||
Kind m_kind;
|
||||
};
|
||||
|
||||
class MismatchedConfigException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MismatchedConfigException(std::string message) : std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
class InvalidTransactionException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidTransactionException(std::string message) : std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
class IncorrectThreadException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
IncorrectThreadException(std::string message) : std::runtime_error(message) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* defined(REALM_REALM_HPP) */
|
||||
|
|
Loading…
Reference in New Issue