diff --git a/CMakeLists.txt b/CMakeLists.txt index 39b30790..0d75308f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") include(CompilerFlags) include(RealmCore) -set(REALM_CORE_VERSION "0.96.2" CACHE STRING "") +set(REALM_CORE_VERSION "0.97.0" CACHE STRING "") use_realm_core(${REALM_CORE_VERSION}) include_directories(${REALM_CORE_INCLUDE_DIR} src external/pegtl) diff --git a/README.md b/README.md index a641cb36..6776d5a5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Cross-platform code used accross bindings. Binding developers can choose to use The object store's build system currently only suports building for OS X. The object store itself can build for all Apple platforms when integrated into a binding. -1. Install CMake. You can download an installer for OS X from the [CMake download page], or install via [Homebrew](http://brew.sh): +1. Install CMake. You can download an installer for OS X from the [CMake download page](https://cmake.org/download/), or install via [Homebrew](http://brew.sh): ``` brew install cmake ``` diff --git a/src/impl/async_query.cpp b/src/impl/async_query.cpp index efb03699..ecee53c8 100644 --- a/src/impl/async_query.cpp +++ b/src/impl/async_query.cpp @@ -159,7 +159,7 @@ void AsyncQuery::run() if (m_initial_run_complete) { // Make an empty tableview from the query to get the table version, since // Query doesn't expose it - if (m_query->find_all(0, 0, 0).outside_version() == m_handed_over_table_version) { + if (m_query->find_all(0, 0, 0).sync_if_needed() == m_handed_over_table_version) { return; } } @@ -181,7 +181,7 @@ void AsyncQuery::prepare_handover() REALM_ASSERT(m_tv.is_in_sync()); m_initial_run_complete = true; - m_handed_over_table_version = m_tv.outside_version(); + m_handed_over_table_version = m_tv.sync_if_needed(); m_tv_handover = m_sg->export_for_handover(m_tv, MutableSourcePayload::Move); // detach the TableView as we won't need it again and keeping it around @@ -231,7 +231,7 @@ bool AsyncQuery::deliver(SharedGroup& sg, std::exception_ptr err) m_tv_handover->version = m_sg_version; Results::Internal::set_table_view(*m_target_results, std::move(*sg.import_from_handover(std::move(m_tv_handover)))); - m_delievered_table_version = m_handed_over_table_version; + m_delivered_table_version = m_handed_over_table_version; } REALM_ASSERT(!m_tv_handover); @@ -259,8 +259,8 @@ std::function AsyncQuery::next_callback() std::lock_guard callback_lock(m_callback_mutex); for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) { auto& callback = m_callbacks[m_callback_index]; - if (m_error || callback.delivered_version != m_delievered_table_version) { - callback.delivered_version = m_delievered_table_version; + if (m_error || callback.delivered_version != m_delivered_table_version) { + callback.delivered_version = m_delivered_table_version; return callback.fn; } } diff --git a/src/impl/async_query.hpp b/src/impl/async_query.hpp index 3f643d39..cf70b463 100644 --- a/src/impl/async_query.hpp +++ b/src/impl/async_query.hpp @@ -97,7 +97,7 @@ private: SharedGroup* m_sg = nullptr; uint_fast64_t m_handed_over_table_version = -1; - uint_fast64_t m_delievered_table_version = -1; + uint_fast64_t m_delivered_table_version = -1; // Iteration variable for looping over callbacks // remove_callback() updates this when needed diff --git a/src/impl/realm_coordinator.cpp b/src/impl/realm_coordinator.cpp index 3f587675..a1e34f8c 100644 --- a/src/impl/realm_coordinator.cpp +++ b/src/impl/realm_coordinator.cpp @@ -385,7 +385,7 @@ void RealmCoordinator::move_new_queries_to_main() void RealmCoordinator::advance_helper_shared_group_to_latest() { if (m_new_queries.empty()) { - LangBindHelper::advance_read(*m_query_sg, *m_query_history); + LangBindHelper::advance_read(*m_query_sg); return; } @@ -397,14 +397,13 @@ void RealmCoordinator::advance_helper_shared_group_to_latest() // Import all newly added queries to our helper SG for (auto& query : m_new_queries) { - LangBindHelper::advance_read(*m_advancer_sg, *m_advancer_history, query->version()); + LangBindHelper::advance_read(*m_advancer_sg, query->version()); query->attach_to(*m_advancer_sg); } // Advance both SGs to the newest version - LangBindHelper::advance_read(*m_advancer_sg, *m_advancer_history); - LangBindHelper::advance_read(*m_query_sg, *m_query_history, - m_advancer_sg->get_version_of_current_transaction()); + LangBindHelper::advance_read(*m_advancer_sg); + LangBindHelper::advance_read(*m_query_sg, m_advancer_sg->get_version_of_current_transaction()); // Transfer all new queries over to the main SG for (auto& query : m_new_queries) { @@ -421,7 +420,6 @@ void RealmCoordinator::advance_to_ready(Realm& realm) decltype(m_queries) queries; auto& sg = Realm::Internal::get_shared_group(realm); - auto& history = Realm::Internal::get_history(realm); auto get_query_version = [&] { for (auto& query : m_queries) { @@ -440,8 +438,8 @@ void RealmCoordinator::advance_to_ready(Realm& realm) } // no async queries; just advance to latest - if (version.version == 0) { - transaction::advance(sg, history, realm.m_binding_context.get()); + if (version.version == std::numeric_limits::max()) { + transaction::advance(sg, realm.m_binding_context.get()); return; } @@ -453,14 +451,14 @@ void RealmCoordinator::advance_to_ready(Realm& realm) while (true) { // Advance to the ready version without holding any locks because it // may end up calling user code (in did_change() notifications) - transaction::advance(sg, history, realm.m_binding_context.get(), version); + transaction::advance(sg, realm.m_binding_context.get(), version); // Reacquire the lock and recheck the query version, as the queries may // have advanced to a later version while we didn't hold the lock. If // so, we need to release the lock and re-advance std::lock_guard lock(m_query_mutex); version = get_query_version(); - if (version.version == 0) + if (version.version == std::numeric_limits::max()) return; if (version != sg.get_version_of_current_transaction()) continue; diff --git a/src/impl/realm_coordinator.hpp b/src/impl/realm_coordinator.hpp index 54aa9a41..05cf6d6c 100644 --- a/src/impl/realm_coordinator.hpp +++ b/src/impl/realm_coordinator.hpp @@ -25,7 +25,7 @@ namespace realm { class AsyncQueryCallback; -class ClientHistory; +class Replication; class Results; class Schema; class SharedGroup; @@ -65,10 +65,10 @@ public: // Should only be called in test code, as continuing to use the previously // cached instances will have odd results static void clear_cache(); - + // Clears all caches on existing coordinators static void clear_all_caches(); - + // Explicit constructor/destructor needed for the unique_ptrs to forward-declared types RealmCoordinator(); ~RealmCoordinator(); @@ -102,13 +102,13 @@ private: // SharedGroup used for actually running async queries // Will have a read transaction iff m_queries is non-empty - std::unique_ptr m_query_history; + std::unique_ptr m_query_history; std::unique_ptr m_query_sg; // SharedGroup used to advance queries in m_new_queries to the main shared // group's transaction version // Will have a read transaction iff m_new_queries is non-empty - std::unique_ptr m_advancer_history; + std::unique_ptr m_advancer_history; std::unique_ptr m_advancer_sg; std::exception_ptr m_async_error; diff --git a/src/impl/transact_log_handler.cpp b/src/impl/transact_log_handler.cpp index 44544d17..d99a7ea7 100644 --- a/src/impl/transact_log_handler.cpp +++ b/src/impl/transact_log_handler.cpp @@ -432,23 +432,21 @@ public: namespace realm { namespace _impl { namespace transaction { -void advance(SharedGroup& sg, ClientHistory& history, BindingContext* context, - SharedGroup::VersionID version) +void advance(SharedGroup& sg, BindingContext* context, SharedGroup::VersionID version) { TransactLogObserver(context, sg, [&](auto&&... args) { - LangBindHelper::advance_read(sg, history, std::move(args)...); + LangBindHelper::advance_read(sg, std::move(args)...); }, true); } -void begin(SharedGroup& sg, ClientHistory& history, BindingContext* context, - bool validate_schema_changes) +void begin(SharedGroup& sg, BindingContext* context, bool validate_schema_changes) { TransactLogObserver(context, sg, [&](auto&&... args) { - LangBindHelper::promote_to_write(sg, history, std::move(args)...); + LangBindHelper::promote_to_write(sg, std::move(args)...); }, validate_schema_changes); } -void commit(SharedGroup& sg, ClientHistory&, BindingContext* context) +void commit(SharedGroup& sg, BindingContext* context) { LangBindHelper::commit_and_continue_as_read(sg); @@ -457,10 +455,10 @@ void commit(SharedGroup& sg, ClientHistory&, BindingContext* context) } } -void cancel(SharedGroup& sg, ClientHistory& history, BindingContext* context) +void cancel(SharedGroup& sg, BindingContext* context) { TransactLogObserver(context, sg, [&](auto&&... args) { - LangBindHelper::rollback_and_continue_as_read(sg, history, std::move(args)...); + LangBindHelper::rollback_and_continue_as_read(sg, std::move(args)...); }, false); } diff --git a/src/impl/transact_log_handler.hpp b/src/impl/transact_log_handler.hpp index b68845fb..4249f8f3 100644 --- a/src/impl/transact_log_handler.hpp +++ b/src/impl/transact_log_handler.hpp @@ -24,27 +24,26 @@ namespace realm { class BindingContext; class SharedGroup; -class ClientHistory; 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, BindingContext* binding_context, +void advance(SharedGroup& sg, BindingContext* binding_context, SharedGroup::VersionID version=SharedGroup::VersionID{}); // 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, BindingContext* binding_context, +void begin(SharedGroup& sg, BindingContext* binding_context, bool validate_schema_changes=true); // Commit a write transaction -void commit(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); +void commit(SharedGroup& sg, 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, BindingContext* binding_context); +void cancel(SharedGroup& sg, BindingContext* binding_context); } // namespace transaction } // namespace _impl } // namespace realm diff --git a/src/object_store.hpp b/src/object_store.hpp index d7c78ac0..bfbc6acc 100644 --- a/src/object_store.hpp +++ b/src/object_store.hpp @@ -52,7 +52,7 @@ namespace realm { // determines if a realm with the given old schema needs non-migration // changes to make it compatible with the given target schema static bool needs_update(Schema const& old_schema, Schema const& schema); - + // updates a Realm from old_schema to the given target schema, creating and updating tables as needed // passed in target schema is updated with the correct column mapping // optionally runs migration function if schema is out of date diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 92fdc1ca..a16000ee 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -112,7 +112,7 @@ struct ParserState { return group_stack.back(); } - + Predicate *last_predicate() { Predicate *pred = current_group(); @@ -121,12 +121,12 @@ struct ParserState } return pred; } - + void add_predicate_to_current_group(Predicate::Type type) { current_group()->cpnd.sub_predicates.emplace_back(type, negate_next); negate_next = false; - + if (current_group()->cpnd.sub_predicates.size() > 1) { if (next_type == Predicate::Type::Or) { apply_or(); @@ -136,10 +136,10 @@ struct ParserState } } } - + bool negate_next = false; Predicate::Type next_type = Predicate::Type::And; - + void add_expression(Expression && exp) { Predicate *current = last_predicate(); @@ -151,32 +151,32 @@ struct ParserState last_predicate()->cmpr.expr[0] = std::move(exp); } } - + void apply_or() { Predicate *group = current_group(); if (group->type == Predicate::Type::Or) { return; } - + // convert to OR group->type = Predicate::Type::Or; if (group->cpnd.sub_predicates.size() > 2) { // split the current group into an AND group ORed with the last subpredicate Predicate new_sub(Predicate::Type::And); new_sub.cpnd.sub_predicates = std::move(group->cpnd.sub_predicates); - + group->cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) }; group->cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back(); } } - + void apply_and() { if (current_group()->type == Predicate::Type::And) { return; } - + auto &sub_preds = current_group()->cpnd.sub_predicates; auto second_last = sub_preds.end() - 2; if (second_last->type == Predicate::Type::And && !second_last->negate) { diff --git a/src/parser/query_builder.cpp b/src/parser/query_builder.cpp index 478bd52e..8a6ddf54 100644 --- a/src/parser/query_builder.cpp +++ b/src/parser/query_builder.cpp @@ -462,7 +462,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments if (pred.negate) { query.Not(); } - + switch (pred.type) { case Predicate::Type::And: query.group(); @@ -474,7 +474,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments } query.end_group(); break; - + case Predicate::Type::Or: query.group(); for (auto &sub : pred.cpnd.sub_predicates) { @@ -486,7 +486,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments } query.end_group(); break; - + case Predicate::Type::Comparison: { add_comparison_to_query(query, pred, arguments, schema, type); break; @@ -494,11 +494,11 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments case Predicate::Type::True: query.and_query(std::unique_ptr(new TrueExpression)); break; - + case Predicate::Type::False: query.and_query(std::unique_ptr(new FalseExpression)); break; - + default: throw std::runtime_error("Invalid predicate type"); } @@ -507,7 +507,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments void apply_predicate(Query &query, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType) { update_query_with_predicate(query, predicate, arguments, schema, objectType); - + // Test the constructed query in core std::string validateMessage = query.validate(); precondition(validateMessage.empty(), validateMessage.c_str()); diff --git a/src/property.hpp b/src/property.hpp index 05993ae1..fa8b0ec3 100644 --- a/src/property.hpp +++ b/src/property.hpp @@ -55,6 +55,7 @@ namespace realm { size_t table_column = -1; bool requires_index() const { return is_primary || is_indexed; } + bool is_indexable() const { return type == PropertyTypeInt || type == PropertyTypeBool || type == PropertyTypeString || type == PropertyTypeDate; } }; static inline const char *string_for_property_type(PropertyType type) { diff --git a/src/results.hpp b/src/results.hpp index fc761130..57767d86 100644 --- a/src/results.hpp +++ b/src/results.hpp @@ -83,10 +83,10 @@ public: // Get the Realm SharedRealm get_realm() const { return m_realm; } - + // Object schema describing the vendored object type const ObjectSchema &get_object_schema() const { return *m_object_schema; } - + // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty Query get_query() const; diff --git a/src/schema.cpp b/src/schema.cpp index 911dc4e5..3d3988d7 100644 --- a/src/schema.cpp +++ b/src/schema.cpp @@ -89,7 +89,7 @@ void Schema::validate() const // check indexable if (prop.is_indexed) { - if (prop.type != PropertyTypeString && prop.type != PropertyTypeInt) { + if (!prop.is_indexable()) { exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop)); } } diff --git a/src/shared_realm.cpp b/src/shared_realm.cpp index b02c954d..865cbe6b 100644 --- a/src/shared_realm.cpp +++ b/src/shared_realm.cpp @@ -72,7 +72,7 @@ Realm::Realm(Config config) } void Realm::open_with_config(const Config& config, - std::unique_ptr& history, + std::unique_ptr& history, std::unique_ptr& shared_group, std::unique_ptr& read_only_group) { @@ -211,9 +211,8 @@ void Realm::update_schema(std::unique_ptr schema, uint64_t version) } read_group(); - transaction::begin(*m_shared_group, *m_history, m_binding_context.get(), + transaction::begin(*m_shared_group, m_binding_context.get(), /* error on schema changes */ false); - m_in_transaction = true; struct WriteTransactionGuard { Realm& realm; @@ -289,20 +288,27 @@ void Realm::verify_in_write() const } } +bool Realm::is_in_transaction() const noexcept +{ + if (!m_shared_group) { + return false; + } + return m_shared_group->get_transact_stage() == SharedGroup::transact_Writing; +} + void Realm::begin_transaction() { check_read_write(this); verify_thread(); - if (m_in_transaction) { + if (is_in_transaction()) { throw InvalidTransactionException("The Realm is already in a write transaction"); } // make sure we have a read transaction read_group(); - transaction::begin(*m_shared_group, *m_history, m_binding_context.get()); - m_in_transaction = true; + transaction::begin(*m_shared_group, m_binding_context.get()); } void Realm::commit_transaction() @@ -310,12 +316,11 @@ void Realm::commit_transaction() check_read_write(this); verify_thread(); - if (!m_in_transaction) { + if (!is_in_transaction()) { throw InvalidTransactionException("Can't commit a non-existing write transaction"); } - m_in_transaction = false; - transaction::commit(*m_shared_group, *m_history, m_binding_context.get()); + transaction::commit(*m_shared_group, m_binding_context.get()); m_coordinator->send_commit_notifications(); } @@ -324,18 +329,19 @@ void Realm::cancel_transaction() check_read_write(this); verify_thread(); - if (!m_in_transaction) { + if (!is_in_transaction()) { throw InvalidTransactionException("Can't cancel a non-existing write transaction"); } - m_in_transaction = false; - transaction::cancel(*m_shared_group, *m_history, m_binding_context.get()); + transaction::cancel(*m_shared_group, m_binding_context.get()); } void Realm::invalidate() { verify_thread(); - if (m_in_transaction) { + check_read_write(this); + + if (is_in_transaction()) { cancel_transaction(); } if (!m_group) { @@ -353,7 +359,7 @@ bool Realm::compact() if (m_config.read_only) { throw InvalidTransactionException("Can't compact a read-only Realm"); } - if (m_in_transaction) { + if (is_in_transaction()) { throw InvalidTransactionException("Can't compact a Realm within a write transaction"); } @@ -395,7 +401,7 @@ bool Realm::refresh() check_read_write(this); // can't be any new changes if we're in a write transaction - if (m_in_transaction) { + if (is_in_transaction()) { return false; } @@ -405,7 +411,7 @@ bool Realm::refresh() } if (m_group) { - transaction::advance(*m_shared_group, *m_history, m_binding_context.get()); + transaction::advance(*m_shared_group, m_binding_context.get()); m_coordinator->process_available_async(*this); } else { diff --git a/src/shared_realm.hpp b/src/shared_realm.hpp index 128bd004..ecd1efae 100644 --- a/src/shared_realm.hpp +++ b/src/shared_realm.hpp @@ -32,7 +32,7 @@ namespace realm { class BindingContext; - class ClientHistory; + class Replication; class Group; class Realm; class RealmDelegate; @@ -114,7 +114,7 @@ namespace realm { void begin_transaction(); void commit_transaction(); void cancel_transaction(); - bool is_in_transaction() const { return m_in_transaction; } + bool is_in_transaction() const noexcept; bool is_in_read_transaction() const { return !!m_group; } bool refresh(); @@ -149,7 +149,6 @@ namespace realm { // AsyncQuery needs access to the SharedGroup to be able to call the // handover functions, which are not very wrappable static SharedGroup& get_shared_group(Realm& realm) { return *realm.m_shared_group; } - static ClientHistory& get_history(Realm& realm) { return *realm.m_history; } // AsyncQuery needs to be able to access the owning coordinator to // wake up the worker thread when a callback is added, and @@ -158,17 +157,16 @@ namespace realm { }; static void open_with_config(const Config& config, - std::unique_ptr& history, + std::unique_ptr& history, std::unique_ptr& shared_group, std::unique_ptr& read_only_group); private: Config m_config; std::thread::id m_thread_id = std::this_thread::get_id(); - bool m_in_transaction = false; bool m_auto_refresh = true; - std::unique_ptr m_history; + std::unique_ptr m_history; std::unique_ptr m_shared_group; std::unique_ptr m_read_only_group; @@ -231,7 +229,7 @@ namespace realm { public: UnitializedRealmException(std::string message) : std::runtime_error(message) {} }; - + class InvalidEncryptionKeyException : public std::runtime_error { public: InvalidEncryptionKeyException() : std::runtime_error("Encryption key must be 64 bytes.") {}