From 8ad79f1918bc76806cfc8334152e40939e94b15d Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 13 Aug 2015 09:28:53 -0700 Subject: [PATCH 01/57] missing files and project fixes --- object_accessor.cpp | 19 +++++ object_accessor.hpp | 196 ++++++++++++++++++++++++++++++++++++++++++++ results.cpp | 38 +++++++++ results.hpp | 29 +++++++ 4 files changed, 282 insertions(+) create mode 100644 object_accessor.cpp create mode 100644 object_accessor.hpp create mode 100644 results.cpp create mode 100644 results.hpp diff --git a/object_accessor.cpp b/object_accessor.cpp new file mode 100644 index 00000000..2516cc5d --- /dev/null +++ b/object_accessor.cpp @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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_accessor.hpp" diff --git a/object_accessor.hpp b/object_accessor.hpp new file mode 100644 index 00000000..fdc33462 --- /dev/null +++ b/object_accessor.hpp @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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_ACCESSOR_HPP +#define REALM_OBJECT_ACCESSOR_HPP + +#include +#include "shared_realm.hpp" + +namespace realm { + template + class NativeAccessor { + public: + // + // Value converters - template specializations must be implemented for each platform + // + static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); + + static bool to_bool(ContextType ctx, ValueType &val); + static long long to_long(ContextType ctx, ValueType &val); + static float to_float(ContextType ctx, ValueType &val); + static double to_double(ContextType ctx, ValueType &val); + static std::string to_string(ContextType ctx, ValueType &val); + static DateTime to_datetime(ContextType ctx, ValueType &val); + + static bool is_null(ContextType ctx, ValueType &val); + + // 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, Property &prop, bool try_update); + + // array value acessors + static size_t array_size(ContextType ctx, ValueType &val); + static ValueType array_value_at_index(ContextType ctx, ValueType &val, size_t index); + + // + // Deprecated + // + static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); } + }; + + class Object { + public: + Object(SharedRealm &r, ObjectSchema &s, Row o) : realm(r), object_schema(s), row(o) {} + // FIXME - all should be const + SharedRealm realm; + ObjectSchema &object_schema; + Row row; + + // property setter + template + inline void set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update); + + // create an Object from a native representation + template + static inline Object create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update); + + private: + template + inline void set_property_value_impl(ContextType ctx, Property &property, ValueType value, bool try_update); + }; + + // + // template method implementations + // + template + inline void Object::set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update) + { + Property *prop = object_schema.property_for_name(prop_name); + if (!prop) { + throw std::runtime_error("Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); + } + set_property_value_impl(ctx, *prop, value, try_update); + }; + + template + inline void Object::set_property_value_impl(ContextType ctx, Property &property, ValueType value, bool try_update) + { + using Accessor = NativeAccessor; + + size_t column = property.table_column; + switch (property.type) { + case PropertyTypeBool: + row.set_bool(column, Accessor::to_bool(ctx, value)); + break; + case PropertyTypeInt: + row.set_int(column, Accessor::to_long(ctx, value)); + break; + case PropertyTypeFloat: + row.set_float(column, Accessor::to_float(ctx, value)); + break; + case PropertyTypeDouble: + row.set_double(column, Accessor::to_double(ctx, value)); + break; + case PropertyTypeString: + row.set_string(column, Accessor::to_string(ctx, value)); + break; + case PropertyTypeData: + row.set_binary(column, BinaryData(Accessor::to_string(ctx, value))); + break; + case PropertyTypeAny: + row.set_mixed(column, Accessor::to_mixed(ctx, value)); + break; + case PropertyTypeDate: + row.set_datetime(column, Accessor::to_datetime(ctx, value)); + break; + case PropertyTypeObject: { + if (Accessor::is_null(ctx, value)) { + row.nullify_link(column); + } + else { + row.set_link(column, Accessor::to_object_index(ctx, realm, value, property, try_update)); + } + break; + } + case PropertyTypeArray: { + realm::LinkViewRef link_view = row.get_linklist(column); + link_view->clear(); + size_t count = Accessor::array_size(ctx, value); + for (size_t i = 0; i < count; i++) { + ValueType element = Accessor::array_value_at_index(ctx, value, i); + link_view->add(Accessor::to_object_index(ctx, realm, element, property, try_update)); + } + break; + } + } + } + + 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 std::runtime_error("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); + Property *primary_prop = object_schema.primary_key_property(); + if (try_update && 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 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) { + ValueType prop_value = Accessor::dict_value_for_key(ctx, value, prop.name); + if (prop_value) { + if (created || !prop.is_primary) { + object.set_property_value_impl(ctx, prop, prop_value, try_update); + } + } + else if (created) { + throw std::runtime_error("Missing property value for property " + prop.name); + } + } + return object; + } +} + +#endif /* defined(REALM_OBJECT_ACCESSOR_HPP) */ diff --git a/results.cpp b/results.cpp new file mode 100644 index 00000000..9d4d1b2f --- /dev/null +++ b/results.cpp @@ -0,0 +1,38 @@ +// +// results.cpp +// RealmJS +// +// Created by Ari Lazier on 7/31/15. +// Copyright (c) 2015 Realm. All rights reserved. +// + +#include "results.hpp" +#import + +using namespace realm; + +Results::Results(SharedRealm &r, ObjectSchema &o, Query q) : + realm(r), object_schema(o), backing_query(q), table_view(backing_query.find_all()) +{ +} + +size_t Results::size() { + verify_attached(); + return table_view.size(); +} + +Row Results::get(std::size_t row_ndx) { + verify_attached(); + if (row_ndx >= table_view.size()) { + throw std::range_error(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(); +} \ No newline at end of file diff --git a/results.hpp b/results.hpp new file mode 100644 index 00000000..39a6278f --- /dev/null +++ b/results.hpp @@ -0,0 +1,29 @@ +// +// results.h +// RealmJS +// +// Created by Ari Lazier on 7/31/15. +// Copyright (c) 2015 Realm. All rights reserved. +// + +#ifndef REALM_RESULTS_HPP +#define REALM_RESULTS_HPP + +#import "shared_realm.hpp" +#import + +namespace realm { + struct Results { + Results(SharedRealm &r, ObjectSchema &o, Query q); + size_t size(); + Row get(std::size_t row_ndx); + void verify_attached(); + + SharedRealm realm; + ObjectSchema &object_schema; + Query backing_query; + TableView table_view; + }; +} + +#endif /* REALM_RESULTS_HPP */ From 6b7919475655cf7206a47919af357ef689577df3 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 14 Aug 2015 08:18:49 -0700 Subject: [PATCH 02/57] proper copyright --- results.cpp | 18 ++++++++++++++---- results.hpp | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/results.cpp b/results.cpp index 9d4d1b2f..67116e8e 100644 --- a/results.cpp +++ b/results.cpp @@ -1,10 +1,20 @@ +//////////////////////////////////////////////////////////////////////////// // -// results.cpp -// RealmJS +// Copyright 2015 Realm Inc. // -// Created by Ari Lazier on 7/31/15. -// Copyright (c) 2015 Realm. All rights reserved. +// 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 "results.hpp" #import diff --git a/results.hpp b/results.hpp index 39a6278f..a58136c9 100644 --- a/results.hpp +++ b/results.hpp @@ -1,10 +1,20 @@ +//////////////////////////////////////////////////////////////////////////// // -// results.h -// RealmJS +// Copyright 2015 Realm Inc. // -// Created by Ari Lazier on 7/31/15. -// Copyright (c) 2015 Realm. All rights reserved. +// 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_RESULTS_HPP #define REALM_RESULTS_HPP From 6d1c000d0951fa880ceeb76f1e4a5cd1260cf580 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 14 Aug 2015 10:47:56 -0700 Subject: [PATCH 03/57] test existing array functionality --- results.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/results.cpp b/results.cpp index 67116e8e..6b240f59 100644 --- a/results.cpp +++ b/results.cpp @@ -45,4 +45,4 @@ void Results::verify_attached() { throw std::runtime_error("Tableview is not attached"); } table_view.sync_if_needed(); -} \ No newline at end of file +} From 942af6a754041d58307f5dc7c3aaaaeb9d901186 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 24 Aug 2015 09:22:37 -0700 Subject: [PATCH 04/57] todo example --- object_accessor.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index fdc33462..c7f92022 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -43,7 +43,7 @@ namespace realm { // 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, Property &prop, bool try_update); + static size_t to_object_index(ContextType ctx, SharedRealm &realm, ValueType &val, std::string &type, bool try_update); // array value acessors static size_t array_size(ContextType ctx, ValueType &val); @@ -125,7 +125,7 @@ namespace realm { row.nullify_link(column); } else { - row.set_link(column, Accessor::to_object_index(ctx, realm, value, property, try_update)); + row.set_link(column, Accessor::to_object_index(ctx, realm, value, property.object_type, try_update)); } break; } @@ -135,7 +135,7 @@ namespace realm { size_t count = Accessor::array_size(ctx, value); for (size_t i = 0; i < count; i++) { ValueType element = Accessor::array_value_at_index(ctx, value, i); - link_view->add(Accessor::to_object_index(ctx, realm, element, property, try_update)); + link_view->add(Accessor::to_object_index(ctx, realm, element, property.object_type, try_update)); } break; } From 9ca8e46928a8961f6d247024e133f8967b6fb2f7 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 3 Sep 2015 14:05:56 -0700 Subject: [PATCH 05/57] primary keys and upsert --- object_accessor.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index c7f92022..3e5e3a20 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -158,7 +158,7 @@ namespace realm { size_t row_index = realm::not_found; realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name); Property *primary_prop = object_schema.primary_key_property(); - if (try_update && primary_prop) { + 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) { @@ -167,6 +167,10 @@ namespace realm { 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 std::runtime_error("Attempting to create an object of type '" + object_schema.name + "' with an exising primary key value."); + } } // if no existing, create row From e2836f77f9d3146daeefa5af5a4d6e7b95d05edb Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 3 Sep 2015 14:37:22 -0700 Subject: [PATCH 06/57] fix for partial update of string properties --- object_accessor.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index 3e5e3a20..f318a2f5 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -29,6 +29,7 @@ namespace realm { // // Value converters - template specializations must be implemented for each platform // + 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 to_bool(ContextType ctx, ValueType &val); @@ -183,9 +184,9 @@ namespace realm { // populate Object object(realm, object_schema, table->get(row_index)); for (Property &prop : object_schema.properties) { - ValueType prop_value = Accessor::dict_value_for_key(ctx, value, prop.name); - if (prop_value) { + if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { if (created || !prop.is_primary) { + ValueType prop_value = Accessor::dict_value_for_key(ctx, value, prop.name); object.set_property_value_impl(ctx, prop, prop_value, try_update); } } From beb7c19931751c91223c0abcff3c9097127d6f86 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 3 Sep 2015 15:46:31 -0700 Subject: [PATCH 07/57] support sorting by a single property --- results.cpp | 18 ++++++++++++++---- results.hpp | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/results.cpp b/results.cpp index 6b240f59..f33fc465 100644 --- a/results.cpp +++ b/results.cpp @@ -21,17 +21,26 @@ using namespace realm; -Results::Results(SharedRealm &r, ObjectSchema &o, Query q) : +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() { +size_t Results::size() +{ verify_attached(); return table_view.size(); } -Row Results::get(std::size_t row_ndx) { +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::range_error(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + @@ -40,7 +49,8 @@ Row Results::get(std::size_t row_ndx) { return table_view.get(row_ndx); } -void Results::verify_attached() { +void Results::verify_attached() +{ if (!table_view.is_attached()) { throw std::runtime_error("Tableview is not attached"); } diff --git a/results.hpp b/results.hpp index a58136c9..fcb3e894 100644 --- a/results.hpp +++ b/results.hpp @@ -23,8 +23,19 @@ #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); + Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort); size_t size(); Row get(std::size_t row_ndx); void verify_attached(); @@ -33,6 +44,9 @@ namespace realm { ObjectSchema &object_schema; Query backing_query; TableView table_view; + std::unique_ptr sort_order; + + void setSort(SortOrder s); }; } From a099682f0e502e5d04826c49a7d431fb9c3b6938 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 4 Sep 2015 12:41:17 -0700 Subject: [PATCH 08/57] more extensive upsert tests --- object_store.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/object_store.cpp b/object_store.cpp index 3d1ec9c3..17ccd41c 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -275,6 +275,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; } From 591ec90e67525ba0e0c274353304157b541865fc Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 4 Sep 2015 15:43:26 -0700 Subject: [PATCH 09/57] support defaults --- object_accessor.hpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index f318a2f5..dfb24f69 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -32,6 +32,9 @@ namespace realm { 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, const ObjectSchema &object_schema, const std::string &prop_name); + static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); + static bool to_bool(ContextType ctx, ValueType &val); static long long to_long(ContextType ctx, ValueType &val); static float to_float(ContextType ctx, ValueType &val); @@ -184,14 +187,18 @@ namespace realm { // populate Object object(realm, object_schema, table->get(row_index)); for (Property &prop : object_schema.properties) { - if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { - if (created || !prop.is_primary) { - ValueType prop_value = Accessor::dict_value_for_key(ctx, value, prop.name); - object.set_property_value_impl(ctx, prop, prop_value, try_update); + 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, object_schema, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update); + } + else { + throw std::runtime_error("Missing property value for property " + prop.name); + } } - } - else if (created) { - throw std::runtime_error("Missing property value for property " + prop.name); } } return object; From 5c1e20a7b1883ce862c098350025733ad5e6496a Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 11:51:19 -0700 Subject: [PATCH 10/57] Store a copy of the encryption key --- shared_realm.cpp | 16 +++++++++++----- shared_realm.hpp | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index a7269591..fc5eaa2e 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -17,9 +17,11 @@ //////////////////////////////////////////////////////////////////////////// #include "shared_realm.hpp" + +#include #include #include -#include + #include using namespace realm; @@ -27,25 +29,29 @@ using namespace realm; RealmCache Realm::s_global_cache; 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) +Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), in_memory(c.in_memory), schema_version(c.schema_version), migration_function(c.migration_function) { if (c.schema) { schema = std::make_unique(*c.schema); } + if (c.encryption_key) { + encryption_key = std::make_unique(64); + memcpy(encryption_key.get(), c.encryption_key.get(), 64); + } } Realm::Realm(Config &config) : m_config(config), m_thread_id(std::this_thread::get_id()), m_auto_refresh(true), m_in_transaction(false) { try { if (config.read_only) { - m_read_only_group = std::make_unique(config.path, config.encryption_key.data(), Group::mode_ReadOnly); + m_read_only_group = std::make_unique(config.path, config.encryption_key.get(), Group::mode_ReadOnly); m_group = m_read_only_group.get(); } else { - m_history = realm::make_client_history(config.path, config.encryption_key.data()); + m_history = realm::make_client_history(config.path, config.encryption_key.get()); SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly : SharedGroup::durability_Full; - m_shared_group = std::make_unique(*m_history, durability, config.encryption_key.data()); + m_shared_group = std::make_unique(*m_history, durability, config.encryption_key.get()); m_group = nullptr; } } diff --git a/shared_realm.hpp b/shared_realm.hpp index af8b774a..d3c19263 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -45,7 +45,7 @@ namespace realm { std::string path; bool read_only; bool in_memory; - StringData encryption_key; + std::unique_ptr encryption_key; std::unique_ptr schema; uint64_t schema_version; From d6566ff3c1ca58e213f9790855dae9ed1a8259d9 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 24 Aug 2015 13:12:49 -0700 Subject: [PATCH 11/57] Use NSDMIs for Realm::Config and make it moveable --- shared_realm.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/shared_realm.hpp b/shared_realm.hpp index d3c19263..5344f1ef 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -43,16 +43,17 @@ namespace realm { struct Config { std::string path; - bool read_only; - bool in_memory; + bool read_only = false; + bool in_memory = false; std::unique_ptr encryption_key; std::unique_ptr schema; - uint64_t schema_version; + uint64_t schema_version = ObjectStore::NotVersioned; MigrationFunction migration_function; - Config() : read_only(false), in_memory(false), schema_version(ObjectStore::NotVersioned) {}; + Config() = default; + Config(Config&&) = default; Config(const Config& c); }; From 9c224fb1415090fa5e3e4a0867295d354d30191e Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 24 Aug 2015 13:14:59 -0700 Subject: [PATCH 12/57] Reduce s_init_mutex's scope --- shared_realm.cpp | 3 ++- shared_realm.hpp | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index fc5eaa2e..fa921fe7 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -23,11 +23,11 @@ #include #include +#include using namespace realm; RealmCache Realm::s_global_cache; -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), migration_function(c.migration_function) { @@ -108,6 +108,7 @@ SharedRealm Realm::get_shared_realm(Config &config) realm = SharedRealm(new Realm(config)); // we want to ensure we are only initializing a single realm at a time + static std::mutex s_init_mutex; std::lock_guard lock(s_init_mutex); uint64_t old_version = ObjectStore::get_schema_version(realm->read_group()); diff --git a/shared_realm.hpp b/shared_realm.hpp index 5344f1ef..31c0a79b 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -117,8 +116,6 @@ namespace realm { Group *m_group; - static std::mutex s_init_mutex; - public: ExternalNotificationFunction m_external_notifier; From 6a491eaf1b1e5f6efb692010e86d29c73e2bcdd6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 24 Aug 2015 13:16:31 -0700 Subject: [PATCH 13/57] Use NSDMIs for Realm --- shared_realm.cpp | 3 +-- shared_realm.hpp | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index fa921fe7..df89301c 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -40,7 +40,7 @@ Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), i } } -Realm::Realm(Config &config) : m_config(config), m_thread_id(std::this_thread::get_id()), m_auto_refresh(true), m_in_transaction(false) +Realm::Realm(Config &config) : m_config(config) { try { if (config.read_only) { @@ -52,7 +52,6 @@ Realm::Realm(Config &config) : m_config(config), m_thread_id(std::this_thread::g SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly : SharedGroup::durability_Full; m_shared_group = std::make_unique(*m_history, durability, config.encryption_key.get()); - m_group = nullptr; } } catch (util::File::PermissionDenied const& ex) { diff --git a/shared_realm.hpp b/shared_realm.hpp index 31c0a79b..3ddfb5a5 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -100,9 +100,9 @@ namespace realm { Realm(Config &config); Config m_config; - std::thread::id m_thread_id; - bool m_in_transaction; - bool m_auto_refresh; + std::thread::id m_thread_id = std::this_thread::get_id(); + bool m_in_transaction = false; + bool m_auto_refresh = true; std::set m_notifications; void send_local_notifications(const std::string ¬ification); @@ -114,7 +114,7 @@ namespace realm { std::unique_ptr m_shared_group; std::unique_ptr m_read_only_group; - Group *m_group; + Group *m_group = nullptr; public: ExternalNotificationFunction m_external_notifier; From 06ce25053d1088f788763afbc11142433e60b6b4 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 24 Aug 2015 13:22:42 -0700 Subject: [PATCH 14/57] Eliminate some copies --- shared_realm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index df89301c..fe97d8b3 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -299,7 +299,7 @@ void Realm::notify() void Realm::send_local_notifications(const std::string &type) { verify_thread(); - for (NotificationFunction notification : m_notifications) { + for (NotificationFunction const& notification : m_notifications) { (*notification)(type); } } From c6d82ad436203103c36e0190d6c68ede8ed00eb8 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Aug 2015 11:32:13 -0700 Subject: [PATCH 15/57] Don't cache dynamic realms in the ObjectStore cache either --- shared_realm.cpp | 6 ++++-- shared_realm.hpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index fe97d8b3..7a2e7be0 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -29,7 +29,7 @@ using namespace realm; RealmCache Realm::s_global_cache; -Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), in_memory(c.in_memory), schema_version(c.schema_version), migration_function(c.migration_function) +Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), in_memory(c.in_memory), cache(c.cache), schema_version(c.schema_version), migration_function(c.migration_function) { if (c.schema) { schema = std::make_unique(*c.schema); @@ -132,7 +132,9 @@ SharedRealm Realm::get_shared_realm(Config &config) realm->update_schema(*realm->m_config.schema, realm->m_config.schema_version); } - s_global_cache.cache_realm(realm, realm->m_thread_id); + if (config.cache) { + s_global_cache.cache_realm(realm, realm->m_thread_id); + } return realm; } diff --git a/shared_realm.hpp b/shared_realm.hpp index 3ddfb5a5..5c74e13d 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -44,6 +44,7 @@ namespace realm { std::string path; bool read_only = false; bool in_memory = false; + bool cache = true; std::unique_ptr encryption_key; std::unique_ptr schema; From 169bdb66480ea61c751429236e12b0bc71617cd6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Aug 2015 13:20:06 -0700 Subject: [PATCH 16/57] Use NSDMIs for realm::Property --- property.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/property.hpp b/property.hpp index 22e6f90c..893f772d 100644 --- a/property.hpp +++ b/property.hpp @@ -46,15 +46,12 @@ 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; - bool is_primary; - bool is_indexed; - bool is_nullable; + bool is_primary = false; + bool is_indexed = false; + bool is_nullable = false; size_t table_column; bool requires_index() { return is_primary || is_indexed; } From 6b43c4ca3152fe2271b735f098a1e1405a470c69 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 25 Aug 2015 16:39:32 -0700 Subject: [PATCH 17/57] Convert RLMRealmConfiguration to a wrapper around Realm::Config --- shared_realm.cpp | 60 +++++++++++++++++++++++++++++------------------- shared_realm.hpp | 29 +++++++++++++---------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 7a2e7be0..c46b654e 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -22,47 +22,57 @@ #include #include -#include #include using namespace realm; RealmCache Realm::s_global_cache; -Realm::Config::Config(const Config& c) : path(c.path), read_only(c.read_only), in_memory(c.in_memory), cache(c.cache), schema_version(c.schema_version), migration_function(c.migration_function) +Realm::Config::Config(const Config& c) +: path(c.path) +, read_only(c.read_only) +, in_memory(c.in_memory) +, cache(c.cache) +, encryption_key(c.encryption_key) +, schema_version(c.schema_version) +, migration_function(c.migration_function) { if (c.schema) { schema = std::make_unique(*c.schema); } - if (c.encryption_key) { - encryption_key = std::make_unique(64); - memcpy(encryption_key.get(), c.encryption_key.get(), 64); - } } -Realm::Realm(Config &config) : m_config(config) +Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) +{ + if (&c != this) { + *this = Config(c); + } + return *this; +} + +Realm::Realm(Config config) : m_config(std::move(config)) { try { - if (config.read_only) { - m_read_only_group = std::make_unique(config.path, config.encryption_key.get(), Group::mode_ReadOnly); + if (m_config.read_only) { + m_read_only_group = std::make_unique(m_config.path, m_config.encryption_key.data(), Group::mode_ReadOnly); m_group = m_read_only_group.get(); } else { - m_history = realm::make_client_history(config.path, config.encryption_key.get()); - SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly : - SharedGroup::durability_Full; - m_shared_group = std::make_unique(*m_history, durability, config.encryption_key.get()); + m_history = realm::make_client_history(m_config.path, m_config.encryption_key.data()); + SharedGroup::DurabilityLevel durability = m_config.in_memory ? SharedGroup::durability_MemOnly : + SharedGroup::durability_Full; + m_shared_group = std::make_unique(*m_history, durability, m_config.encryption_key.data()); } } catch (util::File::PermissionDenied const& ex) { - 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."); + throw RealmFileException(RealmFileException::Kind::PermissionDenied, "Unable to open a realm at path '" + m_config.path + + "'. Please use a path where your app has " + (m_config.read_only ? "read" : "read-write") + " permissions."); } catch (util::File::Exists const& ex) { - throw RealmFileException(RealmFileException::Kind::Exists, "Unable to open a realm at path '" + config.path + "'"); + throw RealmFileException(RealmFileException::Kind::Exists, "Unable to open a realm at path '" + m_config.path + "'"); } catch (util::File::AccessError const& ex) { - throw RealmFileException(RealmFileException::Kind::AccessError, "Unable to open a realm at path '" + config.path + "'"); + throw RealmFileException(RealmFileException::Kind::AccessError, "Unable to open a realm at path '" + m_config.path + "'"); } catch (IncompatibleLockFile const&) { throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, "Realm file is currently open in another process " @@ -78,7 +88,7 @@ Group *Realm::read_group() return m_group; } -SharedRealm Realm::get_shared_realm(Config &config) +SharedRealm Realm::get_shared_realm(Config config) { SharedRealm realm = s_global_cache.get_realm(config.path); if (realm) { @@ -104,7 +114,7 @@ SharedRealm Realm::get_shared_realm(Config &config) return realm; } - realm = SharedRealm(new Realm(config)); + realm = SharedRealm(new Realm(std::move(config))); // we want to ensure we are only initializing a single realm at a time static std::mutex s_init_mutex; @@ -153,13 +163,17 @@ bool Realm::update_schema(Schema &schema, uint64_t version) if (!m_config.read_only && 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(); + old_config.schema_version = ObjectStore::get_schema_version(read_group()); + old_config.schema = std::make_unique(ObjectStore::schema_from_group(read_group())); + SharedRealm old_realm(new Realm(old_config)); + auto 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); - }); + 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 { diff --git a/shared_realm.hpp b/shared_realm.hpp index 5c74e13d..6f868a09 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -19,11 +19,11 @@ #ifndef REALM_REALM_HPP #define REALM_REALM_HPP +#include #include +#include #include #include -#include -#include #include "object_store.hpp" @@ -45,7 +45,7 @@ namespace realm { bool read_only = false; bool in_memory = false; bool cache = true; - std::unique_ptr encryption_key; + std::vector encryption_key; std::unique_ptr schema; uint64_t schema_version = ObjectStore::NotVersioned; @@ -55,18 +55,23 @@ namespace realm { Config() = default; Config(Config&&) = default; Config(const Config& c); + + Config& operator=(Config const&); + Config& operator=(Config&&) = default; }; // Get a cached Realm or create a new one if no cached copies exists - // Caching is done by path - mismatches for inMemory and readOnly Config properties - // will raise an exception - // If schema/schema_version is specified, update_schema is called automatically on the realm - // and a migration is performed. If not specified, the schema version and schema are dynamically - // read from the the existing Realm. - static SharedRealm get_shared_realm(Config &config); + // Caching is done by path - mismatches for in_memory and read_only + // Config properties will raise an exception + // If schema/schema_version is specified, update_schema is called + // automatically on the realm and a migration is performed. If not + // specified, the schema version and schema are dynamically read from + // the the existing Realm. + static SharedRealm get_shared_realm(Config config); - // Updates a Realm to a given target schema/version creating tables and updating indexes as necessary - // Uses the existing migration function on the Config, and the resulting Schema and version with updated + // Updates a Realm to a given target schema/version creating tables and + // updating indexes as necessary. 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(Schema &schema, uint64_t version); @@ -98,7 +103,7 @@ namespace realm { const std::string DidChangeNotification = "DidChangeNotification"; private: - Realm(Config &config); + Realm(Config config); Config m_config; std::thread::id m_thread_id = std::this_thread::get_id(); From bf3d9bd45263ad35f7980d07cfb2234c848070d6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Aug 2015 14:53:39 -0700 Subject: [PATCH 18/57] Remove property.hpp include from object_schema.hpp --- object_schema.cpp | 4 ++++ object_schema.hpp | 6 +++--- object_store.hpp | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/object_schema.cpp b/object_schema.cpp index 83edd1f1..41e34685 100644 --- a/object_schema.cpp +++ b/object_schema.cpp @@ -17,13 +17,17 @@ //////////////////////////////////////////////////////////////////////////// #include "object_schema.hpp" + #include "object_store.hpp" +#include "property.hpp" #include #include using namespace realm; +ObjectSchema::~ObjectSchema() = default; + ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) { TableRef tableRef = ObjectStore::table_for_object_type(group, name); Table *table = tableRef.get(); diff --git a/object_schema.hpp b/object_schema.hpp index 56a14d17..51a4c584 100644 --- a/object_schema.hpp +++ b/object_schema.hpp @@ -22,14 +22,14 @@ #include #include -#include "property.hpp" - namespace realm { + class Property; class Group; class ObjectSchema { public: - ObjectSchema() {} + ObjectSchema() = default; + ~ObjectSchema(); // create object schema from existing table // if no table is provided it is looked up in the group diff --git a/object_store.hpp b/object_store.hpp index cacdcd8b..86bbe3ff 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -26,6 +26,7 @@ #include #include "object_schema.hpp" +#include "property.hpp" namespace realm { class ObjectSchemaValidationException; From 6df371992529eddd2ffa7f37ee38e566a21a6df5 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Aug 2015 15:07:15 -0700 Subject: [PATCH 19/57] Change realm::Schema to a vector rather than a map Much faster to copy and destroy with no loss in lookup performance. --- object_store.cpp | 48 ++++++++++++++++++++++++++++++------------------ object_store.hpp | 4 +--- shared_realm.cpp | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 17ccd41c..908ddbec 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -140,18 +140,24 @@ 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 compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) { + return lft.name < rgt.name; +} + void ObjectStore::verify_schema(Group *group, Schema &target_schema, bool allow_missing_tables) { + std::sort(begin(target_schema), end(target_schema), compare_by_name); + std::vector errors; for (auto &object_schema : target_schema) { - if (!table_for_object_type(group, object_schema.first)) { + if (!table_for_object_type(group, object_schema.name)) { if (!allow_missing_tables) { - errors.emplace_back(ObjectSchemaValidationException(object_schema.first, - "Missing table for object type '" + object_schema.first + "'.")); + errors.emplace_back(ObjectSchemaValidationException(object_schema.name, + "Missing table for object type '" + object_schema.name + "'.")); } continue; } - auto more_errors = verify_object_schema(group, object_schema.second, target_schema); + auto more_errors = verify_object_schema(group, object_schema, target_schema); errors.insert(errors.end(), more_errors.begin(), more_errors.end()); } if (errors.size()) { @@ -163,6 +169,12 @@ std::vector ObjectStore::verify_object_schema(G std::vector exceptions; ObjectSchema table_schema(group, target_schema.name); + ObjectSchema cmp; + auto schema_contains_table = [&](std::string const& name) { + cmp.name = name; + return std::binary_search(begin(schema), end(schema), cmp, compare_by_name); + }; + // check to see if properties are the same Property *primary = nullptr; for (auto& current_prop : table_schema.properties) { @@ -178,7 +190,7 @@ std::vector ObjectStore::verify_object_schema(G } // check object_type existence - if (current_prop.object_type.length() && schema.find(current_prop.object_type) == schema.end()) { + if (current_prop.object_type.length() && !schema_contains_table(current_prop.object_type)) { exceptions.emplace_back(MissingObjectTypeException(table_schema.name, current_prop)); } @@ -249,11 +261,11 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update std::vector to_update; for (auto& object_schema : target_schema) { bool created = false; - ObjectStore::table_for_object_type_create_if_needed(group, object_schema.first, created); + ObjectStore::table_for_object_type_create_if_needed(group, object_schema.name, 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.second); + to_update.push_back(&object_schema); changed = true; } } @@ -330,7 +342,7 @@ bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema & return true; } for (auto& target_schema : schema) { - TableRef table = table_for_object_type(group, target_schema.first); + TableRef table = table_for_object_type(group, target_schema.name); if (!table) { return true; } @@ -378,7 +390,7 @@ Schema ObjectStore::schema_from_group(Group *group) { 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(object_type, std::move(ObjectSchema(group, object_type))); + schema.emplace_back(group, object_type); } } return schema; @@ -386,13 +398,13 @@ 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.first); + TableRef table = table_for_object_type(group, object_schema.name); if (!table) { continue; } - update_column_mapping(group, object_schema.second); - for (auto& property : object_schema.second.properties) { + update_column_mapping(group, object_schema); + for (auto& property : object_schema.properties) { if (property.requires_index() != table->has_search_index(property.table_column)) { return false; } @@ -404,12 +416,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.first); + TableRef table = table_for_object_type(group, object_schema.name); if (!table) { continue; } - for (auto& property : object_schema.second.properties) { + for (auto& property : object_schema.properties) { if (property.requires_index() == table->has_search_index(property.table_column)) { continue; } @@ -420,7 +432,7 @@ bool ObjectStore::update_indexes(Group *group, Schema &schema) { table->add_search_index(property.table_column); } catch (LogicError const&) { - throw PropertyTypeNotIndexableException(object_schema.first, property); + throw PropertyTypeNotIndexableException(object_schema.name, property); } } else { @@ -433,14 +445,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.second.primary_key_property(); + auto primary_prop = object_schema.primary_key_property(); if (!primary_prop) { continue; } - TableRef table = table_for_object_type(group, object_schema.first); + TableRef table = table_for_object_type(group, object_schema.name); if (table->get_distinct_view(primary_prop->table_column).size() != table->size()) { - throw DuplicatePrimaryKeyValueException(object_schema.first, *primary_prop); + throw DuplicatePrimaryKeyValueException(object_schema.name, *primary_prop); } } } diff --git a/object_store.hpp b/object_store.hpp index 86bbe3ff..42b8749e 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,7 +19,6 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP -#include #include #include #include @@ -30,8 +29,7 @@ namespace realm { class ObjectSchemaValidationException; - class Schema : public std::map { - }; + using Schema = std::vector; class ObjectStore { public: diff --git a/shared_realm.cpp b/shared_realm.cpp index c46b654e..9d4a4a7f 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -284,7 +284,7 @@ bool Realm::compact() } for (auto &object_schema : *m_config.schema) { - ObjectStore::table_for_object_type(read_group(), object_schema.first)->optimize(); + ObjectStore::table_for_object_type(read_group(), object_schema.name)->optimize(); } m_shared_group->end_read(); From ce8060b4b33944775d0ec8aa5e9e30fb140598a3 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 27 Aug 2015 11:38:30 -0700 Subject: [PATCH 20/57] Add Realm::get_schema_version() --- shared_realm.cpp | 12 +++++++++++- shared_realm.hpp | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 9d4a4a7f..c441df2d 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -133,7 +133,7 @@ SharedRealm Realm::get_shared_realm(Config config) } else if (realm->m_config.read_only) { if (old_version == ObjectStore::NotVersioned) { - throw UnitializedRealmException("Can't open an un-initizliazed Realm without a Schema"); + throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); } ObjectStore::verify_schema(realm->read_group(), *realm->m_config.schema, true); } @@ -348,6 +348,16 @@ bool Realm::refresh() return true; } +uint64_t Realm::get_schema_version(const realm::Realm::Config &config) +{ + auto existing_realm = s_global_cache.get_any_realm(config.path); + if (existing_realm) { + return existing_realm->config().schema_version; + } + + return ObjectStore::get_schema_version(Realm(config).read_group()); +} + SharedRealm RealmCache::get_realm(const std::string &path, std::thread::id thread_id) { std::lock_guard lock(m_mutex); diff --git a/shared_realm.hpp b/shared_realm.hpp index 6f868a09..064bfff7 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -76,6 +76,8 @@ namespace realm { // returns if any changes were made bool update_schema(Schema &schema, uint64_t version); + static uint64_t get_schema_version(Config const& config); + const Config &config() const { return m_config; } void begin_transaction(); From aae979ce581f5cd061b961c3ab1cabfa223c0584 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Aug 2015 16:11:45 -0700 Subject: [PATCH 21/57] Rework change notifications Switch to an abstract class rather than std::function in preparation for having more kinds of notifications with different arguments for KVO. --- shared_realm.cpp | 31 ++++++++++++++----------------- shared_realm.hpp | 39 +++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index c441df2d..ba71cada 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -223,8 +223,8 @@ void Realm::begin_transaction() LangBindHelper::promote_to_write(*m_shared_group, *m_history); m_in_transaction = true; - if (announce) { - send_local_notifications(DidChangeNotification); + if (announce && m_delegate) { + m_delegate->did_change(); } } @@ -240,8 +240,10 @@ void Realm::commit_transaction() LangBindHelper::commit_and_continue_as_read(*m_shared_group); m_in_transaction = false; - send_external_notifications(); - send_local_notifications(DidChangeNotification); + if (m_delegate) { + m_delegate->transaction_committed(); + m_delegate->did_change(); + } } void Realm::cancel_transaction() @@ -303,24 +305,17 @@ void Realm::notify() if (m_group) { LangBindHelper::advance_read(*m_shared_group, *m_history); } - send_local_notifications(DidChangeNotification); + if (m_delegate) { + m_delegate->did_change(); + } } - else { - send_local_notifications(RefreshRequiredNotification); + else if (m_delegate) { + m_delegate->changes_available(); } } } -void Realm::send_local_notifications(const std::string &type) -{ - verify_thread(); - for (NotificationFunction const& notification : m_notifications) { - (*notification)(type); - } -} - - bool Realm::refresh() { verify_thread(); @@ -344,7 +339,9 @@ bool Realm::refresh() read_group(); } - send_local_notifications(DidChangeNotification); + if (m_delegate) { + m_delegate->did_change(); + } return true; } diff --git a/shared_realm.hpp b/shared_realm.hpp index 064bfff7..0f0598a4 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -21,18 +21,18 @@ #include #include -#include #include #include #include "object_store.hpp" namespace realm { - class RealmCache; + class ClientHistory; class Realm; + class RealmCache; + class RealmDelegate; typedef std::shared_ptr SharedRealm; typedef std::weak_ptr WeakRealm; - class ClientHistory; class Realm : public std::enable_shared_from_this { @@ -90,20 +90,12 @@ namespace realm { bool auto_refresh() { return m_auto_refresh; } void notify(); - typedef std::shared_ptr> NotificationFunction; - void add_notification(NotificationFunction ¬ification) { m_notifications.insert(notification); } - void remove_notification(NotificationFunction notification) { m_notifications.erase(notification); } - void remove_all_notifications() { m_notifications.clear(); } - void invalidate(); bool compact(); std::thread::id thread_id() const { return m_thread_id; } void verify_thread(); - const std::string RefreshRequiredNotification = "RefreshRequiredNotification"; - const std::string DidChangeNotification = "DidChangeNotification"; - private: Realm(Config config); @@ -112,12 +104,6 @@ namespace realm { bool m_in_transaction = false; bool m_auto_refresh = true; - std::set m_notifications; - void send_local_notifications(const std::string ¬ification); - - typedef std::unique_ptr> ExternalNotificationFunction; - void send_external_notifications() { if (m_external_notifier) (*m_external_notifier)(); } - std::unique_ptr m_history; std::unique_ptr m_shared_group; std::unique_ptr m_read_only_group; @@ -125,7 +111,7 @@ namespace realm { Group *m_group = nullptr; public: - ExternalNotificationFunction m_external_notifier; + std::unique_ptr m_delegate; // FIXME private Group *read_group(); @@ -146,6 +132,23 @@ namespace realm { std::mutex m_mutex; }; + class RealmDelegate + { + public: + virtual ~RealmDelegate() = default; + + // The Realm has committed a write transaction, and other Realms at the + // same path should be notified + virtual void transaction_committed() = 0; + + // There are now new versions available for the Realm, but it has not + // had its read version advanced + virtual void changes_available() = 0; + + // The Realm's read version has advanced + virtual void did_change() = 0; + }; + class RealmFileException : public std::runtime_error { public: From 524edf04c689eaba3a54552011804ef43ccde7cb Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 1 Sep 2015 10:59:28 -0700 Subject: [PATCH 22/57] Add the ability to bypass the Realm cache entirely --- shared_realm.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index ba71cada..5c2733ed 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -90,31 +90,31 @@ Group *Realm::read_group() 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 MismatchedConfigException("Realm at path already opened with different read permissions."); - } - if (realm->config().in_memory != config.in_memory) { - 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"); - }*/ + if (config.cache) { + if (SharedRealm realm = s_global_cache.get_realm(config.path)) { + if (realm->config().read_only != config.read_only) { + throw MismatchedConfigException("Realm at path already opened with different read permissions."); + } + if (realm->config().in_memory != config.in_memory) { + 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; - realm->m_config.migration_function = config.migration_function; - - return realm; + return realm; + } } - realm = SharedRealm(new Realm(std::move(config))); + SharedRealm realm(new Realm(std::move(config))); // we want to ensure we are only initializing a single realm at a time static std::mutex s_init_mutex; From 9403c6f83720e7aafe2eab04e0a75904a63ec3f9 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 31 Aug 2015 11:20:20 -0700 Subject: [PATCH 23/57] Port some of the KVO support functionality to the object store --- shared_realm.cpp | 313 ++++++++++++++++++++++++++++++++++++++++++++--- shared_realm.hpp | 17 --- 2 files changed, 296 insertions(+), 34 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 5c2733ed..e2753d85 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -18,14 +18,296 @@ #include "shared_realm.hpp" +#include "realm_delegate.hpp" + #include #include #include #include +#include using namespace realm; +namespace { +class TransactLogHandler { + using ColumnInfo = RealmDelegate::ColumnInfo; + using ObserverState = RealmDelegate::ObserverState; + + size_t currentTable = 0; + std::vector observers; + std::vector invalidated; + ColumnInfo *activeLinkList = nullptr; + RealmDelegate* m_delegate; + + // Get the change info for the given column, creating it if needed + static ColumnInfo& get_change(ObserverState& state, size_t i) + { + if (state.changes.size() <= i) { + state.changes.resize(std::max(state.changes.size() * 2, i + 1)); + } + return state.changes[i]; + } + + // Loop over the columns which were changed in an observer state + template + static void for_each(ObserverState& state, Func&& f) + { + for (size_t i = 0; i < state.changes.size(); ++i) { + auto const& change = state.changes[i]; + if (change.changed) { + f(i, change); + } + } + } + + // Mark the given row/col as needing notifications sent + bool mark_dirty(size_t row_ndx, size_t col_ndx) + { + auto it = lower_bound(begin(observers), end(observers), ObserverState{currentTable, row_ndx, nullptr}); + if (it != end(observers) && it->table_ndx == currentTable && it->row_ndx == row_ndx) { + get_change(*it, col_ndx).changed = true; + } + return true; + } + + // Remove the given observer from the list of observed objects and add it + // to the listed of invalidated objects + void invalidate(ObserverState *o) + { + invalidated.push_back(o->info); + observers.erase(observers.begin() + (o - &observers[0])); + } + +public: + template + TransactLogHandler(RealmDelegate* delegate, SharedGroup& sg, Func&& func) + : m_delegate(delegate) + { + if (!delegate) { + func(); + return; + } + + observers = delegate->get_observed_rows(); + if (observers.empty()) { + auto old_version = sg.get_version_of_current_transaction(); + func(); + if (old_version != sg.get_version_of_current_transaction()) { + delegate->did_change({}, {}); + } + return; + } + + func(*this); + delegate->did_change(observers, invalidated); + } + + // Called at the end of the transaction log immediately before the version + // is advanced + void parse_complete() + { + m_delegate->will_change(observers, invalidated); + } + + // These would require having an observer before schema init + // Maybe do something here to throw an error when multiple processes have different schemas? + bool insert_group_level_table(size_t, size_t, StringData) noexcept { return false; } + bool erase_group_level_table(size_t, size_t) noexcept { return false; } + bool rename_group_level_table(size_t, StringData) noexcept { return false; } + bool insert_column(size_t, DataType, StringData, bool) { return false; } + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return false; } + bool erase_column(size_t) { return false; } + bool erase_link_column(size_t, size_t, size_t) { return false; } + bool rename_column(size_t, StringData) { return false; } + bool add_search_index(size_t) { return false; } + bool remove_search_index(size_t) { return false; } + bool add_primary_key(size_t) { return false; } + bool remove_primary_key() { return false; } + bool set_link_type(size_t, LinkType) { return false; } + + bool select_table(size_t group_level_ndx, int, const size_t*) noexcept { + currentTable = group_level_ndx; + return true; + } + + bool insert_empty_rows(size_t, size_t, size_t, bool) { + // rows are only inserted at the end, so no need to do anything + return true; + } + + bool erase_rows(size_t row_ndx, size_t, size_t last_row_ndx, bool unordered) noexcept { + for (size_t i = 0; i < observers.size(); ++i) { + auto& o = observers[i]; + if (o.table_ndx == currentTable) { + if (o.row_ndx == row_ndx) { + invalidate(&o); + --i; + } + else if (unordered && o.row_ndx == last_row_ndx) { + o.row_ndx = row_ndx; + } + else if (!unordered && o.row_ndx > row_ndx) { + o.row_ndx -= 1; + } + } + } + return true; + } + + bool clear_table() noexcept { + for (size_t i = 0; i < observers.size(); ) { + auto& o = observers[i]; + if (o.table_ndx == currentTable) { + invalidate(&o); + } + else { + ++i; + } + } + return true; + } + + bool select_link_list(size_t col, size_t row) { + activeLinkList = nullptr; + for (auto& o : observers) { + if (o.table_ndx == currentTable && o.row_ndx == row) { + activeLinkList = &get_change(o, col); + break; + } + } + return true; + } + + void append_link_list_change(ColumnInfo::Kind kind, size_t index) { + ColumnInfo *o = activeLinkList; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + // Active LinkList isn't observed or already has multiple kinds of changes + return; + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = kind; + o->changed = true; + o->indices.add(index); + } + else if (o->kind == kind) { + if (kind == ColumnInfo::Kind::Remove) { + // Shift the index to compensate for already-removed indices + for (auto i : o->indices) { + if (i <= index) + ++index; + else + break; + } + o->indices.add(index); + } + else if (kind == ColumnInfo::Kind::Insert) { + o->indices.insert_at(index); + } + else { + o->indices.add(index); + } + } + else { + // Array KVO can only send a single kind of change at a time, so + // if there's multiple just give up and send "Set" + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + } + + bool link_list_set(size_t index, size_t) { + append_link_list_change(ColumnInfo::Kind::Set, index); + return true; + } + + bool link_list_insert(size_t index, size_t) { + append_link_list_change(ColumnInfo::Kind::Insert, index); + return true; + } + + bool link_list_erase(size_t index) { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_nullify(size_t index) { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_swap(size_t index1, size_t index2) { + append_link_list_change(ColumnInfo::Kind::Set, index1); + append_link_list_change(ColumnInfo::Kind::Set, index2); + return true; + } + + bool link_list_clear(size_t old_size) { + ColumnInfo *o = activeLinkList; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + + if (o->kind == ColumnInfo::Kind::Remove) + old_size += o->indices.size(); + else if (o->kind == ColumnInfo::Kind::Insert) + old_size -= o->indices.size(); + + o->indices.set(old_size); + + o->kind = ColumnInfo::Kind::Remove; + o->changed = true; + return true; + } + + bool link_list_move(size_t from, size_t to) { + ColumnInfo *o = activeLinkList; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + if (from > to) { + std::swap(from, to); + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = ColumnInfo::Kind::Set; + o->changed = true; + } + if (o->kind == ColumnInfo::Kind::Set) { + for (size_t i = from; i <= to; ++i) + o->indices.add(i); + } + else { + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + return true; + } + + // Things that just mark the field as modified + bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } + bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); } + bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); } + bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } + bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } + bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } + bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); } + bool set_table(size_t col, size_t row) { return mark_dirty(row, col); } + bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); } + bool set_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } + bool set_null(size_t col, size_t row) { return mark_dirty(row, col); } + bool nullify_link(size_t col, size_t row) { return mark_dirty(row, col); } + + // Things we don't need to do anything for + bool optimize_table() { return false; } + + // Things that we don't do in the binding + bool select_descriptor(int, const size_t*) { return true; } + bool add_int_to_column(size_t, int_fast64_t) { return false; } +}; +} + RealmCache Realm::s_global_cache; Realm::Config::Config(const Config& c) @@ -214,18 +496,13 @@ void Realm::begin_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 - bool announce = m_shared_group->has_changed(); - // make sure we have a read transaction read_group(); - LangBindHelper::promote_to_write(*m_shared_group, *m_history); + TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { + LangBindHelper::promote_to_write(*m_shared_group, *m_history, std::move(args)...); + }); m_in_transaction = true; - - if (announce && m_delegate) { - m_delegate->did_change(); - } } void Realm::commit_transaction() @@ -242,7 +519,6 @@ void Realm::commit_transaction() if (m_delegate) { m_delegate->transaction_committed(); - m_delegate->did_change(); } } @@ -255,7 +531,9 @@ void Realm::cancel_transaction() throw InvalidTransactionException("Can't cancel a non-existing write transaction"); } - LangBindHelper::rollback_and_continue_as_read(*m_shared_group, *m_history); + TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { + LangBindHelper::rollback_and_continue_as_read(*m_shared_group, *m_history, std::move(args)...); + }); m_in_transaction = false; } @@ -303,10 +581,12 @@ void Realm::notify() if (m_shared_group->has_changed()) { // Throws if (m_auto_refresh) { if (m_group) { - LangBindHelper::advance_read(*m_shared_group, *m_history); + TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { + LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); + }); } - if (m_delegate) { - m_delegate->did_change(); + else if (m_delegate) { + m_delegate->did_change({}, {}); } } else if (m_delegate) { @@ -332,16 +612,15 @@ bool Realm::refresh() } if (m_group) { - LangBindHelper::advance_read(*m_shared_group, *m_history); + TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { + LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); + }); } else { // Create the read transaction read_group(); } - if (m_delegate) { - m_delegate->did_change(); - } return true; } diff --git a/shared_realm.hpp b/shared_realm.hpp index 0f0598a4..cdbf4d51 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -132,23 +132,6 @@ namespace realm { std::mutex m_mutex; }; - class RealmDelegate - { - public: - virtual ~RealmDelegate() = default; - - // The Realm has committed a write transaction, and other Realms at the - // same path should be notified - virtual void transaction_committed() = 0; - - // There are now new versions available for the Realm, but it has not - // had its read version advanced - virtual void changes_available() = 0; - - // The Realm's read version has advanced - virtual void did_change() = 0; - }; - class RealmFileException : public std::runtime_error { public: From 9129add439dd5fe0c6771ec867f3ab39d3ec14b2 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 1 Sep 2015 16:21:59 -0700 Subject: [PATCH 24/57] Improve performance of realm_requires_update() and make more things const --- object_schema.cpp | 9 ++++++--- object_schema.hpp | 5 ++++- object_store.cpp | 37 +++++++++++++++---------------------- object_store.hpp | 5 +---- property.hpp | 2 +- shared_realm.cpp | 2 +- shared_realm.hpp | 2 +- 7 files changed, 29 insertions(+), 33 deletions(-) diff --git a/object_schema.cpp b/object_schema.cpp index 41e34685..cfb65f5d 100644 --- a/object_schema.cpp +++ b/object_schema.cpp @@ -60,12 +60,15 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) { } } -Property *ObjectSchema::property_for_name(const std::string &name) { - for (auto& prop:properties) { - if (prop.name == name) { +Property *ObjectSchema::property_for_name(StringData name) { + for (auto& prop : properties) { + if (StringData(prop.name) == name) { return ∝ } } return nullptr; } +const Property *ObjectSchema::property_for_name(StringData name) const { + return const_cast(this)->property_for_name(name); +} diff --git a/object_schema.hpp b/object_schema.hpp index 51a4c584..5b7bf343 100644 --- a/object_schema.hpp +++ b/object_schema.hpp @@ -19,6 +19,8 @@ #ifndef REALM_OBJECT_SCHEMA_HPP #define REALM_OBJECT_SCHEMA_HPP +#include + #include #include @@ -39,7 +41,8 @@ namespace realm { std::vector properties; std::string primary_key; - Property *property_for_name(const std::string &name); + Property *property_for_name(StringData name); + const Property *property_for_name(StringData name) const; Property *primary_key_property() { return property_for_name(primary_key); } diff --git a/object_store.cpp b/object_store.cpp index 908ddbec..1f99c400 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -337,19 +337,29 @@ bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) { return old_version == version; } -bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema &schema) { +bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema const& schema) { if (!is_schema_at_version(group, version)) { return true; } - for (auto& target_schema : schema) { + + for (auto const& target_schema : schema) { TableRef table = table_for_object_type(group, target_schema.name); if (!table) { return true; } + + // Check that all of the property indexes are up to date + size_t count = table->get_column_count(); + for (size_t col = 0; col < count; ++col) { + StringData name = table->get_column_name(col); + bool is_indexed = table->has_search_index(col); + auto prop = target_schema.property_for_name(name); + if (prop && prop->requires_index() != is_indexed) { + return true; + } + } } - if (!indexes_are_up_to_date(group, schema)) { - return true; - } + return false; } @@ -396,23 +406,6 @@ Schema ObjectStore::schema_from_group(Group *group) { return schema; } -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); - if (!table) { - continue; - } - - update_column_mapping(group, object_schema); - for (auto& property : object_schema.properties) { - if (property.requires_index() != table->has_search_index(property.table_column)) { - return false; - } - } - } - return true; -} - bool ObjectStore::update_indexes(Group *group, Schema &schema) { bool changed = false; for (auto& object_schema : schema) { diff --git a/object_store.hpp b/object_store.hpp index 42b8749e..5361ec37 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -53,7 +53,7 @@ namespace realm { // 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); + static bool realm_requires_update(Group *group, uint64_t version, Schema const& schema); // updates a Realm to a given target schema/version creating tables and updating indexes as necessary // returns if any changes were made @@ -104,9 +104,6 @@ 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); diff --git a/property.hpp b/property.hpp index 893f772d..3883e805 100644 --- a/property.hpp +++ b/property.hpp @@ -54,7 +54,7 @@ namespace realm { bool is_nullable = false; size_t table_column; - bool requires_index() { return is_primary || is_indexed; } + bool requires_index() const { return is_primary || is_indexed; } }; static inline const char *string_for_property_type(PropertyType type) { diff --git a/shared_realm.cpp b/shared_realm.cpp index e2753d85..6acd7593 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -430,7 +430,7 @@ SharedRealm Realm::get_shared_realm(Config config) return realm; } -bool Realm::update_schema(Schema &schema, uint64_t version) +bool Realm::update_schema(Schema const& schema, uint64_t version) { bool changed = false; Config old_config(m_config); diff --git a/shared_realm.hpp b/shared_realm.hpp index cdbf4d51..3ef4bdf1 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -74,7 +74,7 @@ namespace realm { // 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(Schema &schema, uint64_t version); + bool update_schema(Schema const& schema, uint64_t version); static uint64_t get_schema_version(Config const& config); From 2f869541c7944badad4334b8be45d17c49c7918d Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 2 Sep 2015 09:31:06 -0700 Subject: [PATCH 25/57] Skip PK uniqueness checking when first creating a Realm file --- object_store.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 1f99c400..bb713e4c 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -387,9 +387,9 @@ bool ObjectStore::update_realm_with_schema(Group *group, // apply the migration block if provided and there's any old data if (get_schema_version(group) != ObjectStore::NotVersioned) { migration(group, schema); - } - validate_primary_column_uniqueness(group, schema); + validate_primary_column_uniqueness(group, schema); + } set_schema_version(group, version); return true; From 429a652eebb78b8a3e25c02c82dcc19a6d2f32c5 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 2 Sep 2015 11:04:01 -0700 Subject: [PATCH 26/57] Improve array KVO performance a bit --- shared_realm.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 6acd7593..8f3e416b 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -193,14 +193,7 @@ public: } else if (o->kind == kind) { if (kind == ColumnInfo::Kind::Remove) { - // Shift the index to compensate for already-removed indices - for (auto i : o->indices) { - if (i <= index) - ++index; - else - break; - } - o->indices.add(index); + o->indices.add_shifted(index); } else if (kind == ColumnInfo::Kind::Insert) { o->indices.insert_at(index); From 5ffeedb233bc51568ade621c0b50833df8dbb6f9 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 2 Sep 2015 13:51:28 -0700 Subject: [PATCH 27/57] Shuffle stuff around and clean some things up --- shared_realm.cpp | 298 +---------------------------------------------- 1 file changed, 6 insertions(+), 292 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 8f3e416b..7d270cc2 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -19,288 +19,15 @@ #include "shared_realm.hpp" #include "realm_delegate.hpp" +#include "transact_log_handler.hpp" #include #include -#include #include -#include using namespace realm; -namespace { -class TransactLogHandler { - using ColumnInfo = RealmDelegate::ColumnInfo; - using ObserverState = RealmDelegate::ObserverState; - - size_t currentTable = 0; - std::vector observers; - std::vector invalidated; - ColumnInfo *activeLinkList = nullptr; - RealmDelegate* m_delegate; - - // Get the change info for the given column, creating it if needed - static ColumnInfo& get_change(ObserverState& state, size_t i) - { - if (state.changes.size() <= i) { - state.changes.resize(std::max(state.changes.size() * 2, i + 1)); - } - return state.changes[i]; - } - - // Loop over the columns which were changed in an observer state - template - static void for_each(ObserverState& state, Func&& f) - { - for (size_t i = 0; i < state.changes.size(); ++i) { - auto const& change = state.changes[i]; - if (change.changed) { - f(i, change); - } - } - } - - // Mark the given row/col as needing notifications sent - bool mark_dirty(size_t row_ndx, size_t col_ndx) - { - auto it = lower_bound(begin(observers), end(observers), ObserverState{currentTable, row_ndx, nullptr}); - if (it != end(observers) && it->table_ndx == currentTable && it->row_ndx == row_ndx) { - get_change(*it, col_ndx).changed = true; - } - return true; - } - - // Remove the given observer from the list of observed objects and add it - // to the listed of invalidated objects - void invalidate(ObserverState *o) - { - invalidated.push_back(o->info); - observers.erase(observers.begin() + (o - &observers[0])); - } - -public: - template - TransactLogHandler(RealmDelegate* delegate, SharedGroup& sg, Func&& func) - : m_delegate(delegate) - { - if (!delegate) { - func(); - return; - } - - observers = delegate->get_observed_rows(); - if (observers.empty()) { - auto old_version = sg.get_version_of_current_transaction(); - func(); - if (old_version != sg.get_version_of_current_transaction()) { - delegate->did_change({}, {}); - } - return; - } - - func(*this); - delegate->did_change(observers, invalidated); - } - - // Called at the end of the transaction log immediately before the version - // is advanced - void parse_complete() - { - m_delegate->will_change(observers, invalidated); - } - - // These would require having an observer before schema init - // Maybe do something here to throw an error when multiple processes have different schemas? - bool insert_group_level_table(size_t, size_t, StringData) noexcept { return false; } - bool erase_group_level_table(size_t, size_t) noexcept { return false; } - bool rename_group_level_table(size_t, StringData) noexcept { return false; } - bool insert_column(size_t, DataType, StringData, bool) { return false; } - bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return false; } - bool erase_column(size_t) { return false; } - bool erase_link_column(size_t, size_t, size_t) { return false; } - bool rename_column(size_t, StringData) { return false; } - bool add_search_index(size_t) { return false; } - bool remove_search_index(size_t) { return false; } - bool add_primary_key(size_t) { return false; } - bool remove_primary_key() { return false; } - bool set_link_type(size_t, LinkType) { return false; } - - bool select_table(size_t group_level_ndx, int, const size_t*) noexcept { - currentTable = group_level_ndx; - return true; - } - - bool insert_empty_rows(size_t, size_t, size_t, bool) { - // rows are only inserted at the end, so no need to do anything - return true; - } - - bool erase_rows(size_t row_ndx, size_t, size_t last_row_ndx, bool unordered) noexcept { - for (size_t i = 0; i < observers.size(); ++i) { - auto& o = observers[i]; - if (o.table_ndx == currentTable) { - if (o.row_ndx == row_ndx) { - invalidate(&o); - --i; - } - else if (unordered && o.row_ndx == last_row_ndx) { - o.row_ndx = row_ndx; - } - else if (!unordered && o.row_ndx > row_ndx) { - o.row_ndx -= 1; - } - } - } - return true; - } - - bool clear_table() noexcept { - for (size_t i = 0; i < observers.size(); ) { - auto& o = observers[i]; - if (o.table_ndx == currentTable) { - invalidate(&o); - } - else { - ++i; - } - } - return true; - } - - bool select_link_list(size_t col, size_t row) { - activeLinkList = nullptr; - for (auto& o : observers) { - if (o.table_ndx == currentTable && o.row_ndx == row) { - activeLinkList = &get_change(o, col); - break; - } - } - return true; - } - - void append_link_list_change(ColumnInfo::Kind kind, size_t index) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - // Active LinkList isn't observed or already has multiple kinds of changes - return; - } - - if (o->kind == ColumnInfo::Kind::None) { - o->kind = kind; - o->changed = true; - o->indices.add(index); - } - else if (o->kind == kind) { - if (kind == ColumnInfo::Kind::Remove) { - o->indices.add_shifted(index); - } - else if (kind == ColumnInfo::Kind::Insert) { - o->indices.insert_at(index); - } - else { - o->indices.add(index); - } - } - else { - // Array KVO can only send a single kind of change at a time, so - // if there's multiple just give up and send "Set" - o->indices.set(0); - o->kind = ColumnInfo::Kind::SetAll; - } - } - - bool link_list_set(size_t index, size_t) { - append_link_list_change(ColumnInfo::Kind::Set, index); - return true; - } - - bool link_list_insert(size_t index, size_t) { - append_link_list_change(ColumnInfo::Kind::Insert, index); - return true; - } - - bool link_list_erase(size_t index) { - append_link_list_change(ColumnInfo::Kind::Remove, index); - return true; - } - - bool link_list_nullify(size_t index) { - append_link_list_change(ColumnInfo::Kind::Remove, index); - return true; - } - - bool link_list_swap(size_t index1, size_t index2) { - append_link_list_change(ColumnInfo::Kind::Set, index1); - append_link_list_change(ColumnInfo::Kind::Set, index2); - return true; - } - - bool link_list_clear(size_t old_size) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - return true; - } - - if (o->kind == ColumnInfo::Kind::Remove) - old_size += o->indices.size(); - else if (o->kind == ColumnInfo::Kind::Insert) - old_size -= o->indices.size(); - - o->indices.set(old_size); - - o->kind = ColumnInfo::Kind::Remove; - o->changed = true; - return true; - } - - bool link_list_move(size_t from, size_t to) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - return true; - } - if (from > to) { - std::swap(from, to); - } - - if (o->kind == ColumnInfo::Kind::None) { - o->kind = ColumnInfo::Kind::Set; - o->changed = true; - } - if (o->kind == ColumnInfo::Kind::Set) { - for (size_t i = from; i <= to; ++i) - o->indices.add(i); - } - else { - o->indices.set(0); - o->kind = ColumnInfo::Kind::SetAll; - } - return true; - } - - // Things that just mark the field as modified - bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } - bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); } - bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); } - bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } - bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } - bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } - bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); } - bool set_table(size_t col, size_t row) { return mark_dirty(row, col); } - bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); } - bool set_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } - bool set_null(size_t col, size_t row) { return mark_dirty(row, col); } - bool nullify_link(size_t col, size_t row) { return mark_dirty(row, col); } - - // Things we don't need to do anything for - bool optimize_table() { return false; } - - // Things that we don't do in the binding - bool select_descriptor(int, const size_t*) { return true; } - bool add_int_to_column(size_t, int_fast64_t) { return false; } -}; -} - RealmCache Realm::s_global_cache; Realm::Config::Config(const Config& c) @@ -492,9 +219,7 @@ void Realm::begin_transaction() // make sure we have a read transaction read_group(); - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::promote_to_write(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::begin(*m_shared_group, *m_history, m_delegate.get()); m_in_transaction = true; } @@ -507,12 +232,8 @@ void Realm::commit_transaction() throw InvalidTransactionException("Can't commit a non-existing write transaction"); } - LangBindHelper::commit_and_continue_as_read(*m_shared_group); m_in_transaction = false; - - if (m_delegate) { - m_delegate->transaction_committed(); - } + transaction::commit(*m_shared_group, *m_history, m_delegate.get()); } void Realm::cancel_transaction() @@ -524,13 +245,10 @@ void Realm::cancel_transaction() throw InvalidTransactionException("Can't cancel a non-existing write transaction"); } - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::rollback_and_continue_as_read(*m_shared_group, *m_history, std::move(args)...); - }); m_in_transaction = false; + transaction::cancel(*m_shared_group, *m_history, m_delegate.get()); } - void Realm::invalidate() { verify_thread(); @@ -574,9 +292,7 @@ void Realm::notify() if (m_shared_group->has_changed()) { // Throws if (m_auto_refresh) { if (m_group) { - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::advance(*m_shared_group, *m_history, m_delegate.get()); } else if (m_delegate) { m_delegate->did_change({}, {}); @@ -605,9 +321,7 @@ bool Realm::refresh() } if (m_group) { - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::advance(*m_shared_group, *m_history, m_delegate.get()); } else { // Create the read transaction From 4dd72d47a0ef0f26c2e2ba44477f1c62c59c02b9 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 2 Sep 2015 17:53:21 -0700 Subject: [PATCH 28/57] Move the interprocess notification functionality to the object store --- shared_realm.cpp | 7 +++++-- shared_realm.hpp | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 7d270cc2..b201574c 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -18,6 +18,7 @@ #include "shared_realm.hpp" +#include "external_commit_helper.hpp" #include "realm_delegate.hpp" #include "transact_log_handler.hpp" @@ -52,7 +53,9 @@ Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) return *this; } -Realm::Realm(Config config) : m_config(std::move(config)) +Realm::Realm(Config config) +: m_config(std::move(config)) +, m_notifier(new ExternalCommitHelper(this)) { try { if (m_config.read_only) { @@ -234,6 +237,7 @@ void Realm::commit_transaction() m_in_transaction = false; transaction::commit(*m_shared_group, *m_history, m_delegate.get()); + m_notifier->notify_others(); } void Realm::cancel_transaction() @@ -416,4 +420,3 @@ void RealmCache::clear() m_cache.clear(); } - diff --git a/shared_realm.hpp b/shared_realm.hpp index 3ef4bdf1..eb9ee424 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -28,6 +28,7 @@ namespace realm { class ClientHistory; + class ExternalCommitHelper; class Realm; class RealmCache; class RealmDelegate; @@ -110,6 +111,8 @@ namespace realm { Group *m_group = nullptr; + std::unique_ptr m_notifier; + public: std::unique_ptr m_delegate; From ce39ef965f77ca2f8f495bc4bf88956f600290df Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 3 Sep 2015 10:37:24 -0700 Subject: [PATCH 29/57] Refactor schema initialization a bit Change schema verification to operate on a pair of Schema objects rather than a Schema and a Group to eliminate some redundant work done, defer some of the work done for migrations to within the migration block to avoid doing it unnecessarily, and make passing in a custom schema in the Config when creating a Realm entirely equivalent to calling update_schema() afterwards. --- object_store.cpp | 99 +++++++++++++++++++++++++++--------------------- object_store.hpp | 54 +++++++++++++------------- shared_realm.cpp | 94 ++++++++++++++++++++++++--------------------- shared_realm.hpp | 2 +- 4 files changed, 134 insertions(+), 115 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index bb713e4c..9f2b3e3b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -136,20 +136,24 @@ 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); } -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 inline bool property_has_changed(Property const& p1, Property const& p2) { + return p1.type != p2.type + || p1.name != p2.name + || p1.object_type != p2.object_type + || p1.is_nullable != p2.is_nullable; } static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) { return lft.name < rgt.name; } -void ObjectStore::verify_schema(Group *group, Schema &target_schema, bool allow_missing_tables) { +void ObjectStore::verify_schema(Schema const& actual_schema, Schema &target_schema, bool allow_missing_tables) { std::sort(begin(target_schema), end(target_schema), compare_by_name); std::vector errors; for (auto &object_schema : target_schema) { - if (!table_for_object_type(group, object_schema.name)) { + auto matching_schema = std::lower_bound(begin(actual_schema), end(actual_schema), object_schema, compare_by_name); + if (matching_schema == end(actual_schema) || matching_schema->name != object_schema.name) { if (!allow_missing_tables) { errors.emplace_back(ObjectSchemaValidationException(object_schema.name, "Missing table for object type '" + object_schema.name + "'.")); @@ -157,7 +161,7 @@ void ObjectStore::verify_schema(Group *group, Schema &target_schema, bool allow_ continue; } - auto more_errors = verify_object_schema(group, object_schema, target_schema); + auto more_errors = verify_object_schema(*matching_schema, object_schema, target_schema); errors.insert(errors.end(), more_errors.begin(), more_errors.end()); } if (errors.size()) { @@ -165,9 +169,8 @@ void ObjectStore::verify_schema(Group *group, Schema &target_schema, bool allow_ } } -std::vector ObjectStore::verify_object_schema(Group *group, ObjectSchema &target_schema, Schema &schema) { +std::vector ObjectStore::verify_object_schema(ObjectSchema const& table_schema, ObjectSchema &target_schema, Schema &schema) { std::vector exceptions; - ObjectSchema table_schema(group, target_schema.name); ObjectSchema cmp; auto schema_contains_table = [&](std::string const& name) { @@ -176,7 +179,7 @@ std::vector ObjectStore::verify_object_schema(G }; // check to see if properties are the same - Property *primary = nullptr; + const Property *primary = nullptr; for (auto& current_prop : table_schema.properties) { auto target_prop = target_schema.property_for_name(current_prop.name); @@ -276,12 +279,27 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update ObjectSchema current_schema(group, target_object_schema->name); std::vector &target_props = target_object_schema->properties; + // remove extra columns + size_t deleted = 0; + for (auto& current_prop : current_schema.properties) { + auto target_prop = target_object_schema->property_for_name(current_prop.name); + if (!target_prop || property_has_changed(current_prop, *target_prop)) { + table->remove_column(current_prop.table_column - deleted); + ++deleted; + current_prop.table_column = npos; + changed = true; + } + else { + current_prop.table_column -= deleted; + } + } + // add missing columns for (auto& target_prop : target_props) { auto current_prop = current_schema.property_for_name(target_prop.name); // add any new properties (new name or different type) - if (!current_prop || property_has_changed(*current_prop, target_prop)) { + if (!current_prop || current_prop->table_column == npos) { switch (target_prop.type) { // for objects and arrays, we have to specify target table case PropertyTypeObject: @@ -298,17 +316,8 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update changed = true; } - } - - // remove extra columns - sort(begin(current_schema.properties), end(current_schema.properties), [](Property &i, Property &j) { - return j.table_column < i.table_column; - }); - for (auto& current_prop : current_schema.properties) { - auto target_prop = target_object_schema->property_for_name(current_prop.name); - if (!target_prop || property_has_changed(current_prop, *target_prop)) { - table->remove_column(current_prop.table_column); - changed = true; + else { + target_prop.table_column = current_prop->table_column; } } @@ -337,24 +346,22 @@ bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) { return old_version == version; } -bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema const& schema) { - if (!is_schema_at_version(group, version)) { - return true; - } - +bool ObjectStore::needs_update(Schema const& old_schema, Schema& schema) { for (auto const& target_schema : schema) { - TableRef table = table_for_object_type(group, target_schema.name); - if (!table) { + auto matching_schema = std::lower_bound(begin(old_schema), end(old_schema), target_schema, compare_by_name); + if (matching_schema == end(old_schema) || matching_schema->name != target_schema.name) { + // Table doesn't exist return true; } + if (matching_schema->properties.size() != target_schema.properties.size()) { + // If the number of properties don't match then a migration is required + return false; + } + // Check that all of the property indexes are up to date - size_t count = table->get_column_count(); - for (size_t col = 0; col < count; ++col) { - StringData name = table->get_column_name(col); - bool is_indexed = table->has_search_index(col); - auto prop = target_schema.property_for_name(name); - if (prop && prop->requires_index() != is_indexed) { + for (size_t i = 0, count = target_schema.properties.size(); i < count; ++i) { + if (target_schema.properties[i].is_indexed != matching_schema->properties[i].is_indexed) { return true; } } @@ -363,9 +370,8 @@ bool ObjectStore::realm_requires_update(Group *group, uint64_t version, Schema c return false; } -bool ObjectStore::update_realm_with_schema(Group *group, - uint64_t version, - Schema &schema, +bool ObjectStore::update_realm_with_schema(Group *group, Schema const& old_schema, + uint64_t version, Schema &schema, MigrationFunction migration) { // Recheck the schema version after beginning the write transaction as // another process may have done the migration after we opened the read @@ -376,7 +382,11 @@ bool ObjectStore::update_realm_with_schema(Group *group, bool changed = create_metadata_tables(group); changed = create_tables(group, schema, migrating) || changed; - verify_schema(group, schema); + if (!migrating) { + // If we aren't migrating, then verify that all of the tables which + // were already present are valid (newly created ones always are) + verify_schema(old_schema, schema, true); + } changed = update_indexes(group, schema) || changed; @@ -403,6 +413,7 @@ Schema ObjectStore::schema_from_group(Group *group) { schema.emplace_back(group, object_type); } } + std::sort(begin(schema), end(schema), compare_by_name); return schema; } @@ -466,7 +477,7 @@ InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_versio 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) : +DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, Property const& property) : m_object_type(object_type), m_property(property) { m_what = "Primary key property '" + property.name + "' has duplicate values after migration."; @@ -482,25 +493,25 @@ SchemaValidationException::SchemaValidationException(std::vector MigrationFunction; - static bool update_realm_with_schema(Group *group, uint64_t version, Schema &schema, MigrationFunction migration); + static bool update_realm_with_schema(Group *group, Schema const& old_schema, uint64_t version, + Schema &schema, MigrationFunction migration); // get a table for an object type static realm::TableRef table_for_object_type(Group *group, StringData object_type); @@ -88,10 +88,10 @@ namespace realm { // if update existing is true, updates existing tables, otherwise only adds and initializes new tables static bool create_tables(realm::Group *group, Schema &target_schema, bool update_existing); - // verify a target schema against its table, setting the table_column property on each schema object + // verify a target schema against an expected schema, setting the table_column property on each schema object // updates the column mapping on the target_schema // returns array of validation errors - static std::vector verify_object_schema(Group *group, ObjectSchema &target_schema, Schema &schema); + static std::vector verify_object_schema(ObjectSchema const& expected, ObjectSchema &target_schema, Schema &schema); // get primary key property name for object type static StringData get_primary_key_for_object(Group *group, StringData object_type); @@ -118,7 +118,7 @@ namespace realm { public: ObjectStoreException() = default; ObjectStoreException(const std::string &what) : m_what(what) {} - virtual const char* what() const noexcept { return m_what.c_str(); } + const char* what() const noexcept override { return m_what.c_str(); } protected: std::string m_what; }; @@ -137,9 +137,9 @@ namespace realm { class DuplicatePrimaryKeyValueException : public MigrationException { public: - DuplicatePrimaryKeyValueException(std::string object_type, Property &property); + DuplicatePrimaryKeyValueException(std::string object_type, Property const& property); std::string object_type() { return m_object_type; } - Property &property() { return m_property; } + Property const& property() { return m_property; } private: std::string m_object_type; Property m_property; @@ -166,36 +166,36 @@ namespace realm { class ObjectSchemaPropertyException : public ObjectSchemaValidationException { public: - ObjectSchemaPropertyException(std::string object_type, Property &property) : + ObjectSchemaPropertyException(std::string object_type, Property const& property) : ObjectSchemaValidationException(object_type), m_property(property) {} - Property &property() { return m_property; } + Property const& property() { return m_property; } private: Property m_property; }; class PropertyTypeNotIndexableException : public ObjectSchemaPropertyException { public: - PropertyTypeNotIndexableException(std::string object_type, Property &property); + PropertyTypeNotIndexableException(std::string object_type, Property const& property); }; class ExtraPropertyException : public ObjectSchemaPropertyException { public: - ExtraPropertyException(std::string object_type, Property &property); + ExtraPropertyException(std::string object_type, Property const& property); }; class MissingPropertyException : public ObjectSchemaPropertyException { public: - MissingPropertyException(std::string object_type, Property &property); + MissingPropertyException(std::string object_type, Property const& property); }; class InvalidNullabilityException : public ObjectSchemaPropertyException { public: - InvalidNullabilityException(std::string object_type, Property &property); + InvalidNullabilityException(std::string object_type, Property const& property); }; class MissingObjectTypeException : public ObjectSchemaPropertyException { public: - MissingObjectTypeException(std::string object_type, Property &property); + MissingObjectTypeException(std::string object_type, Property const& property); }; class DuplicatePrimaryKeysException : public ObjectSchemaValidationException { @@ -205,9 +205,9 @@ namespace realm { class MismatchedPropertiesException : public ObjectSchemaValidationException { public: - MismatchedPropertiesException(std::string object_type, Property &old_property, Property &new_property); - Property &old_property() { return m_old_property; } - Property &new_property() { return m_new_property; } + MismatchedPropertiesException(std::string object_type, Property const& old_property, Property const& new_property); + Property const& old_property() { return m_old_property; } + Property const& new_property() { return m_new_property; } private: Property m_old_property, m_new_property; }; diff --git a/shared_realm.cpp b/shared_realm.cpp index b201574c..2143db38 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -121,30 +121,36 @@ SharedRealm Realm::get_shared_realm(Config config) SharedRealm realm(new Realm(std::move(config))); + auto target_schema = std::move(realm->m_config.schema); + auto target_schema_version = realm->m_config.schema_version; + realm->m_config.schema_version = ObjectStore::get_schema_version(realm->read_group()); + // we want to ensure we are only initializing a single realm at a time static std::mutex s_init_mutex; std::lock_guard lock(s_init_mutex); - - uint64_t old_version = ObjectStore::get_schema_version(realm->read_group()); 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(*existing->m_config.schema); } - else if (!realm->m_config.schema) { - // get schema from group and skip validation - realm->m_config.schema_version = old_version; - realm->m_config.schema = std::make_unique(ObjectStore::schema_from_group(realm->read_group())); - } - else if (realm->m_config.read_only) { - if (old_version == ObjectStore::NotVersioned) { - throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); - } - ObjectStore::verify_schema(realm->read_group(), *realm->m_config.schema, true); - } else { - // its a non-cached realm so update/migrate if needed - realm->update_schema(*realm->m_config.schema, realm->m_config.schema_version); + // otherwise get the schema from the group + realm->m_config.schema = std::make_unique(ObjectStore::schema_from_group(realm->read_group())); + + // if a target schema is supplied, verify that it matches or migrate to + // it, as neeeded + if (target_schema) { + if (realm->m_config.read_only) { + if (realm->m_config.schema_version == ObjectStore::NotVersioned) { + throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); + } + ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true); + realm->m_config.schema = std::move(target_schema); + } + else { + realm->update_schema(std::move(target_schema), target_schema_version); + } + } } if (config.cache) { @@ -153,47 +159,49 @@ SharedRealm Realm::get_shared_realm(Config config) return realm; } -bool Realm::update_schema(Schema const& schema, uint64_t version) +bool Realm::update_schema(std::unique_ptr 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); + bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema)); + if (!needs_update) { + ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only); + m_config.schema = std::move(schema); + m_config.schema_version = version; + return false; } + + // Store the old config/schema for the migration function, and update + // our schema to the new one + auto old_schema = std::move(m_config.schema); + Config old_config(m_config); + old_config.read_only = true; + old_config.schema = std::move(old_schema); + + m_config.schema = std::move(schema); m_config.schema_version = version; - try { - if (!m_config.read_only && ObjectStore::realm_requires_update(read_group(), version, schema)) { - // keep old copy to pass to migration function - old_config.read_only = true; - old_config.schema_version = ObjectStore::get_schema_version(read_group()); - old_config.schema = std::make_unique(ObjectStore::schema_from_group(read_group())); - SharedRealm old_realm(new Realm(old_config)); - auto updated_realm = shared_from_this(); + 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); + }; - // 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 { - ObjectStore::verify_schema(read_group(), *m_config.schema, m_config.read_only); - } + try { + // update and migrate + begin_transaction(); + bool changed = ObjectStore::update_realm_with_schema(read_group(), *old_config.schema, + version, *m_config.schema, + migration_function); + commit_transaction(); + return changed; } catch (...) { if (is_in_transaction()) { cancel_transaction(); } m_config.schema_version = old_config.schema_version; - m_config.schema = std::move(old_config.schema); + m_config.schema = std::move(m_config.schema); throw; } - return changed; } static void check_read_write(Realm *realm) diff --git a/shared_realm.hpp b/shared_realm.hpp index eb9ee424..a3070c47 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -75,7 +75,7 @@ namespace realm { // 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(Schema const& schema, uint64_t version); + bool update_schema(std::unique_ptr schema, uint64_t version); static uint64_t get_schema_version(Config const& config); From 663492e9dac8e1ca6170dd4b8098aa48d71702da Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 3 Sep 2015 14:48:24 -0700 Subject: [PATCH 30/57] Honor is_nullable when creating columns --- object_store.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/object_store.cpp b/object_store.cpp index 9f2b3e3b..af05f47c 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -310,7 +310,9 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update break; } default: - target_prop.table_column = table->add_column(DataType(target_prop.type), target_prop.name); + target_prop.table_column = table->add_column(DataType(target_prop.type), + target_prop.name, + target_prop.is_nullable); break; } From 873d24f3f054448513ebfb64bce5c69dc3adbba1 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 3 Sep 2015 14:54:41 -0700 Subject: [PATCH 31/57] Allow more nullable property types when supported --- object_store.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/object_store.cpp b/object_store.cpp index af05f47c..0bc585d9 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -198,6 +198,11 @@ std::vector ObjectStore::verify_object_schema(O } // check nullablity +#if REALM_NULL_STRINGS == 1 + if (current_prop.type == PropertyTypeArray && current_prop.is_nullable) { + exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); + } +#else if (current_prop.type == PropertyTypeObject) { if (!current_prop.is_nullable) { exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); @@ -208,6 +213,7 @@ std::vector ObjectStore::verify_object_schema(O exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); } } +#endif // check primary keys if (current_prop.is_primary) { @@ -516,6 +522,9 @@ MissingPropertyException::MissingPropertyException(std::string object_type, Prop InvalidNullabilityException::InvalidNullabilityException(std::string object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { +#if REALM_NULL_STRINGS == 1 + m_what = "'Array' property '" + property.name + "' cannot be nullable"; +#else if (property.type == PropertyTypeObject) { if (!property.is_nullable) { m_what = "'Object' property '" + property.name + "' must be nullable."; @@ -526,6 +535,7 @@ InvalidNullabilityException::InvalidNullabilityException(std::string object_type m_what = "Only 'Object' property types are nullable"; } } +#endif } MissingObjectTypeException::MissingObjectTypeException(std::string object_type, Property const& property) : From 515ce6296f4acc3f5c9b7edc6dca033d6593b172 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 3 Sep 2015 17:59:20 -0700 Subject: [PATCH 32/57] Share ExternalCommitHelpers between Realm instances for a single path --- shared_realm.cpp | 12 +++++++++++- shared_realm.hpp | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 2143db38..8b1abd84 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -55,7 +55,6 @@ Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) Realm::Realm(Config config) : m_config(std::move(config)) -, m_notifier(new ExternalCommitHelper(this)) { try { if (m_config.read_only) { @@ -85,6 +84,12 @@ Realm::Realm(Config config) } } +Realm::~Realm() { + if (m_notifier) { // might not exist yet if an error occurred during init + m_notifier->remove_realm(this); + } +} + Group *Realm::read_group() { if (!m_group) { @@ -132,8 +137,13 @@ SharedRealm Realm::get_shared_realm(Config config) // 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(*existing->m_config.schema); + + realm->m_notifier = existing->m_notifier; + realm->m_notifier->add_realm(realm.get()); } else { + realm->m_notifier = std::make_shared(realm.get()); + // otherwise get the schema from the group realm->m_config.schema = std::make_unique(ObjectStore::schema_from_group(realm->read_group())); diff --git a/shared_realm.hpp b/shared_realm.hpp index a3070c47..3836c5e9 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -97,6 +97,8 @@ namespace realm { std::thread::id thread_id() const { return m_thread_id; } void verify_thread(); + ~Realm(); + private: Realm(Config config); @@ -111,7 +113,7 @@ namespace realm { Group *m_group = nullptr; - std::unique_ptr m_notifier; + std::shared_ptr m_notifier; public: std::unique_ptr m_delegate; From fb186248a169690dfc2404c945d5d0540c4d4b3d Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 09:40:40 -0700 Subject: [PATCH 33/57] Remove an unused function --- object_store.cpp | 11 ----------- object_store.hpp | 3 --- 2 files changed, 14 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 0bc585d9..1d37e80b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -249,17 +249,6 @@ std::vector ObjectStore::verify_object_schema(O return exceptions; } -void ObjectStore::update_column_mapping(Group *group, ObjectSchema &target_schema) { - ObjectSchema table_schema(group, target_schema.name); - for (auto& target_prop : target_schema.properties) { - auto table_prop = table_schema.property_for_name(target_prop.name); - if (table_prop) { - // Update target property column to match what's in the realm if it exists - target_prop.table_column = table_prop->table_column; - } - } -} - // 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 diff --git a/object_store.hpp b/object_store.hpp index 5778ff64..34169ccd 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -47,9 +47,6 @@ namespace realm { // throws if the schema is invalid or does not match static void verify_schema(Schema const& actual_schema, Schema &target_schema, bool allow_missing_tables = false); - // 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 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& schema); From cc6364fff31e48ecd121b43c8588567a5d6a6d7e Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 09:40:29 -0700 Subject: [PATCH 34/57] Make a bunch of things const --- object_schema.cpp | 7 +++---- object_schema.hpp | 5 ++++- object_store.cpp | 31 ++++++++++++++++++------------- object_store.hpp | 44 +++++++++++++++++++++++--------------------- shared_realm.cpp | 2 +- shared_realm.hpp | 6 +++--- 6 files changed, 52 insertions(+), 43 deletions(-) diff --git a/object_schema.cpp b/object_schema.cpp index cfb65f5d..33cd2497 100644 --- a/object_schema.cpp +++ b/object_schema.cpp @@ -28,9 +28,8 @@ using namespace realm; ObjectSchema::~ObjectSchema() = default; -ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) { - TableRef tableRef = ObjectStore::table_for_object_type(group, name); - Table *table = tableRef.get(); +ObjectSchema::ObjectSchema(const Group *group, const std::string &name) : name(name) { + ConstTableRef table = ObjectStore::table_for_object_type(group, name); size_t count = table->get_column_count(); properties.reserve(count); @@ -44,7 +43,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) { property.table_column = col; if (property.type == PropertyTypeObject || property.type == PropertyTypeArray) { // set link type for objects and arrays - realm::TableRef linkTable = table->get_link_target(col); + ConstTableRef linkTable = table->get_link_target(col); property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); } properties.push_back(std::move(property)); diff --git a/object_schema.hpp b/object_schema.hpp index 5b7bf343..aca4af31 100644 --- a/object_schema.hpp +++ b/object_schema.hpp @@ -35,7 +35,7 @@ namespace realm { // create object schema from existing table // if no table is provided it is looked up in the group - ObjectSchema(Group *group, const std::string &name); + ObjectSchema(const Group *group, const std::string &name); std::string name; std::vector properties; @@ -46,6 +46,9 @@ namespace realm { Property *primary_key_property() { return property_for_name(primary_key); } + const Property *primary_key_property() const { + return property_for_name(primary_key); + } }; } diff --git a/object_store.cpp b/object_store.cpp index 1d37e80b..1f7a21d1 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -45,7 +45,7 @@ const size_t c_object_table_prefix_length = c_object_table_prefix.length(); const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); -bool ObjectStore::has_metadata_tables(Group *group) { +bool ObjectStore::has_metadata_tables(const Group *group) { return group->get_table(c_primaryKeyTableName) && group->get_table(c_metadataTableName); } @@ -71,8 +71,8 @@ bool ObjectStore::create_metadata_tables(Group *group) { return changed; } -uint64_t ObjectStore::get_schema_version(Group *group) { - TableRef table = group->get_table(c_metadataTableName); +uint64_t ObjectStore::get_schema_version(const Group *group) { + ConstTableRef table = group->get_table(c_metadataTableName); if (!table || table->get_column_count() == 0) { return ObjectStore::NotVersioned; } @@ -84,8 +84,8 @@ void ObjectStore::set_schema_version(Group *group, uint64_t version) { table->set_int(c_versionColumnIndex, c_zeroRowIndex, version); } -StringData ObjectStore::get_primary_key_for_object(Group *group, StringData object_type) { - TableRef table = group->get_table(c_primaryKeyTableName); +StringData ObjectStore::get_primary_key_for_object(const Group *group, StringData object_type) { + ConstTableRef table = group->get_table(c_primaryKeyTableName); if (!table) { return ""; } @@ -132,6 +132,10 @@ TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type return group->get_table(table_name_for_object_type(object_type)); } +ConstTableRef ObjectStore::table_for_object_type(const Group *group, StringData object_type) { + return group->get_table(table_name_for_object_type(object_type)); +} + TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, const StringData &object_type, bool &created) { return group->get_or_add_table(table_name_for_object_type(object_type), &created); } @@ -147,7 +151,7 @@ static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) { return lft.name < rgt.name; } -void ObjectStore::verify_schema(Schema const& actual_schema, Schema &target_schema, bool allow_missing_tables) { +void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) { std::sort(begin(target_schema), end(target_schema), compare_by_name); std::vector errors; @@ -169,7 +173,9 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema &target_sche } } -std::vector ObjectStore::verify_object_schema(ObjectSchema const& table_schema, ObjectSchema &target_schema, Schema &schema) { +std::vector ObjectStore::verify_object_schema(ObjectSchema const& table_schema, + ObjectSchema& target_schema, + Schema const& schema) { std::vector exceptions; ObjectSchema cmp; @@ -335,7 +341,7 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update return changed; } -bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) { +bool ObjectStore::is_schema_at_version(const Group *group, uint64_t version) { uint64_t old_version = get_schema_version(group); if (old_version > version && old_version != NotVersioned) { throw InvalidSchemaVersionException(old_version, version); @@ -343,7 +349,7 @@ bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) { return old_version == version; } -bool ObjectStore::needs_update(Schema const& old_schema, Schema& schema) { +bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) { for (auto const& target_schema : schema) { auto matching_schema = std::lower_bound(begin(old_schema), end(old_schema), target_schema, compare_by_name); if (matching_schema == end(old_schema) || matching_schema->name != target_schema.name) { @@ -402,7 +408,7 @@ bool ObjectStore::update_realm_with_schema(Group *group, Schema const& old_schem return true; } -Schema ObjectStore::schema_from_group(Group *group) { +Schema ObjectStore::schema_from_group(const 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)); @@ -444,14 +450,14 @@ bool ObjectStore::update_indexes(Group *group, Schema &schema) { return changed; } -void ObjectStore::validate_primary_column_uniqueness(Group *group, Schema &schema) { +void ObjectStore::validate_primary_column_uniqueness(const Group *group, Schema const& schema) { for (auto& object_schema : schema) { auto primary_prop = object_schema.primary_key_property(); if (!primary_prop) { continue; } - TableRef table = table_for_object_type(group, object_schema.name); + ConstTableRef table = table_for_object_type(group, object_schema.name); if (table->get_distinct_view(primary_prop->table_column).size() != table->size()) { throw DuplicatePrimaryKeyValueException(object_schema.name, *primary_prop); } @@ -568,4 +574,3 @@ DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string object_ { m_what = "Duplicate primary keys for object '" + object_type + "'."; } - diff --git a/object_store.hpp b/object_store.hpp index 34169ccd..89a7108e 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -37,19 +37,19 @@ namespace realm { static const uint64_t NotVersioned; // get the last set schema version - static uint64_t get_schema_version(Group *group); + static uint64_t get_schema_version(const Group *group); // checks if the schema in the group is at the given version - static bool is_schema_at_version(realm::Group *group, uint64_t version); + static bool is_schema_at_version(const Group *group, uint64_t version); // verify that schema from a group and a target schema are compatible // updates the column mapping on all ObjectSchema properties of the target schema // throws if the schema is invalid or does not match - static void verify_schema(Schema const& actual_schema, Schema &target_schema, bool allow_missing_tables = false); + static void verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables = false); // 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& 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 // returns if any changes were made @@ -62,9 +62,10 @@ namespace realm { // get a table for an object type static realm::TableRef table_for_object_type(Group *group, StringData object_type); + static realm::ConstTableRef table_for_object_type(const Group *group, StringData object_type); // get existing Schema from a group - static Schema schema_from_group(Group *group); + static Schema schema_from_group(const Group *group); // deletes the table for the given type static void delete_data_for_object(Group *group, const StringData &object_type); @@ -74,7 +75,7 @@ namespace realm { static void set_schema_version(Group *group, uint64_t version); // check if the realm already has all metadata tables - static bool has_metadata_tables(Group *group); + static bool has_metadata_tables(const Group *group); // create any metadata tables that don't already exist // must be in write transaction to set @@ -88,10 +89,12 @@ namespace realm { // verify a target schema against an expected schema, setting the table_column property on each schema object // updates the column mapping on the target_schema // returns array of validation errors - static std::vector verify_object_schema(ObjectSchema const& expected, ObjectSchema &target_schema, Schema &schema); + static std::vector verify_object_schema(ObjectSchema const& expected, + ObjectSchema &target_schema, + Schema const& schema); // get primary key property name for object type - static StringData get_primary_key_for_object(Group *group, StringData object_type); + static StringData get_primary_key_for_object(const Group *group, StringData object_type); // sets primary key property for object type // must be in write transaction to set @@ -105,7 +108,7 @@ namespace realm { static bool update_indexes(Group *group, Schema &schema); // validates that all primary key properties have unique values - static void validate_primary_column_uniqueness(Group *group, Schema &schema); + static void validate_primary_column_uniqueness(const Group *group, Schema const& schema); friend ObjectSchema; }; @@ -126,8 +129,8 @@ namespace realm { class InvalidSchemaVersionException : public MigrationException { public: InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version); - uint64_t old_version() { return m_old_version; } - uint64_t new_version() { return m_new_version; } + uint64_t old_version() const { return m_old_version; } + uint64_t new_version() const { return m_new_version; } private: uint64_t m_old_version, m_new_version; }; @@ -135,8 +138,8 @@ namespace realm { class DuplicatePrimaryKeyValueException : public MigrationException { public: DuplicatePrimaryKeyValueException(std::string object_type, Property const& property); - std::string object_type() { return m_object_type; } - Property const& property() { return m_property; } + std::string object_type() const { return m_object_type; } + Property const& property() const { return m_property; } private: std::string m_object_type; Property m_property; @@ -156,7 +159,7 @@ namespace realm { ObjectSchemaValidationException(std::string object_type) : m_object_type(object_type) {} ObjectSchemaValidationException(std::string object_type, std::string message) : m_object_type(object_type) { m_what = message; } - std::string object_type() { return m_object_type; } + std::string object_type() const { return m_object_type; } protected: std::string m_object_type; }; @@ -165,7 +168,7 @@ namespace realm { public: ObjectSchemaPropertyException(std::string object_type, Property const& property) : ObjectSchemaValidationException(object_type), m_property(property) {} - Property const& property() { return m_property; } + Property const& property() const { return m_property; } private: Property m_property; }; @@ -203,8 +206,8 @@ namespace realm { class MismatchedPropertiesException : public ObjectSchemaValidationException { public: MismatchedPropertiesException(std::string object_type, Property const& old_property, Property const& new_property); - Property const& old_property() { return m_old_property; } - Property const& new_property() { return m_new_property; } + Property const& old_property() const { return m_old_property; } + Property const& new_property() const { return m_new_property; } private: Property m_old_property, m_new_property; }; @@ -212,8 +215,8 @@ namespace realm { class ChangedPrimaryKeyException : public ObjectSchemaValidationException { public: ChangedPrimaryKeyException(std::string object_type, std::string old_primary, std::string new_primary); - std::string old_primary() { return m_old_primary; } - std::string new_primary() { return m_new_primary; } + std::string old_primary() const { return m_old_primary; } + std::string new_primary() const { return m_new_primary; } private: std::string m_old_primary, m_new_primary; }; @@ -221,11 +224,10 @@ namespace realm { class InvalidPrimaryKeyException : public ObjectSchemaValidationException { public: InvalidPrimaryKeyException(std::string object_type, std::string primary_key); - std::string primary_key() { return m_primary_key; } + std::string primary_key() const { return m_primary_key; } private: std::string m_primary_key; }; } #endif /* defined(REALM_OBJECT_STORE_HPP) */ - diff --git a/shared_realm.cpp b/shared_realm.cpp index 8b1abd84..68972e4d 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -221,7 +221,7 @@ static void check_read_write(Realm *realm) } } -void Realm::verify_thread() +void Realm::verify_thread() const { if (m_thread_id != std::this_thread::get_id()) { throw IncorrectThreadException("Realm accessed from incorrect thread."); diff --git a/shared_realm.hpp b/shared_realm.hpp index 3836c5e9..e1c1a0c6 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -84,18 +84,18 @@ namespace realm { void begin_transaction(); void commit_transaction(); void cancel_transaction(); - bool is_in_transaction() { return m_in_transaction; } + bool is_in_transaction() const { return m_in_transaction; } bool refresh(); void set_auto_refresh(bool auto_refresh) { m_auto_refresh = auto_refresh; } - bool auto_refresh() { return m_auto_refresh; } + bool auto_refresh() const { return m_auto_refresh; } void notify(); void invalidate(); bool compact(); std::thread::id thread_id() const { return m_thread_id; } - void verify_thread(); + void verify_thread() const; ~Realm(); From d3a218dac34beae89f76cf7c7905858c11c78cc8 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 09:53:51 -0700 Subject: [PATCH 35/57] USe more const refs to avoid copies --- object_store.cpp | 26 +++++++++++++------------- object_store.hpp | 40 ++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 1f7a21d1..61711078 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -19,8 +19,8 @@ #include "object_store.hpp" #include -#include #include +#include #include #include @@ -480,41 +480,41 @@ InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_versio 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 const& property) : +DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property) : m_object_type(object_type), m_property(property) { m_what = "Primary key property '" + property.name + "' has duplicate values after migration."; }; -SchemaValidationException::SchemaValidationException(std::vector errors) : +SchemaValidationException::SchemaValidationException(std::vector const& errors) : m_validation_errors(errors) { m_what ="Migration is required due to the following errors: "; - for (auto error : errors) { + for (auto const& error : errors) { m_what += std::string("\n- ") + error.what(); } } -PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string object_type, Property const& property) : +PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, 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 const& property) : +ExtraPropertyException::ExtraPropertyException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { m_what = "Property '" + property.name + "' has been added to latest object model."; } -MissingPropertyException::MissingPropertyException(std::string object_type, Property const& property) : +MissingPropertyException::MissingPropertyException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { m_what = "Property '" + property.name + "' is missing from latest object model."; } -InvalidNullabilityException::InvalidNullabilityException(std::string object_type, Property const& property) : +InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { #if REALM_NULL_STRINGS == 1 @@ -533,13 +533,13 @@ InvalidNullabilityException::InvalidNullabilityException(std::string object_type #endif } -MissingObjectTypeException::MissingObjectTypeException(std::string object_type, Property const& property) : +MissingObjectTypeException::MissingObjectTypeException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { m_what = "Target type '" + property.object_type + "' doesn't exist for property '" + property.name + "'."; } -MismatchedPropertiesException::MismatchedPropertiesException(std::string object_type, Property const& old_property, Property const& new_property) : +MismatchedPropertiesException::MismatchedPropertiesException(std::string const& object_type, Property const& old_property, Property const& new_property) : ObjectSchemaValidationException(object_type), m_old_property(old_property), m_new_property(new_property) { if (new_property.type != old_property.type) { @@ -554,7 +554,7 @@ MismatchedPropertiesException::MismatchedPropertiesException(std::string object_ } } -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) +ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string const& object_type, std::string const& old_primary, std::string const& 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."; @@ -564,13 +564,13 @@ ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string object_type, } } -InvalidPrimaryKeyException::InvalidPrimaryKeyException(std::string object_type, std::string primary) : +InvalidPrimaryKeyException::InvalidPrimaryKeyException(std::string const& object_type, std::string const& primary) : ObjectSchemaValidationException(object_type), m_primary_key(primary) { m_what = "Specified primary key property '" + primary + "' does not exist."; } -DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string object_type) : ObjectSchemaValidationException(object_type) +DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const& object_type) : ObjectSchemaValidationException(object_type) { m_what = "Duplicate primary keys for object '" + object_type + "'."; } diff --git a/object_store.hpp b/object_store.hpp index 89a7108e..9b260df8 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,14 +19,14 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP -#include -#include -#include -#include - #include "object_schema.hpp" #include "property.hpp" +#include + +#include +#include + namespace realm { class ObjectSchemaValidationException; using Schema = std::vector; @@ -137,7 +137,7 @@ namespace realm { class DuplicatePrimaryKeyValueException : public MigrationException { public: - DuplicatePrimaryKeyValueException(std::string object_type, Property const& property); + DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property); std::string object_type() const { return m_object_type; } Property const& property() const { return m_property; } private: @@ -148,16 +148,16 @@ namespace realm { // Schema validation exceptions class SchemaValidationException : public ObjectStoreException { public: - SchemaValidationException(std::vector errors); - std::vector &validation_errors() { return m_validation_errors; } + SchemaValidationException(std::vector const& errors); + std::vector const& validation_errors() const { return m_validation_errors; } private: std::vector m_validation_errors; }; class ObjectSchemaValidationException : public ObjectStoreException { public: - ObjectSchemaValidationException(std::string object_type) : m_object_type(object_type) {} - ObjectSchemaValidationException(std::string object_type, std::string message) : + ObjectSchemaValidationException(std::string const& object_type) : m_object_type(object_type) {} + ObjectSchemaValidationException(std::string const& object_type, std::string const& message) : m_object_type(object_type) { m_what = message; } std::string object_type() const { return m_object_type; } protected: @@ -166,7 +166,7 @@ namespace realm { class ObjectSchemaPropertyException : public ObjectSchemaValidationException { public: - ObjectSchemaPropertyException(std::string object_type, Property const& property) : + ObjectSchemaPropertyException(std::string const& object_type, Property const& property) : ObjectSchemaValidationException(object_type), m_property(property) {} Property const& property() const { return m_property; } private: @@ -175,37 +175,37 @@ namespace realm { class PropertyTypeNotIndexableException : public ObjectSchemaPropertyException { public: - PropertyTypeNotIndexableException(std::string object_type, Property const& property); + PropertyTypeNotIndexableException(std::string const& object_type, Property const& property); }; class ExtraPropertyException : public ObjectSchemaPropertyException { public: - ExtraPropertyException(std::string object_type, Property const& property); + ExtraPropertyException(std::string const& object_type, Property const& property); }; class MissingPropertyException : public ObjectSchemaPropertyException { public: - MissingPropertyException(std::string object_type, Property const& property); + MissingPropertyException(std::string const& object_type, Property const& property); }; class InvalidNullabilityException : public ObjectSchemaPropertyException { public: - InvalidNullabilityException(std::string object_type, Property const& property); + InvalidNullabilityException(std::string const& object_type, Property const& property); }; class MissingObjectTypeException : public ObjectSchemaPropertyException { public: - MissingObjectTypeException(std::string object_type, Property const& property); + MissingObjectTypeException(std::string const& object_type, Property const& property); }; class DuplicatePrimaryKeysException : public ObjectSchemaValidationException { public: - DuplicatePrimaryKeysException(std::string object_type); + DuplicatePrimaryKeysException(std::string const& object_type); }; class MismatchedPropertiesException : public ObjectSchemaValidationException { public: - MismatchedPropertiesException(std::string object_type, Property const& old_property, Property const& new_property); + MismatchedPropertiesException(std::string const& object_type, Property const& old_property, Property const& new_property); Property const& old_property() const { return m_old_property; } Property const& new_property() const { return m_new_property; } private: @@ -214,7 +214,7 @@ namespace realm { class ChangedPrimaryKeyException : public ObjectSchemaValidationException { public: - ChangedPrimaryKeyException(std::string object_type, std::string old_primary, std::string new_primary); + ChangedPrimaryKeyException(std::string const& object_type, std::string const& old_primary, std::string const& new_primary); std::string old_primary() const { return m_old_primary; } std::string new_primary() const { return m_new_primary; } private: @@ -223,7 +223,7 @@ namespace realm { class InvalidPrimaryKeyException : public ObjectSchemaValidationException { public: - InvalidPrimaryKeyException(std::string object_type, std::string primary_key); + InvalidPrimaryKeyException(std::string const& object_type, std::string const& primary_key); std::string primary_key() const { return m_primary_key; } private: std::string m_primary_key; From 924482a30541fd3c8781c63730c788dfe30ae251 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 10:02:29 -0700 Subject: [PATCH 36/57] Fix checks for what types of columns can be optional --- object_store.cpp | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 61711078..2c42f64b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -204,22 +204,18 @@ std::vector ObjectStore::verify_object_schema(O } // check nullablity + if (current_prop.is_nullable) { #if REALM_NULL_STRINGS == 1 - if (current_prop.type == PropertyTypeArray && current_prop.is_nullable) { + if (current_prop.type == PropertyTypeArray || current_prop.type == PropertyTypeAny) { +#else + if (current_prop.type != PropertyTypeObject) { +#endif + exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); + } + } + else if (current_prop.type == PropertyTypeObject) { exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); } -#else - if (current_prop.type == PropertyTypeObject) { - if (!current_prop.is_nullable) { - exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); - } - } - else { - if (current_prop.is_nullable) { - exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); - } - } -#endif // check primary keys if (current_prop.is_primary) { @@ -517,20 +513,16 @@ MissingPropertyException::MissingPropertyException(std::string const& object_typ InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) : ObjectSchemaPropertyException(object_type, property) { -#if REALM_NULL_STRINGS == 1 - m_what = "'Array' property '" + property.name + "' cannot be nullable"; -#else if (property.type == PropertyTypeObject) { - if (!property.is_nullable) { - m_what = "'Object' property '" + property.name + "' must be nullable."; - } + m_what = "'Object' property '" + property.name + "' must be nullable."; } else { - if (property.is_nullable) { - m_what = "Only 'Object' property types are nullable"; - } - } +#if REALM_NULL_STRINGS == 1 + m_what = "Array or Mixed property '" + property.name + "' cannot be nullable"; +#else + m_what = "Only 'Object' property types are nullable"; #endif + } } MissingObjectTypeException::MissingObjectTypeException(std::string const& object_type, Property const& property) : From 9075917334c76ec879d16b8693237bf26b7d3a95 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 10:10:18 -0700 Subject: [PATCH 37/57] Fix a comment --- object_store.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_store.cpp b/object_store.cpp index 2c42f64b..bcfa964f 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -295,7 +295,7 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update for (auto& target_prop : target_props) { auto current_prop = current_schema.property_for_name(target_prop.name); - // add any new properties (new name or different type) + // add any new properties (no old column or old column was removed due to not matching) if (!current_prop || current_prop->table_column == npos) { switch (target_prop.type) { // for objects and arrays, we have to specify target table From a5f21e5e8291681c2251e3503ed0905ce6c43668 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 11:30:00 -0700 Subject: [PATCH 38/57] Reduce the scope of a variable --- shared_realm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 68972e4d..93e1aa13 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -355,8 +355,7 @@ bool Realm::refresh() uint64_t Realm::get_schema_version(const realm::Realm::Config &config) { - auto existing_realm = s_global_cache.get_any_realm(config.path); - if (existing_realm) { + if (auto existing_realm = s_global_cache.get_any_realm(config.path)) { return existing_realm->config().schema_version; } From d72d79bb05a2392ce20ea9ae12dadfc7cc319b3e Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 11:34:02 -0700 Subject: [PATCH 39/57] Make Realm::compact() more robust Throw if it's called on a read-only Realm and ensure the Realm is left in a valid state regardless of the starting state. --- shared_realm.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 93e1aa13..ab72b638 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -291,20 +291,21 @@ bool Realm::compact() { verify_thread(); - bool success = false; + if (m_config.read_only) { + throw InvalidTransactionException("Can't compact a read-only Realm"); + } if (m_in_transaction) { throw InvalidTransactionException("Can't compact a Realm within a write transaction"); } + Group* group = read_group(); for (auto &object_schema : *m_config.schema) { - ObjectStore::table_for_object_type(read_group(), object_schema.name)->optimize(); + ObjectStore::table_for_object_type(group, object_schema.name)->optimize(); } - m_shared_group->end_read(); - success = m_shared_group->compact(); - m_shared_group->begin_read(); + m_group = nullptr; - return success; + return m_shared_group->compact(); } void Realm::notify() From df6d6e86188893de3b23d63bff1b6ddc4e6cd8e4 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 14:03:58 -0700 Subject: [PATCH 40/57] Add a Schema class, move lookup by name and internal-consistency checks there --- object_store.cpp | 65 +++++++----------------------------------------- object_store.hpp | 5 ++-- shared_realm.cpp | 6 +++++ shared_realm.hpp | 1 + 4 files changed, 18 insertions(+), 59 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index bcfa964f..f4ebcfbd 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -18,6 +18,8 @@ #include "object_store.hpp" +#include "schema.hpp" + #include #include #include @@ -147,17 +149,11 @@ static inline bool property_has_changed(Property const& p1, Property const& p2) || p1.is_nullable != p2.is_nullable; } -static bool compare_by_name(ObjectSchema const& lft, ObjectSchema const& rgt) { - return lft.name < rgt.name; -} - void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) { - std::sort(begin(target_schema), end(target_schema), compare_by_name); - std::vector errors; for (auto &object_schema : target_schema) { - auto matching_schema = std::lower_bound(begin(actual_schema), end(actual_schema), object_schema, compare_by_name); - if (matching_schema == end(actual_schema) || matching_schema->name != object_schema.name) { + auto matching_schema = actual_schema.find(object_schema); + if (matching_schema == actual_schema.end()) { if (!allow_missing_tables) { errors.emplace_back(ObjectSchemaValidationException(object_schema.name, "Missing table for object type '" + object_schema.name + "'.")); @@ -165,7 +161,7 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_sche continue; } - auto more_errors = verify_object_schema(*matching_schema, object_schema, target_schema); + auto more_errors = verify_object_schema(*matching_schema, object_schema); errors.insert(errors.end(), more_errors.begin(), more_errors.end()); } if (errors.size()) { @@ -174,18 +170,10 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_sche } std::vector ObjectStore::verify_object_schema(ObjectSchema const& table_schema, - ObjectSchema& target_schema, - Schema const& schema) { + ObjectSchema& target_schema) { std::vector exceptions; - ObjectSchema cmp; - auto schema_contains_table = [&](std::string const& name) { - cmp.name = name; - return std::binary_search(begin(schema), end(schema), cmp, compare_by_name); - }; - // check to see if properties are the same - const Property *primary = nullptr; for (auto& current_prop : table_schema.properties) { auto target_prop = target_schema.property_for_name(current_prop.name); @@ -198,40 +186,6 @@ std::vector ObjectStore::verify_object_schema(O continue; } - // check object_type existence - if (current_prop.object_type.length() && !schema_contains_table(current_prop.object_type)) { - exceptions.emplace_back(MissingObjectTypeException(table_schema.name, current_prop)); - } - - // check nullablity - if (current_prop.is_nullable) { -#if REALM_NULL_STRINGS == 1 - if (current_prop.type == PropertyTypeArray || current_prop.type == PropertyTypeAny) { -#else - if (current_prop.type != PropertyTypeObject) { -#endif - exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); - } - } - else if (current_prop.type == PropertyTypeObject) { - exceptions.emplace_back(InvalidNullabilityException(table_schema.name, current_prop)); - } - - // check primary keys - if (current_prop.is_primary) { - if (primary) { - exceptions.emplace_back(DuplicatePrimaryKeysException(table_schema.name)); - } - primary = ¤t_prop; - } - - // check indexable - if (current_prop.is_indexed) { - if (current_prop.type != PropertyTypeString && current_prop.type != PropertyTypeInt) { - exceptions.emplace_back(PropertyTypeNotIndexableException(table_schema.name, current_prop)); - } - } - // create new property with aligned column target_prop->table_column = current_prop.table_column; } @@ -347,8 +301,8 @@ bool ObjectStore::is_schema_at_version(const Group *group, uint64_t version) { bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) { for (auto const& target_schema : schema) { - auto matching_schema = std::lower_bound(begin(old_schema), end(old_schema), target_schema, compare_by_name); - if (matching_schema == end(old_schema) || matching_schema->name != target_schema.name) { + auto matching_schema = old_schema.find(target_schema); + if (matching_schema == end(old_schema)) { // Table doesn't exist return true; } @@ -405,14 +359,13 @@ bool ObjectStore::update_realm_with_schema(Group *group, Schema const& old_schem } Schema ObjectStore::schema_from_group(const Group *group) { - Schema schema; + std::vector schema; for (size_t i = 0; i < group->size(); i++) { std::string object_type = object_type_for_table_name(group->get_table_name(i)); if (object_type.length()) { schema.emplace_back(group, object_type); } } - std::sort(begin(schema), end(schema), compare_by_name); return schema; } diff --git a/object_store.hpp b/object_store.hpp index 9b260df8..f38524f8 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -29,7 +29,7 @@ namespace realm { class ObjectSchemaValidationException; - using Schema = std::vector; + class Schema; class ObjectStore { public: @@ -90,8 +90,7 @@ namespace realm { // updates the column mapping on the target_schema // returns array of validation errors static std::vector verify_object_schema(ObjectSchema const& expected, - ObjectSchema &target_schema, - Schema const& schema); + ObjectSchema &target_schema); // get primary key property name for object type static StringData get_primary_key_for_object(const Group *group, StringData object_type); diff --git a/shared_realm.cpp b/shared_realm.cpp index ab72b638..55707918 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -20,6 +20,7 @@ #include "external_commit_helper.hpp" #include "realm_delegate.hpp" +#include "schema.hpp" #include "transact_log_handler.hpp" #include @@ -45,6 +46,8 @@ Realm::Config::Config(const Config& c) } } +Realm::Config::~Config() = default; + Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) { if (&c != this) { @@ -154,6 +157,7 @@ SharedRealm Realm::get_shared_realm(Config config) if (realm->m_config.schema_version == ObjectStore::NotVersioned) { throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); } + target_schema->validate(); ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true); realm->m_config.schema = std::move(target_schema); } @@ -171,6 +175,8 @@ SharedRealm Realm::get_shared_realm(Config config) bool Realm::update_schema(std::unique_ptr schema, uint64_t version) { + schema->validate(); + bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema)); if (!needs_update) { ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only); diff --git a/shared_realm.hpp b/shared_realm.hpp index e1c1a0c6..aa19071e 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -56,6 +56,7 @@ namespace realm { Config() = default; Config(Config&&) = default; Config(const Config& c); + ~Config(); Config& operator=(Config const&); Config& operator=(Config&&) = default; From 1fcc5a1be8f031aa8f67e65e79e9037c28bf76d6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 14:24:11 -0700 Subject: [PATCH 41/57] Simplify column shifting for removed properties a little --- object_store.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index f4ebcfbd..2cd37f0b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -233,16 +233,15 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update // remove extra columns size_t deleted = 0; for (auto& current_prop : current_schema.properties) { + current_prop.table_column -= deleted; + auto target_prop = target_object_schema->property_for_name(current_prop.name); if (!target_prop || property_has_changed(current_prop, *target_prop)) { - table->remove_column(current_prop.table_column - deleted); + table->remove_column(current_prop.table_column); ++deleted; current_prop.table_column = npos; changed = true; } - else { - current_prop.table_column -= deleted; - } } // add missing columns From 981e7007f74b5777ad25f76c7d3c23041e8a712b Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Sep 2015 14:54:41 -0700 Subject: [PATCH 42/57] Fix error in cleanup after an error during a migration --- shared_realm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 55707918..9fe02a0a 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -215,7 +215,7 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) cancel_transaction(); } m_config.schema_version = old_config.schema_version; - m_config.schema = std::move(m_config.schema); + m_config.schema = std::move(old_config.schema); throw; } } From cb7360c6a3ab8d9966b7042941f53290f5d5e2b4 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 9 Sep 2015 17:16:32 -0700 Subject: [PATCH 43/57] use latest object store changes --- object_store.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/object_store.hpp b/object_store.hpp index f38524f8..3908077a 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,6 +19,7 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP +#include "schema.hpp" #include "object_schema.hpp" #include "property.hpp" From 0c18978887051c7edd48865225aeee68705d0daf Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 10 Sep 2015 13:49:50 -0700 Subject: [PATCH 44/57] Send changes_available() even if autorefresh is enabled --- shared_realm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 9fe02a0a..8b003d30 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -319,6 +319,9 @@ void Realm::notify() verify_thread(); if (m_shared_group->has_changed()) { // Throws + if (m_delegate) { + m_delegate->changes_available(); + } if (m_auto_refresh) { if (m_group) { transaction::advance(*m_shared_group, *m_history, m_delegate.get()); @@ -327,9 +330,6 @@ void Realm::notify() m_delegate->did_change({}, {}); } } - else if (m_delegate) { - m_delegate->changes_available(); - } } } From b70e5432b7f87c2ebf62b357a814d24025169ae1 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 12 Oct 2015 02:02:23 -0700 Subject: [PATCH 45/57] Setting properties outside transaction should throw Resolves #40 --- object_accessor.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/object_accessor.hpp b/object_accessor.hpp index dfb24f69..fa6d6366 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -98,6 +98,10 @@ namespace realm { { using Accessor = NativeAccessor; + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only set property values within a transaction."); + } + size_t column = property.table_column; switch (property.type) { case PropertyTypeBool: From c8b6efb320b6153e8ee2cdc0302f290345169279 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 12 Oct 2015 15:35:13 -0700 Subject: [PATCH 46/57] Out of bounds getters for Results return undefined --- results.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/results.cpp b/results.cpp index f33fc465..e4ad126e 100644 --- a/results.cpp +++ b/results.cpp @@ -43,7 +43,7 @@ Row Results::get(std::size_t row_ndx) { verify_attached(); if (row_ndx >= table_view.size()) { - throw std::range_error(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + + 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); From ad677b78736d3e1be801fd77bee7d95471609960 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 12 Oct 2015 16:43:05 -0700 Subject: [PATCH 47/57] allow running all tests at the same path by invalidating all cached realm paths between test runs --- shared_realm.cpp | 17 ++++++++++++++++- shared_realm.hpp | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 8b003d30..77d39c21 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -198,7 +198,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 { @@ -438,6 +440,19 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id) } } +void RealmCache::invalidate_all() +{ + std::lock_guard lock(m_mutex); + + for (auto &path_realms:m_cache) { + for (auto &realm_iter:path_realms.second) { + if (auto realm = realm_iter.second.lock()) { + realm->invalidate(); + } + } + } +} + void RealmCache::clear() { std::lock_guard lock(m_mutex); diff --git a/shared_realm.hpp b/shared_realm.hpp index aa19071e..4721e37e 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -131,6 +131,7 @@ namespace realm { SharedRealm get_any_realm(const std::string &path); void remove(const std::string &path, std::thread::id thread_id); void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id()); + void invalidate_all(); void clear(); private: From b27b7941f031ffd4c14c6a3cacc6ba6ec9a298f1 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 12 Oct 2015 17:18:49 -0700 Subject: [PATCH 48/57] pr fixes --- shared_realm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 77d39c21..42217a8f 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -444,8 +444,8 @@ void RealmCache::invalidate_all() { std::lock_guard lock(m_mutex); - for (auto &path_realms:m_cache) { - for (auto &realm_iter:path_realms.second) { + for (auto &path_realms : m_cache) { + for (auto &realm_iter : path_realms.second) { if (auto realm = realm_iter.second.lock()) { realm->invalidate(); } From 06260bc3f0afe5d3681f9a973790d3ce4537c986 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 13 Oct 2015 14:44:31 -0700 Subject: [PATCH 49/57] move List class to its own file --- list.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ list.hpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 list.cpp create mode 100644 list.hpp diff --git a/list.cpp b/list.cpp new file mode 100644 index 00000000..d4b2bb57 --- /dev/null +++ b/list.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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() { + return link_view->size(); +} + +Row List::get(std::size_t row_ndx) { + verify_valid_row(row_ndx); + return link_view->get(row_ndx); +} + +void List::set(std::size_t row_ndx, std::size_t target_row_ndx) { + verify_valid_row(row_ndx); + link_view->set(row_ndx, target_row_ndx); +} + +void List::verify_valid_row(std::size_t row_ndx) { + size_t size = link_view->size(); + if (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 (!link_view->is_attached()) { + throw std::runtime_error("Tableview is not attached"); + } + link_view->sync_if_needed(); +} diff --git a/list.hpp b/list.hpp new file mode 100644 index 00000000..a1fa1dde --- /dev/null +++ b/list.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 { + struct List { + List(SharedRealm &r, ObjectSchema &s, LinkViewRef l) : realm(r), object_schema(s), link_view(l) {} + // FIXME - all should be const + SharedRealm realm; + ObjectSchema &object_schema; + LinkViewRef link_view; + + size_t size(); + Row get(std::size_t row_ndx); + void set(std::size_t row_ndx, std::size_t target_row_ndx); + void verify_valid_row(std::size_t row_ndx); + void verify_attached(); + }; +} + + +#endif /* REALM_LIST_HPP */ From 8a2e24daac5e9b06abd85618eb208314fdb988a6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 13 Oct 2015 15:25:06 -0700 Subject: [PATCH 50/57] move property getters to object store code --- object_accessor.cpp | 19 ------ object_accessor.hpp | 140 ++++++++++++++++++++++++++++++++------------ 2 files changed, 102 insertions(+), 57 deletions(-) delete mode 100644 object_accessor.cpp diff --git a/object_accessor.cpp b/object_accessor.cpp deleted file mode 100644 index 2516cc5d..00000000 --- a/object_accessor.cpp +++ /dev/null @@ -1,19 +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_accessor.hpp" diff --git a/object_accessor.hpp b/object_accessor.hpp index fa6d6366..69bffa70 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -21,43 +21,9 @@ #include #include "shared_realm.hpp" +#include "list.hpp" namespace realm { - template - class NativeAccessor { - public: - // - // Value converters - template specializations must be implemented for each platform - // - 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, const ObjectSchema &object_schema, const std::string &prop_name); - static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); - - static bool to_bool(ContextType ctx, ValueType &val); - static long long to_long(ContextType ctx, ValueType &val); - static float to_float(ContextType ctx, ValueType &val); - static double to_double(ContextType ctx, ValueType &val); - static std::string to_string(ContextType ctx, ValueType &val); - static DateTime to_datetime(ContextType ctx, ValueType &val); - - static bool is_null(ContextType ctx, ValueType &val); - - // 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, std::string &type, bool try_update); - - // array value acessors - static size_t array_size(ContextType ctx, ValueType &val); - static ValueType array_value_at_index(ContextType ctx, ValueType &val, size_t index); - - // - // Deprecated - // - static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); } - }; class Object { public: @@ -67,10 +33,13 @@ namespace realm { ObjectSchema &object_schema; Row row; - // property setter + // 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); @@ -78,6 +47,53 @@ namespace realm { private: template inline void set_property_value_impl(ContextType ctx, Property &property, ValueType value, bool try_update); + template + inline ValueType get_property_value_impl(ContextType ctx, 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, const ObjectSchema &object_schema, const std::string &prop_name); + static ValueType default_value_for_property(ContextType ctx, 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 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, 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"); } }; // @@ -93,6 +109,16 @@ namespace realm { set_property_value_impl(ctx, *prop, value, try_update); }; + template + inline ValueType Object::get_property_value(ContextType ctx, std::string prop_name) + { + Property *prop = object_schema.property_for_name(prop_name); + if (!prop) { + throw std::runtime_error("Setting 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, Property &property, ValueType value, bool try_update) { @@ -140,9 +166,9 @@ namespace realm { case PropertyTypeArray: { realm::LinkViewRef link_view = row.get_linklist(column); link_view->clear(); - size_t count = Accessor::array_size(ctx, value); + size_t count = Accessor::list_size(ctx, value); for (size_t i = 0; i < count; i++) { - ValueType element = Accessor::array_value_at_index(ctx, value, i); + ValueType element = Accessor::list_value_at_index(ctx, value, i); link_view->add(Accessor::to_object_index(ctx, realm, element, property.object_type, try_update)); } break; @@ -150,6 +176,44 @@ namespace realm { } } + template + inline ValueType Object::get_property_value_impl(ContextType ctx, Property &property) + { + using Accessor = NativeAccessor; + + size_t column = property.table_column; + switch (property.type) { + case PropertyTypeBool: + return Accessor::from_bool(ctx, row.get_bool(column)); + case PropertyTypeInt: + return Accessor::from_long(ctx, row.get_int(column)); + case PropertyTypeFloat: + return Accessor::from_float(ctx, row.get_float(column)); + case PropertyTypeDouble: + return Accessor::from_double(ctx, row.get_double(column)); + case PropertyTypeString: + return Accessor::from_string(ctx, row.get_string(column)); + case PropertyTypeData: + return Accessor::from_string(ctx, (std::string)row.get_binary(column)); + case PropertyTypeAny: + throw "Any not supported"; + case PropertyTypeDate: + return Accessor::from_datetime(ctx, row.get_datetime(column)); + case PropertyTypeObject: { + auto linkObjectSchema = realm->config().schema->find(property.object_type); + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), linkObjectSchema->name); + if (row.is_null_link(property.table_column)) { + return Accessor::null_value(ctx); + } + return Accessor::from_object(ctx, std::move(Object(realm, *linkObjectSchema, table->get(row.get_link(column))))); + } + case PropertyTypeArray: { + auto arrayObjectSchema = realm->config().schema->find(property.object_type); + return Accessor::from_list(ctx, std::move(List(realm, *arrayObjectSchema, static_cast(row.get_linklist(column))))); + } + } + } + template inline Object Object::create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update) { From 6dfeaf8080e396f937f16f51a1efba425ececb68 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 15 Sep 2015 16:25:16 +0200 Subject: [PATCH 51/57] Move things which are not part of the API to an impl directory/namespace --- {apple => impl/apple}/external_commit_helper.cpp | 1 + {apple => impl/apple}/external_commit_helper.hpp | 2 ++ transact_log_handler.cpp => impl/transact_log_handler.cpp | 2 ++ transact_log_handler.hpp => impl/transact_log_handler.hpp | 2 ++ shared_realm.cpp | 1 + shared_realm.hpp | 7 +++++-- 6 files changed, 13 insertions(+), 2 deletions(-) rename {apple => impl/apple}/external_commit_helper.cpp (99%) rename {apple => impl/apple}/external_commit_helper.hpp (98%) rename transact_log_handler.cpp => impl/transact_log_handler.cpp (99%) rename transact_log_handler.hpp => impl/transact_log_handler.hpp (97%) diff --git a/apple/external_commit_helper.cpp b/impl/apple/external_commit_helper.cpp similarity index 99% rename from apple/external_commit_helper.cpp rename to impl/apple/external_commit_helper.cpp index 1444281a..4cf0b523 100644 --- a/apple/external_commit_helper.cpp +++ b/impl/apple/external_commit_helper.cpp @@ -30,6 +30,7 @@ #include using namespace realm; +using namespace realm::_impl; namespace { // Write a byte to a pipe to notify anyone waiting for data on the pipe diff --git a/apple/external_commit_helper.hpp b/impl/apple/external_commit_helper.hpp similarity index 98% rename from apple/external_commit_helper.hpp rename to impl/apple/external_commit_helper.hpp index acf0e1c4..d7acb791 100644 --- a/apple/external_commit_helper.hpp +++ b/impl/apple/external_commit_helper.hpp @@ -26,6 +26,7 @@ namespace realm { class Realm; +namespace _impl { class ExternalCommitHelper { public: ExternalCommitHelper(Realm* realm); @@ -87,6 +88,7 @@ private: FdHolder m_shutdown_write_fd; }; +} // namespace _impl } // namespace realm #endif /* REALM_EXTERNAL_COMMIT_HELPER_HPP */ diff --git a/transact_log_handler.cpp b/impl/transact_log_handler.cpp similarity index 99% rename from transact_log_handler.cpp rename to impl/transact_log_handler.cpp index 3f833b19..e1b95405 100644 --- a/transact_log_handler.cpp +++ b/impl/transact_log_handler.cpp @@ -316,6 +316,7 @@ public: } // anonymous namespace namespace realm { +namespace _impl { namespace transaction { void advance(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { TransactLogHandler(delegate, sg, [&](auto&&... args) { @@ -344,4 +345,5 @@ void cancel(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { } } // namespace transaction +} // namespace _impl } // namespace realm diff --git a/transact_log_handler.hpp b/impl/transact_log_handler.hpp similarity index 97% rename from transact_log_handler.hpp rename to impl/transact_log_handler.hpp index 3a77848f..dbb54a53 100644 --- a/transact_log_handler.hpp +++ b/impl/transact_log_handler.hpp @@ -24,6 +24,7 @@ class RealmDelegate; 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. @@ -41,6 +42,7 @@ void commit(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); // for reverting to the old values sent to delegate void cancel(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); } // namespace transaction +} // namespace _impl } // namespace realm #endif /* REALM_TRANSACT_LOG_HANDLER_HPP */ diff --git a/shared_realm.cpp b/shared_realm.cpp index 8b003d30..2ca84649 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -29,6 +29,7 @@ #include using namespace realm; +using namespace realm::_impl; RealmCache Realm::s_global_cache; diff --git a/shared_realm.hpp b/shared_realm.hpp index aa19071e..dc5c4b92 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -28,13 +28,16 @@ namespace realm { class ClientHistory; - class ExternalCommitHelper; class Realm; class RealmCache; class RealmDelegate; typedef std::shared_ptr SharedRealm; typedef std::weak_ptr WeakRealm; + namespace _impl { + class ExternalCommitHelper; + } + class Realm : public std::enable_shared_from_this { public: @@ -114,7 +117,7 @@ namespace realm { Group *m_group = nullptr; - std::shared_ptr m_notifier; + std::shared_ptr<_impl::ExternalCommitHelper> m_notifier; public: std::unique_ptr m_delegate; From dace77579ba346bd9b7344e73d1f4d10a26a882d Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 19 Oct 2015 13:39:21 -0700 Subject: [PATCH 52/57] store schema and prototypes on the realm object --- object_accessor.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index 69bffa70..58802a06 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -60,8 +60,8 @@ namespace realm { 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, const ObjectSchema &object_schema, const std::string &prop_name); - static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, 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); @@ -260,8 +260,8 @@ namespace realm { 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, object_schema, prop.name)) { - object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update); + 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 std::runtime_error("Missing property value for property " + prop.name); From 1fa7c018eabff1d992bbed06cc8ea9c703b815b6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 19 Oct 2015 14:25:35 -0700 Subject: [PATCH 53/57] clean up per realm resources/threads --- shared_realm.cpp | 19 +++++++++++++++---- shared_realm.hpp | 3 ++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 42217a8f..8eab3700 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -295,6 +295,16 @@ void Realm::invalidate() m_group = nullptr; } +void Realm::close() +{ + invalidate(); + if (m_notifier) { + m_notifier->remove_realm(this); + m_notifier = nullptr; + } + m_delegate = nullptr; +} + bool Realm::compact() { verify_thread(); @@ -440,14 +450,15 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id) } } -void RealmCache::invalidate_all() +void RealmCache::close_all(std::thread::id thread_id) { std::lock_guard lock(m_mutex); for (auto &path_realms : m_cache) { - for (auto &realm_iter : path_realms.second) { - if (auto realm = realm_iter.second.lock()) { - realm->invalidate(); + auto thread_realm = path_realms.second.find(thread_id); + if (thread_realm != path_realms.second.end()) { + if (auto realm = thread_realm->second.lock()) { + realm->close(); } } } diff --git a/shared_realm.hpp b/shared_realm.hpp index 4721e37e..310c41a1 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -94,6 +94,7 @@ namespace realm { void invalidate(); bool compact(); + void close(); std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; @@ -131,7 +132,7 @@ namespace realm { SharedRealm get_any_realm(const std::string &path); void remove(const std::string &path, std::thread::id thread_id); void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id()); - void invalidate_all(); + void close_all(std::thread::id thread_id = std::this_thread::get_id()); void clear(); private: From 9f1702a10f2f6ec5e645dd2b7f325e410fd89b30 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 19 Oct 2015 18:52:56 -0700 Subject: [PATCH 54/57] Support migrating required columns to optional, preserving their contents. Required columns are migrated to optional by creating a new nullable column, copying the data from the required column to the optional column, then removing the original required column. --- object_store.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/object_store.cpp b/object_store.cpp index 035ce4fc..757d207f 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -149,6 +149,13 @@ static inline bool property_has_changed(Property const& p1, Property const& p2) || p1.is_nullable != p2.is_nullable; } +static inline bool property_can_be_migrated_to_nullable(const Property& old_property, const Property& new_property) { + return old_property.type == new_property.type + && !old_property.is_nullable + && new_property.is_nullable + && new_property.name == old_property.name; +} + void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_schema, bool allow_missing_tables) { std::vector errors; for (auto &object_schema : target_schema) { @@ -205,6 +212,45 @@ std::vector ObjectStore::verify_object_schema(O return exceptions; } +template +static void copy_property_values(const Property& old_property, const Property& new_property, Table& table, + T (Table::*getter)(std::size_t, std::size_t) const noexcept, + void (Table::*setter)(std::size_t, std::size_t, T)) { + 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++) { + (table.*setter)(new_column, i, (table.*getter)(old_column, i)); + } +} + +static void copy_property_values(const Property& source, const Property& destination, Table& table) { + switch (destination.type) { + case PropertyTypeInt: + copy_property_values(source, destination, table, &Table::get_int, &Table::set_int); + break; + case PropertyTypeBool: + copy_property_values(source, destination, table, &Table::get_bool, &Table::set_bool); + break; + case PropertyTypeFloat: + copy_property_values(source, destination, table, &Table::get_float, &Table::set_float); + break; + case PropertyTypeDouble: + copy_property_values(source, destination, table, &Table::get_double, &Table::set_double); + break; + case PropertyTypeString: + copy_property_values(source, destination, table, &Table::get_string, &Table::set_string); + break; + case PropertyTypeData: + copy_property_values(source, destination, table, &Table::get_binary, &Table::set_binary); + break; + case PropertyTypeDate: + copy_property_values(source, destination, table, &Table::get_datetime, &Table::set_datetime); + break; + default: + break; + } +} + // 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 @@ -230,13 +276,31 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update ObjectSchema current_schema(group, target_object_schema->name); std::vector &target_props = target_object_schema->properties; + // handle columns changing from required to optional + for (auto& current_prop : current_schema.properties) { + auto target_prop = target_object_schema->property_for_name(current_prop.name); + if (!target_prop || !property_can_be_migrated_to_nullable(current_prop, *target_prop)) + continue; + + target_prop->table_column = current_prop.table_column; + current_prop.table_column = current_prop.table_column + 1; + + table->insert_column(target_prop->table_column, DataType(target_prop->type), target_prop->name, target_prop->is_nullable); + copy_property_values(current_prop, *target_prop, *table); + table->remove_column(current_prop.table_column); + + current_prop.table_column = target_prop->table_column; + changed = true; + } + // remove extra columns size_t deleted = 0; for (auto& current_prop : current_schema.properties) { current_prop.table_column -= deleted; auto target_prop = target_object_schema->property_for_name(current_prop.name); - if (!target_prop || property_has_changed(current_prop, *target_prop)) { + if (!target_prop || (property_has_changed(current_prop, *target_prop) + && !property_can_be_migrated_to_nullable(current_prop, *target_prop))) { table->remove_column(current_prop.table_column); ++deleted; current_prop.table_column = npos; From 0b45772a0b715d4ceb4c99aaae4835feda15158a Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 19 Oct 2015 18:53:09 -0700 Subject: [PATCH 55/57] Add a test showing our behavior when migrating from an optional column to a required column. Optional values are not automatically migrated to required columns since it is a lossy process. This test case revealed an issue where the number of objects can be lost if all properties of an object were optional and are all being migrated to required. This happens because the migration process removes the optional columns in a first pass, and recreates them as required in a second pass. Since this results in all columns being removed, we lose track of how many objects were stored. We avoid this by detecting the case where we are about to remove the last column and inserting a placeholder column that we'll remove after inserting the new columns. --- object_store.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/object_store.cpp b/object_store.cpp index 757d207f..ba7a868b 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -293,6 +293,8 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update changed = true; } + bool inserted_placeholder_column = false; + // remove extra columns size_t deleted = 0; for (auto& current_prop : current_schema.properties) { @@ -301,6 +303,13 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update auto target_prop = target_object_schema->property_for_name(current_prop.name); if (!target_prop || (property_has_changed(current_prop, *target_prop) && !property_can_be_migrated_to_nullable(current_prop, *target_prop))) { + if (deleted == current_schema.properties.size() - 1) { + // We're about to remove the last column from the table. Insert a placeholder column to preserve + // the number of rows in the table for the addition of new columns below. + table->add_column(type_Bool, "placeholder"); + inserted_placeholder_column = true; + } + table->remove_column(current_prop.table_column); ++deleted; current_prop.table_column = npos; @@ -336,6 +345,15 @@ bool ObjectStore::create_tables(Group *group, Schema &target_schema, bool update } } + if (inserted_placeholder_column) { + // We inserted a placeholder due to removing all columns from the table. Remove it, and update the indices + // of any columns that we inserted after it. + table->remove_column(0); + for (auto& target_prop : target_props) { + target_prop.table_column--; + } + } + // update table metadata if (target_object_schema->primary_key.length()) { // if there is a primary key set, check if it is the same as the old key From c9ef337552686065448ac721fdf40cb3356f715e Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 26 Oct 2015 13:24:27 -0700 Subject: [PATCH 56/57] make object members private --- object_accessor.hpp | 83 +++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index 58802a06..2ddf43d3 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -27,11 +27,7 @@ namespace realm { class Object { public: - Object(SharedRealm &r, ObjectSchema &s, Row o) : realm(r), object_schema(s), row(o) {} - // FIXME - all should be const - SharedRealm realm; - ObjectSchema &object_schema; - Row row; + Object(SharedRealm &r, ObjectSchema &s, Row o) : m_realm(r), object_schema(s), m_row(o) {} // property getter/setter template @@ -43,12 +39,19 @@ namespace realm { // 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, Property &property, ValueType value, bool try_update); + 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, Property &property); + inline ValueType get_property_value_impl(ContextType ctx, const Property &property); }; // @@ -82,7 +85,7 @@ namespace realm { // 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, std::string &type, bool try_update); + 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 @@ -102,7 +105,7 @@ namespace realm { template inline void Object::set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update) { - Property *prop = object_schema.property_for_name(prop_name); + const Property *prop = object_schema.property_for_name(prop_name); if (!prop) { throw std::runtime_error("Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); } @@ -112,7 +115,7 @@ namespace realm { template inline ValueType Object::get_property_value(ContextType ctx, std::string prop_name) { - Property *prop = object_schema.property_for_name(prop_name); + const Property *prop = object_schema.property_for_name(prop_name); if (!prop) { throw std::runtime_error("Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); } @@ -120,56 +123,56 @@ namespace realm { }; template - inline void Object::set_property_value_impl(ContextType ctx, Property &property, ValueType value, bool try_update) + inline void Object::set_property_value_impl(ContextType ctx, const Property &property, ValueType value, bool try_update) { using Accessor = NativeAccessor; - if (!realm->is_in_transaction()) { + if (!m_realm->is_in_transaction()) { throw std::runtime_error("Can only set property values within a transaction."); } size_t column = property.table_column; switch (property.type) { case PropertyTypeBool: - row.set_bool(column, Accessor::to_bool(ctx, value)); + m_row.set_bool(column, Accessor::to_bool(ctx, value)); break; case PropertyTypeInt: - row.set_int(column, Accessor::to_long(ctx, value)); + m_row.set_int(column, Accessor::to_long(ctx, value)); break; case PropertyTypeFloat: - row.set_float(column, Accessor::to_float(ctx, value)); + m_row.set_float(column, Accessor::to_float(ctx, value)); break; case PropertyTypeDouble: - row.set_double(column, Accessor::to_double(ctx, value)); + m_row.set_double(column, Accessor::to_double(ctx, value)); break; case PropertyTypeString: - row.set_string(column, Accessor::to_string(ctx, value)); + m_row.set_string(column, Accessor::to_string(ctx, value)); break; case PropertyTypeData: - row.set_binary(column, BinaryData(Accessor::to_string(ctx, value))); + m_row.set_binary(column, BinaryData(Accessor::to_string(ctx, value))); break; case PropertyTypeAny: - row.set_mixed(column, Accessor::to_mixed(ctx, value)); + m_row.set_mixed(column, Accessor::to_mixed(ctx, value)); break; case PropertyTypeDate: - row.set_datetime(column, Accessor::to_datetime(ctx, value)); + m_row.set_datetime(column, Accessor::to_datetime(ctx, value)); break; case PropertyTypeObject: { if (Accessor::is_null(ctx, value)) { - row.nullify_link(column); + m_row.nullify_link(column); } else { - row.set_link(column, Accessor::to_object_index(ctx, realm, value, property.object_type, try_update)); + 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 = row.get_linklist(column); + 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, realm, element, property.object_type, try_update)); + link_view->add(Accessor::to_object_index(ctx, m_realm, element, property.object_type, try_update)); } break; } @@ -177,39 +180,39 @@ namespace realm { } template - inline ValueType Object::get_property_value_impl(ContextType ctx, Property &property) + inline ValueType Object::get_property_value_impl(ContextType ctx, const Property &property) { using Accessor = NativeAccessor; size_t column = property.table_column; switch (property.type) { case PropertyTypeBool: - return Accessor::from_bool(ctx, row.get_bool(column)); + return Accessor::from_bool(ctx, m_row.get_bool(column)); case PropertyTypeInt: - return Accessor::from_long(ctx, row.get_int(column)); + return Accessor::from_long(ctx, m_row.get_int(column)); case PropertyTypeFloat: - return Accessor::from_float(ctx, row.get_float(column)); + return Accessor::from_float(ctx, m_row.get_float(column)); case PropertyTypeDouble: - return Accessor::from_double(ctx, row.get_double(column)); + return Accessor::from_double(ctx, m_row.get_double(column)); case PropertyTypeString: - return Accessor::from_string(ctx, row.get_string(column)); + return Accessor::from_string(ctx, m_row.get_string(column)); case PropertyTypeData: - return Accessor::from_string(ctx, (std::string)row.get_binary(column)); + return Accessor::from_string(ctx, (std::string)m_row.get_binary(column)); case PropertyTypeAny: throw "Any not supported"; case PropertyTypeDate: - return Accessor::from_datetime(ctx, row.get_datetime(column)); + return Accessor::from_datetime(ctx, m_row.get_datetime(column)); case PropertyTypeObject: { - auto linkObjectSchema = realm->config().schema->find(property.object_type); - TableRef table = ObjectStore::table_for_object_type(realm->read_group(), linkObjectSchema->name); - if (row.is_null_link(property.table_column)) { + 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(realm, *linkObjectSchema, table->get(row.get_link(column))))); + return Accessor::from_object(ctx, std::move(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column))))); } case PropertyTypeArray: { - auto arrayObjectSchema = realm->config().schema->find(property.object_type); - return Accessor::from_list(ctx, std::move(List(realm, *arrayObjectSchema, static_cast(row.get_linklist(column))))); + 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))))); } } } @@ -229,7 +232,7 @@ namespace realm { // 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); - Property *primary_prop = object_schema.primary_key_property(); + 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); From ed193d8f5cb77f59333c8c9231bd7fcf14b896f5 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 26 Oct 2015 13:32:29 -0700 Subject: [PATCH 57/57] make list members private --- list.cpp | 12 ++++++------ list.hpp | 18 ++++++++++++------ object_accessor.hpp | 4 ++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/list.cpp b/list.cpp index d4b2bb57..89632a09 100644 --- a/list.cpp +++ b/list.cpp @@ -22,29 +22,29 @@ using namespace realm; size_t List::size() { - return link_view->size(); + return m_link_view->size(); } Row List::get(std::size_t row_ndx) { verify_valid_row(row_ndx); - return link_view->get(row_ndx); + return m_link_view->get(row_ndx); } void List::set(std::size_t row_ndx, std::size_t target_row_ndx) { verify_valid_row(row_ndx); - link_view->set(row_ndx, target_row_ndx); + m_link_view->set(row_ndx, target_row_ndx); } void List::verify_valid_row(std::size_t row_ndx) { - size_t size = link_view->size(); + size_t size = m_link_view->size(); if (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 (!link_view->is_attached()) { + if (!m_link_view->is_attached()) { throw std::runtime_error("Tableview is not attached"); } - link_view->sync_if_needed(); + m_link_view->sync_if_needed(); } diff --git a/list.hpp b/list.hpp index a1fa1dde..9165d194 100644 --- a/list.hpp +++ b/list.hpp @@ -23,18 +23,24 @@ #import namespace realm { - struct List { - List(SharedRealm &r, ObjectSchema &s, LinkViewRef l) : realm(r), object_schema(s), link_view(l) {} - // FIXME - all should be const - SharedRealm realm; - ObjectSchema &object_schema; - LinkViewRef link_view; + 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; } + LinkViewRef link_view() { return m_link_view; } size_t size(); Row get(std::size_t row_ndx); void set(std::size_t row_ndx, std::size_t target_row_ndx); + void verify_valid_row(std::size_t row_ndx); void verify_attached(); + + private: + SharedRealm m_realm; + LinkViewRef m_link_view; }; } diff --git a/object_accessor.hpp b/object_accessor.hpp index 2ddf43d3..5636a2cb 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -27,7 +27,7 @@ namespace realm { class Object { public: - Object(SharedRealm &r, ObjectSchema &s, Row o) : m_realm(r), object_schema(s), m_row(o) {} + Object(SharedRealm r, const ObjectSchema &s, Row o) : m_realm(r), object_schema(s), m_row(o) {} // property getter/setter template @@ -85,7 +85,7 @@ namespace realm { // 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 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