diff --git a/impl/transact_log_handler.cpp b/impl/transact_log_handler.cpp index fba97129..2deba2fd 100644 --- a/impl/transact_log_handler.cpp +++ b/impl/transact_log_handler.cpp @@ -18,25 +18,23 @@ #include "transact_log_handler.hpp" -#include "../realm_binding_context.hpp" +#include "../binding_context.hpp" #include #include #include -using namespace realm; - -namespace { +namespace realm { class TransactLogHandler { - using ColumnInfo = RealmBindingContext::ColumnInfo; - using ObserverState = RealmBindingContext::ObserverState; + using ColumnInfo = BindingContext::ColumnInfo; + using ObserverState = BindingContext::ObserverState; // Observed table rows which need change information std::vector m_observers; // Userdata pointers for rows which have been deleted std::vector invalidated; // Delegate to send change information to - RealmBindingContext* m_binding_context; + BindingContext* m_binding_context; // Index of currently selected table size_t m_current_table = 0; @@ -84,7 +82,7 @@ class TransactLogHandler { public: template - TransactLogHandler(RealmBindingContext* binding_context, SharedGroup& sg, Func&& func) + TransactLogHandler(BindingContext* binding_context, SharedGroup& sg, Func&& func) : m_binding_context(binding_context) { if (!binding_context) { @@ -325,19 +323,19 @@ public: namespace realm { namespace _impl { namespace transaction { -void advance(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { +void advance(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context) { TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::advance_read(sg, history, std::move(args)...); }); } -void begin(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { +void begin(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context) { TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::promote_to_write(sg, history, std::move(args)...); }); } -void commit(SharedGroup& sg, ClientHistory&, RealmBindingContext* binding_context) { +void commit(SharedGroup& sg, ClientHistory&, BindingContext* binding_context) { LangBindHelper::commit_and_continue_as_read(sg); if (binding_context) { @@ -345,7 +343,7 @@ void commit(SharedGroup& sg, ClientHistory&, RealmBindingContext* binding_contex } } -void cancel(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { +void cancel(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context) { TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::rollback_and_continue_as_read(sg, history, std::move(args)...); }); diff --git a/impl/transact_log_handler.hpp b/impl/transact_log_handler.hpp index 321a67c9..40ddf3f6 100644 --- a/impl/transact_log_handler.hpp +++ b/impl/transact_log_handler.hpp @@ -20,7 +20,7 @@ #define REALM_TRANSACT_LOG_HANDLER_HPP namespace realm { -class RealmBindingContext; +class BindingContext; class SharedGroup; class ClientHistory; @@ -28,19 +28,19 @@ namespace _impl { namespace transaction { // Advance the read transaction version, with change notifications sent to delegate // Must not be called from within a write transaction. -void advance(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); +void advance(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); // Begin a write transaction // If the read transaction version is not up to date, will first advance to the // most recent read transaction and sent notifications to delegate -void begin(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); +void begin(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); // Commit a write transaction -void commit(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); +void commit(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); // Cancel a write transaction and roll back all changes, with change notifications // for reverting to the old values sent to delegate -void cancel(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); +void cancel(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); } // namespace transaction } // namespace _impl } // namespace realm diff --git a/list.cpp b/list.cpp new file mode 100644 index 00000000..1cbf1477 --- /dev/null +++ b/list.cpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 "list.hpp" +#import + +using namespace realm; + +size_t List::size() { + verify_attached(); + return m_link_view->size(); +} + +Row List::get(std::size_t row_ndx) { + verify_attached(); + verify_valid_row(row_ndx); + return m_link_view->get(row_ndx); +} + +void List::set(std::size_t row_ndx, std::size_t target_row_ndx) { + verify_attached(); + verify_in_tranaction(); + verify_valid_row(row_ndx); + m_link_view->set(row_ndx, target_row_ndx); +} + +void List::add(std::size_t target_row_ndx) { + verify_attached(); + verify_in_tranaction(); + m_link_view->add(target_row_ndx); +} + +void List::insert(std::size_t row_ndx, std::size_t target_row_ndx) { + verify_attached(); + verify_in_tranaction(); + verify_valid_row(row_ndx, true); + m_link_view->insert(row_ndx, target_row_ndx); +} + +void List::remove(std::size_t row_ndx) { + verify_attached(); + verify_in_tranaction(); + verify_valid_row(row_ndx); + m_link_view->remove(row_ndx); +} + +void List::verify_valid_row(std::size_t row_ndx, bool insertion) { + size_t size = m_link_view->size(); + if (row_ndx > size || (!insertion && row_ndx == size)) { + throw std::out_of_range(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + std::to_string(size) + "."); + } +} + +void List::verify_attached() { + if (!m_link_view->is_attached()) { + throw std::runtime_error("Tableview is not attached"); + } + m_link_view->sync_if_needed(); +} + +void List::verify_in_tranaction() { + if (!m_realm->is_in_transaction()) { + throw std::runtime_error("Can only mutate a list within a transaction."); + } +} diff --git a/list.hpp b/list.hpp new file mode 100644 index 00000000..54bbed41 --- /dev/null +++ b/list.hpp @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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_LIST_HPP +#define REALM_LIST_HPP + +#import "shared_realm.hpp" +#import + +namespace realm { + class List { + public: + List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), object_schema(s), m_link_view(l) {} + + const ObjectSchema &object_schema; + SharedRealm realm() { return m_realm; } + + size_t size(); + Row get(std::size_t row_ndx); + void set(std::size_t row_ndx, std::size_t target_row_ndx); + + void add(size_t target_row_ndx); + void remove(size_t list_ndx); + void insert(size_t list_ndx, size_t target_row_ndx); + + template + void add(ContextType ctx, ValueType value); + + template + void insert(ContextType ctx, ValueType value, size_t list_ndx); + + template + void set(ContextType ctx, ValueType value, size_t list_ndx); + + void verify_valid_row(std::size_t row_ndx, bool insertion = false); + void verify_attached(); + void verify_in_tranaction(); + + private: + SharedRealm m_realm; + LinkViewRef m_link_view; + }; +} + + + +#endif /* REALM_LIST_HPP */ diff --git a/object_accessor.hpp b/object_accessor.hpp new file mode 100644 index 00000000..5cc7a358 --- /dev/null +++ b/object_accessor.hpp @@ -0,0 +1,323 @@ +/* Copyright 2015 Realm Inc - All Rights Reserved + * Proprietary and Confidential + */ + +#ifndef REALM_OBJECT_ACCESSOR_HPP +#define REALM_OBJECT_ACCESSOR_HPP + +#include +#include "shared_realm.hpp" +#include "list.hpp" + +namespace realm { + + class Object { + public: + Object(SharedRealm r, const ObjectSchema &s, Row o) : m_realm(r), object_schema(s), m_row(o) {} + + // property getter/setter + template + inline void set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update); + + template + inline ValueType get_property_value(ContextType ctx, std::string prop_name); + + // create an Object from a native representation + template + static inline Object create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update); + + const ObjectSchema &object_schema; + SharedRealm realm() { return m_realm; } + Row row() { return m_row; } + + private: + SharedRealm m_realm; + Row m_row; + + template + inline void set_property_value_impl(ContextType ctx, const Property &property, ValueType value, bool try_update); + template + inline ValueType get_property_value_impl(ContextType ctx, const Property &property); + }; + + // + // Value converters - template specializations must be implemented for each platform in order to call templated methods on Object + // + template + class NativeAccessor { + public: + static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); + static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); + + static bool has_default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name); + static ValueType default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name); + + static bool to_bool(ContextType, ValueType &); + static ValueType from_bool(ContextType, bool); + static long long to_long(ContextType, ValueType &); + static ValueType from_long(ContextType, long long); + static float to_float(ContextType, ValueType &); + static ValueType from_float(ContextType, float); + static double to_double(ContextType, ValueType &); + static ValueType from_double(ContextType, double); + static std::string to_string(ContextType, ValueType &); + static ValueType from_string(ContextType, StringData); + static std::string to_binary(ContextType, ValueType &); + static ValueType from_binary(ContextType, BinaryData); + static DateTime to_datetime(ContextType, ValueType &); + static ValueType from_datetime(ContextType, DateTime); + + static bool is_null(ContextType, ValueType &); + static ValueType null_value(ContextType); + + // convert value to persisted object + // for existing objects return the existing row index + // for new/updated objects return the row index + static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &val, const std::string &type, bool try_update); + static ValueType from_object(ContextType ctx, Object); + + // list value acessors + static size_t list_size(ContextType ctx, ValueType &val); + static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index); + static ValueType from_list(ContextType ctx, List); + + // + // Deprecated + // + static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); } + }; + + class InvalidPropertyException : public std::runtime_error + { + public: + InvalidPropertyException(const std::string object_type, const std::string property_name, const std::string message) : std::runtime_error(message), object_type(object_type), property_name(property_name) {} + const std::string object_type; + const std::string property_name; + }; + + class MissingPropertyValueException : public std::runtime_error + { + public: + MissingPropertyValueException(const std::string object_type, const std::string property_name, const std::string message) : std::runtime_error(message), object_type(object_type), property_name(property_name) {} + const std::string object_type; + const std::string property_name; + }; + + class MutationOutsideTransactionException : public std::runtime_error + { + public: + MutationOutsideTransactionException(std::string message) : std::runtime_error(message) {} + }; + + // + // template method implementations + // + template + inline void Object::set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update) + { + const Property *prop = object_schema.property_for_name(prop_name); + if (!prop) { + throw InvalidPropertyException(object_schema.name, prop_name, + "Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); + } + set_property_value_impl(ctx, *prop, value, try_update); + }; + + template + inline ValueType Object::get_property_value(ContextType ctx, std::string prop_name) + { + const Property *prop = object_schema.property_for_name(prop_name); + if (!prop) { + throw InvalidPropertyException(object_schema.name, prop_name, + "Getting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); + } + return get_property_value_impl(ctx, *prop); + }; + + template + inline void Object::set_property_value_impl(ContextType ctx, const Property &property, ValueType value, bool try_update) + { + using Accessor = NativeAccessor; + + if (!m_realm->is_in_transaction()) { + throw MutationOutsideTransactionException("Can only set property values within a transaction."); + } + + size_t column = property.table_column; + if (property.is_nullable && Accessor::is_null(ctx, value)) { + m_row.set_null(column); + return; + } + + switch (property.type) { + case PropertyTypeBool: + m_row.set_bool(column, Accessor::to_bool(ctx, value)); + break; + case PropertyTypeInt: + m_row.set_int(column, Accessor::to_long(ctx, value)); + break; + case PropertyTypeFloat: + m_row.set_float(column, Accessor::to_float(ctx, value)); + break; + case PropertyTypeDouble: + m_row.set_double(column, Accessor::to_double(ctx, value)); + break; + case PropertyTypeString: + m_row.set_string(column, Accessor::to_string(ctx, value)); + break; + case PropertyTypeData: + m_row.set_binary(column, BinaryData(Accessor::to_binary(ctx, value))); + break; + case PropertyTypeAny: + m_row.set_mixed(column, Accessor::to_mixed(ctx, value)); + break; + case PropertyTypeDate: + m_row.set_datetime(column, Accessor::to_datetime(ctx, value)); + break; + case PropertyTypeObject: { + if (Accessor::is_null(ctx, value)) { + m_row.nullify_link(column); + } + else { + m_row.set_link(column, Accessor::to_object_index(ctx, m_realm, value, property.object_type, try_update)); + } + break; + } + case PropertyTypeArray: { + realm::LinkViewRef link_view = m_row.get_linklist(column); + link_view->clear(); + size_t count = Accessor::list_size(ctx, value); + for (size_t i = 0; i < count; i++) { + ValueType element = Accessor::list_value_at_index(ctx, value, i); + link_view->add(Accessor::to_object_index(ctx, m_realm, element, property.object_type, try_update)); + } + break; + } + } + } + + template + inline ValueType Object::get_property_value_impl(ContextType ctx, const Property &property) + { + using Accessor = NativeAccessor; + + size_t column = property.table_column; + if (property.is_nullable && m_row.is_null(column)) { + return Accessor::null_value(ctx); + } + + switch (property.type) { + case PropertyTypeBool: + return Accessor::from_bool(ctx, m_row.get_bool(column)); + case PropertyTypeInt: + return Accessor::from_long(ctx, m_row.get_int(column)); + case PropertyTypeFloat: + return Accessor::from_float(ctx, m_row.get_float(column)); + case PropertyTypeDouble: + return Accessor::from_double(ctx, m_row.get_double(column)); + case PropertyTypeString: + return Accessor::from_string(ctx, m_row.get_string(column)); + case PropertyTypeData: + return Accessor::from_binary(ctx, m_row.get_binary(column)); + case PropertyTypeAny: + throw "Any not supported"; + case PropertyTypeDate: + return Accessor::from_datetime(ctx, m_row.get_datetime(column)); + case PropertyTypeObject: { + auto linkObjectSchema = m_realm->config().schema->find(property.object_type); + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), linkObjectSchema->name); + if (m_row.is_null_link(property.table_column)) { + return Accessor::null_value(ctx); + } + return Accessor::from_object(ctx, std::move(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column))))); + } + case PropertyTypeArray: { + auto arrayObjectSchema = m_realm->config().schema->find(property.object_type); + return Accessor::from_list(ctx, std::move(List(m_realm, *arrayObjectSchema, static_cast(m_row.get_linklist(column))))); + } + } + } + + template + inline Object Object::create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update) + { + using Accessor = NativeAccessor; + + if (!realm->is_in_transaction()) { + throw MutationOutsideTransactionException("Can only create objects within a transaction."); + } + + // get or create our accessor + bool created; + + // try to get existing row if updating + size_t row_index = realm::not_found; + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name); + const Property *primary_prop = object_schema.primary_key_property(); + if (primary_prop) { + // search for existing object based on primary key type + ValueType primary_value = Accessor::dict_value_for_key(ctx, value, object_schema.primary_key); + if (primary_prop->type == PropertyTypeString) { + row_index = table->find_first_string(primary_prop->table_column, Accessor::to_string(ctx, primary_value)); + } + else { + row_index = table->find_first_int(primary_prop->table_column, Accessor::to_long(ctx, primary_value)); + } + + if (!try_update && row_index != realm::not_found) { + throw DuplicatePrimaryKeyValueException(object_schema.name, *primary_prop, + "Attempting to create an object of type '" + object_schema.name + "' with an exising primary key value."); + } + } + + // if no existing, create row + created = false; + if (row_index == realm::not_found) { + row_index = table->add_empty_row(); + created = true; + } + + // populate + Object object(realm, object_schema, table->get(row_index)); + for (Property &prop : object_schema.properties) { + if (created || !prop.is_primary) { + if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); + } + else if (created) { + if (Accessor::has_default_value_for_property(ctx, realm.get(), object_schema, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, realm.get(), object_schema, prop.name), try_update); + } + else { + throw MissingPropertyValueException(object_schema.name, prop.name, + "Missing property value for property " + prop.name); + } + } + } + } + return object; + } + + // + // List implementation + // + template + void List::add(ContextType ctx, ValueType value) + { + add(NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + } + + template + void List::insert(ContextType ctx, ValueType value, size_t list_ndx) + { + insert(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + } + + template + void List::set(ContextType ctx, ValueType value, size_t list_ndx) + { + set(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + } +} + +#endif /* defined(REALM_OBJECT_ACCESSOR_HPP) */ diff --git a/object_schema.hpp b/object_schema.hpp index 4ede6ce5..0ede3f5d 100644 --- a/object_schema.hpp +++ b/object_schema.hpp @@ -25,6 +25,7 @@ #include namespace realm { + class Property; class Group; struct Property; diff --git a/object_store.cpp b/object_store.cpp index 99364dc3..3e32b98c 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -328,6 +328,7 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update case PropertyTypeObject: case PropertyTypeArray: { TableRef link_table = ObjectStore::table_for_object_type(group, target_prop.object_type); + REALM_ASSERT(link_table); target_prop.table_column = table->add_column_link(DataType(target_prop.type), target_prop.name, *link_table); break; } @@ -525,8 +526,12 @@ DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string m_object_type(object_type), m_property(property) { m_what = "Primary key property '" + property.name + "' has duplicate values after migration."; -}; +} +DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property, const std::string message) : m_object_type(object_type), m_property(property) +{ + m_what = message; +} SchemaValidationException::SchemaValidationException(std::vector const& errors) : m_validation_errors(errors) @@ -607,3 +612,4 @@ DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const& { m_what = "Duplicate primary keys for object '" + object_type + "'."; } + diff --git a/object_store.hpp b/object_store.hpp index 3da680fb..60671f55 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -140,6 +140,8 @@ namespace realm { class DuplicatePrimaryKeyValueException : public MigrationException { public: DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property); + DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property, const std::string message); + std::string object_type() const { return m_object_type; } Property const& property() const { return m_property; } private: diff --git a/results.cpp b/results.cpp new file mode 100644 index 00000000..1977e5be --- /dev/null +++ b/results.cpp @@ -0,0 +1,44 @@ +/* Copyright 2015 Realm Inc - All Rights Reserved + * Proprietary and Confidential + */ + +#include "results.hpp" +#import + +using namespace realm; + +Results::Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s) : + realm(r), object_schema(o), backing_query(q), table_view(backing_query.find_all()) +{ + setSort(std::move(s)); +} + +size_t Results::size() +{ + verify_attached(); + return table_view.size(); +} + +void Results::setSort(SortOrder s) +{ + sort_order = std::make_unique(std::move(s)); + table_view.sort(sort_order->columnIndices, sort_order->ascending); +} + +Row Results::get(std::size_t row_ndx) +{ + verify_attached(); + if (row_ndx >= table_view.size()) { + throw std::out_of_range(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + + std::to_string(table_view.size()) + "."); + } + return table_view.get(row_ndx); +} + +void Results::verify_attached() +{ + if (!table_view.is_attached()) { + throw std::runtime_error("Tableview is not attached"); + } + table_view.sync_if_needed(); +} diff --git a/results.hpp b/results.hpp new file mode 100644 index 00000000..fff37124 --- /dev/null +++ b/results.hpp @@ -0,0 +1,39 @@ +/* Copyright 2015 Realm Inc - All Rights Reserved + * Proprietary and Confidential + */ + +#ifndef REALM_RESULTS_HPP +#define REALM_RESULTS_HPP + +#import "shared_realm.hpp" +#import + +namespace realm { + struct SortOrder { + std::vector columnIndices; + std::vector ascending; + + explicit operator bool() const { + return !columnIndices.empty(); + } + }; + + static SortOrder s_defaultSort = {{}, {}}; + + struct Results { + Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort); + size_t size(); + Row get(std::size_t row_ndx); + void verify_attached(); + + SharedRealm realm; + ObjectSchema &object_schema; + Query backing_query; + TableView table_view; + std::unique_ptr sort_order; + + void setSort(SortOrder s); + }; +} + +#endif /* REALM_RESULTS_HPP */ diff --git a/shared_realm.cpp b/shared_realm.cpp index c87dd5d4..9c0a4e1e 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -203,7 +203,9 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) auto migration_function = [&](Group*, Schema&) { SharedRealm old_realm(new Realm(old_config)); auto updated_realm = shared_from_this(); - m_config.migration_function(old_realm, updated_realm); + if (m_config.migration_function) { + m_config.migration_function(old_realm, updated_realm); + } }; try { @@ -285,8 +287,6 @@ void Realm::cancel_transaction() void Realm::invalidate() { verify_thread(); - check_read_write(this); - if (m_in_transaction) { cancel_transaction(); }