diff --git a/src/realm/array.hpp b/src/realm/array.hpp index 35c7f694592..dd3be12d289 100644 --- a/src/realm/array.hpp +++ b/src/realm/array.hpp @@ -93,8 +93,6 @@ class Array : public Node, public ArrayParent { { } - ~Array() noexcept override {} - /// Create a new integer array of the specified type and size, and filled /// with the specified value, and attach this accessor to it. This does not /// modify the parent reference information of this accessor. diff --git a/src/realm/cluster.cpp b/src/realm/cluster.cpp index af9360fa55e..85028c91237 100644 --- a/src/realm/cluster.cpp +++ b/src/realm/cluster.cpp @@ -531,8 +531,6 @@ void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset) m_keys.truncate(ndx); } -Cluster::~Cluster() {} - ColKey Cluster::get_col_key(size_t ndx_in_parent) const { ColKey::Idx col_ndx{unsigned(ndx_in_parent - 1)}; // <- leaf_index here. Opaque. diff --git a/src/realm/cluster.hpp b/src/realm/cluster.hpp index d01e4efc54d..8c31af09048 100644 --- a/src/realm/cluster.hpp +++ b/src/realm/cluster.hpp @@ -113,9 +113,6 @@ class ClusterNode : public Array { { m_keys.set_parent(this, 0); } - virtual ~ClusterNode() - { - } void init_from_parent() { ref_type ref = get_ref_from_parent(); @@ -163,7 +160,7 @@ class ClusterNode : public Array { /// Locate object identified by 'key' and update 'state' accordingly void get(ObjKey key, State& state) const; /// Locate object identified by 'key' and update 'state' accordingly - /// Returns `false` if the object doesn't not exist. + /// Returns `false` if the object doesn't exist. virtual bool try_get(ObjKey key, State& state) const noexcept = 0; /// Locate object identified by 'ndx' and update 'state' accordingly virtual ObjKey get(size_t ndx, State& state) const = 0; @@ -220,7 +217,6 @@ class Cluster : public ClusterNode { : ClusterNode(offset, allocator, tree_top) { } - ~Cluster() override; static MemRef create_empty_cluster(Allocator& alloc); diff --git a/src/realm/cluster_tree.cpp b/src/realm/cluster_tree.cpp index ea9256f92ed..8142fe25ff2 100644 --- a/src/realm/cluster_tree.cpp +++ b/src/realm/cluster_tree.cpp @@ -53,7 +53,6 @@ namespace realm { class ClusterNodeInner : public ClusterNode { public: ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top); - ~ClusterNodeInner() override; void create(int sub_tree_depth); void init(MemRef mem) override; @@ -196,8 +195,6 @@ ClusterNodeInner::ClusterNodeInner(Allocator& allocator, const ClusterTree& tree { } -ClusterNodeInner::~ClusterNodeInner() {} - void ClusterNodeInner::create(int sub_tree_depth) { Array::create(Array::type_InnerBptreeNode, false, s_first_node_index); @@ -1455,4 +1452,23 @@ ClusterTree::Iterator::pointer ClusterTree::Iterator::operator->() const return &m_obj; } +ClusterTree::LeafCache::LeafCache(const Table& table) + : m_tree(table.m_clusters) + , m_leaf(0, m_tree.get_alloc(), m_tree) +{ +} + +Obj ClusterTree::LeafCache::get(ObjKey key) +{ + ClusterNode::State state; + auto k2 = ObjKey(key.value - m_leaf.m_offset); + if (m_leaf.is_attached() && m_leaf.try_get(k2, state)) { + return Obj(m_tree.get_table_ref(), m_leaf.get_mem(), key, state.index); + } + ClusterNode::IteratorState state2(m_leaf); + bool found = m_tree.get_leaf(key, state2); + REALM_ASSERT(found); + return Obj(m_tree.get_table_ref(), m_leaf.get_mem(), key, state2.m_current_index); +} + } // namespace realm diff --git a/src/realm/cluster_tree.hpp b/src/realm/cluster_tree.hpp index 46dc7c98a13..3f70856e781 100644 --- a/src/realm/cluster_tree.hpp +++ b/src/realm/cluster_tree.hpp @@ -30,6 +30,7 @@ class Cluster; class ClusterTree { public: class Iterator; + class LeafCache; using TraverseFunction = util::FunctionRef; using UpdateFunction = util::FunctionRef; using ColIterateFunction = util::FunctionRef; @@ -282,6 +283,16 @@ class ClusterTree::Iterator { ObjKey load_leaf(ObjKey key) const; size_t get_position(); }; + +class ClusterTree::LeafCache { +public: + LeafCache(const Table& table); + Obj get(ObjKey); + +private: + const ClusterTree& m_tree; + Cluster m_leaf; +}; } // namespace realm #endif /* REALM_CLUSTER_TREE_HPP */ diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index bf70e821162..acc98068d52 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -53,9 +53,9 @@ Obj::Obj(TableRef table, MemRef mem, ObjKey key, size_t row_ndx) , m_key(key) , m_mem(mem) , m_row_ndx(row_ndx) + , m_storage_version(_get_alloc().get_storage_version()) , m_valid(true) { - m_storage_version = get_alloc().get_storage_version(); } GlobalKey Obj::get_object_id() const diff --git a/src/realm/object-store/object.hpp b/src/realm/object-store/object.hpp index 325439c79b1..784fbf94ac8 100644 --- a/src/realm/object-store/object.hpp +++ b/src/realm/object-store/object.hpp @@ -97,7 +97,11 @@ class Object { { return *m_object_schema; } - Obj obj() const + Obj& obj() + { + return m_obj; + } + const Obj& obj() const { return m_obj; } diff --git a/src/realm/object-store/results.hpp b/src/realm/object-store/results.hpp index 1f1ef51d620..882a0a96297 100644 --- a/src/realm/object-store/results.hpp +++ b/src/realm/object-store/results.hpp @@ -294,8 +294,7 @@ class Results { * * @return A SectionedResults object using a user defined sectioning algorithm. */ - SectionedResults sectioned_results( - util::UniqueFunction& realm)>&& section_key_func); + SectionedResults sectioned_results(util::UniqueFunction&& section_key_func); enum class SectionedResultsOperator { FirstLetter // Section by the first letter of each string element. Note that col must be a string. }; diff --git a/src/realm/object-store/sectioned_results.cpp b/src/realm/object-store/sectioned_results.cpp index fa24983a449..0a3110b5ad6 100644 --- a/src/realm/object-store/sectioned_results.cpp +++ b/src/realm/object-store/sectioned_results.cpp @@ -29,15 +29,14 @@ static SectionedResults::SectionKeyFunc builtin_comparison(Results& results, Res case Results::SectionedResultsOperator::FirstLetter: if (results.get_type() == PropertyType::Object) { auto col_key = results.get_table()->get_column_key(prop_name); - return [col_key](Mixed value, const SharedRealm& realm) { - auto link = value.get_link(); - auto v = realm->read_group().get_object(link).get(col_key); + return [col_key](Results& results, size_t i) { + auto v = results.get(i).get(col_key); return v.size() > 0 ? v.prefix(1) : ""; }; } else { - return [](Mixed value, const SharedRealm&) { - auto v = value.get_string(); + return [](Results& results, size_t i) { + auto v = results.get(i); return v.size() > 0 ? v.prefix(1) : ""; }; } @@ -423,7 +422,7 @@ void SectionedResults::calculate_sections() m_row_to_index_path.resize(size); for (size_t i = 0; i < size; ++i) { - Mixed key = m_callback(m_results.get_any(i), m_results.get_realm()); + Mixed key = m_callback(m_results, i); // Disallow links as section keys. It would be uncommon to use them to begin with // and if the object acting as the key was deleted bad things would happen. if (key.is_type(type_Link, type_TypedLink)) { diff --git a/src/realm/object-store/sectioned_results.hpp b/src/realm/object-store/sectioned_results.hpp index bbd4c33cf05..9598a4109f3 100644 --- a/src/realm/object-store/sectioned_results.hpp +++ b/src/realm/object-store/sectioned_results.hpp @@ -105,7 +105,7 @@ class ResultsSection { class SectionedResults { public: SectionedResults() = default; - using SectionKeyFunc = util::UniqueFunction& realm)>; + using SectionKeyFunc = util::UniqueFunction; /** * Returns a `ResultsSection` which will be bound to a section key present at the given index in diff --git a/src/realm/query.cpp b/src/realm/query.cpp index 4222fc3fd26..151088985e0 100644 --- a/src/realm/query.cpp +++ b/src/realm/query.cpp @@ -1909,29 +1909,28 @@ Query Query::operator!() void Query::get_outside_versions(TableVersions& versions) const { - if (m_table) { - if (m_table_keys.empty()) { - // Store primary table info - m_table_keys.push_back(m_table.unchecked_ptr()->get_key()); + if (!m_table) { + return; + } + auto table = m_table.unchecked_ptr(); + if (m_table_keys.empty()) { + // Store primary table info + m_table_keys.push_back(table->get_key()); - if (ParentNode* root = root_node()) - root->get_link_dependencies(m_table_keys); - } - versions.emplace_back(m_table.unchecked_ptr()->get_key(), m_table.unchecked_ptr()->get_content_version()); - - if (Group* g = m_table.unchecked_ptr()->get_parent_group()) { - // update table versions for linked tables - first entry is primary table - skip it - auto end = m_table_keys.end(); - auto it = m_table_keys.begin() + 1; - while (it != end) { - versions.emplace_back(*it, g->get_table(*it)->get_content_version()); - ++it; - } - } - if (m_view) { - m_view->get_dependencies(versions); + if (ParentNode* root = root_node()) + root->get_link_dependencies(m_table_keys); + } + versions.emplace_back(table->get_key(), table->get_content_version()); + + if (Group* g = table->get_parent_group()) { + // update table versions for linked tables - first entry is primary table - skip it + for (auto it = m_table_keys.begin() + 1, end = m_table_keys.end(); it != end; ++it) { + versions.emplace_back(*it, g->get_table(*it).unchecked_ptr()->get_content_version()); } } + if (m_view) { + m_view->get_dependencies(versions); + } } TableVersions Query::sync_view_if_needed() const diff --git a/src/realm/sort_descriptor.cpp b/src/realm/sort_descriptor.cpp index a21313489fd..b47295c9983 100644 --- a/src/realm/sort_descriptor.cpp +++ b/src/realm/sort_descriptor.cpp @@ -50,14 +50,14 @@ void ColumnsDescriptor::collect_dependencies(const Table* table, std::vectorget_column_type(col) == type_Link) { - target_table = t->get_link_target(col); + target_table = t->get_link_target(col).unchecked_ptr(); } if (!target_table) return; table_keys.push_back(target_table->get_key()); - t = target_table.unchecked_ptr(); + t = target_table; } } } @@ -356,6 +356,7 @@ void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v) auto& col = m_columns[0]; ColKey ck = col.col_key; + ClusterTree::LeafCache cache(*col.table); for (size_t i = 0; i < v.size(); i++) { IndexPair& index = v[i]; ObjKey key = index.key_for_object; @@ -368,7 +369,7 @@ void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v) } } - index.cached_value = col.table->get_object(key).get_any(ck); + index.cached_value = cache.get(key).get_any(ck); } } @@ -535,6 +536,6 @@ void DescriptorOrdering::get_versions(const Group* group, TableVersions& version { for (auto table_key : m_dependencies) { REALM_ASSERT_DEBUG(group); - versions.emplace_back(table_key, group->get_table(table_key)->get_content_version()); + versions.emplace_back(table_key, group->get_table(table_key).unchecked_ptr()->get_content_version()); } } diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 9c5eff2639b..80c127ba37c 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -2503,80 +2503,39 @@ ObjKey Table::find_first_uuid(ColKey col_key, UUID value) const } template -TableView Table::find_all(ColKey col_key, T value) +TableView Table::find_all(ColKey col_key, T value) const { return where().equal(col_key, value).find_all(); } -TableView Table::find_all_int(ColKey col_key, int64_t value) -{ - return find_all(col_key, value); -} - TableView Table::find_all_int(ColKey col_key, int64_t value) const { - return const_cast(this)->find_all(col_key, value); -} - -TableView Table::find_all_bool(ColKey col_key, bool value) -{ - return find_all(col_key, value); + return find_all(col_key, value); } TableView Table::find_all_bool(ColKey col_key, bool value) const { - return const_cast(this)->find_all(col_key, value); -} - - -TableView Table::find_all_float(ColKey col_key, float value) -{ - return find_all(col_key, value); + return find_all(col_key, value); } TableView Table::find_all_float(ColKey col_key, float value) const { - return const_cast(this)->find_all(col_key, value); -} - -TableView Table::find_all_double(ColKey col_key, double value) -{ - return find_all(col_key, value); + return find_all(col_key, value); } TableView Table::find_all_double(ColKey col_key, double value) const { - return const_cast(this)->find_all(col_key, value); -} - -TableView Table::find_all_string(ColKey col_key, StringData value) -{ - return where().equal(col_key, value).find_all(); + return find_all(col_key, value); } TableView Table::find_all_string(ColKey col_key, StringData value) const { - return const_cast(this)->find_all_string(col_key, value); -} - -TableView Table::find_all_binary(ColKey, BinaryData) -{ - throw Exception(ErrorCodes::IllegalOperation, "Table::find_all_binary not supported"); -} - -TableView Table::find_all_binary(ColKey col_key, BinaryData value) const -{ - return const_cast(this)->find_all_binary(col_key, value); -} - -TableView Table::find_all_null(ColKey col_key) -{ - return where().equal(col_key, null{}).find_all(); + return where().equal(col_key, value).find_all(); } TableView Table::find_all_null(ColKey col_key) const { - return const_cast(this)->find_all_null(col_key); + return where().equal(col_key, null{}).find_all(); } TableView Table::find_all_fulltext(ColKey col_key, StringData terms) const @@ -2584,30 +2543,20 @@ TableView Table::find_all_fulltext(ColKey col_key, StringData terms) const return where().fulltext(col_key, terms).find_all(); } -TableView Table::get_sorted_view(ColKey col_key, bool ascending) +TableView Table::get_sorted_view(ColKey col_key, bool ascending) const { TableView tv = where().find_all(); tv.sort(col_key, ascending); return tv; } -TableView Table::get_sorted_view(ColKey col_key, bool ascending) const -{ - return const_cast(this)->get_sorted_view(col_key, ascending); -} - -TableView Table::get_sorted_view(SortDescriptor order) +TableView Table::get_sorted_view(SortDescriptor order) const { TableView tv = where().find_all(); tv.sort(std::move(order)); return tv; } -TableView Table::get_sorted_view(SortDescriptor order) const -{ - return const_cast(this)->get_sorted_view(std::move(order)); -} - util::Logger* Table::get_logger() const noexcept { return *m_repl ? (*m_repl)->get_logger() : nullptr; diff --git a/src/realm/table.hpp b/src/realm/table.hpp index a8dfc50d490..0aa1e0892f1 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -425,27 +425,16 @@ class Table { ObjKey find_first_uuid(ColKey col_key, UUID value) const; // TableView find_all_link(Key target_key); - TableView find_all_int(ColKey col_key, int64_t value); TableView find_all_int(ColKey col_key, int64_t value) const; - TableView find_all_bool(ColKey col_key, bool value); TableView find_all_bool(ColKey col_key, bool value) const; - TableView find_all_float(ColKey col_key, float value); TableView find_all_float(ColKey col_key, float value) const; - TableView find_all_double(ColKey col_key, double value); TableView find_all_double(ColKey col_key, double value) const; - TableView find_all_string(ColKey col_key, StringData value); TableView find_all_string(ColKey col_key, StringData value) const; - TableView find_all_binary(ColKey col_key, BinaryData value); - TableView find_all_binary(ColKey col_key, BinaryData value) const; - TableView find_all_null(ColKey col_key); TableView find_all_null(ColKey col_key) const; TableView find_all_fulltext(ColKey col_key, StringData value) const; - TableView get_sorted_view(ColKey col_key, bool ascending = true); TableView get_sorted_view(ColKey col_key, bool ascending = true) const; - - TableView get_sorted_view(SortDescriptor order); TableView get_sorted_view(SortDescriptor order) const; // Report the current content version. This is a 64-bit value which is bumped whenever @@ -515,7 +504,7 @@ class Table { private: template - TableView find_all(ColKey col_key, T value); + TableView find_all(ColKey col_key, T value) const; void build_column_mapping(); ColKey generate_col_key(ColumnType ct, ColumnAttrMask attrs); void convert_column(ColKey from, ColKey to, bool throw_on_null); diff --git a/src/realm/table_view.cpp b/src/realm/table_view.cpp index 0c5e4c7cff6..3a1e43309ae 100644 --- a/src/realm/table_view.cpp +++ b/src/realm/table_view.cpp @@ -438,9 +438,15 @@ void TableView::sort(ColKey column, bool ascending) // Sort according to multiple columns, user specified order on each column void TableView::sort(SortDescriptor order) { + REALM_ASSERT(is_in_sync()); m_descriptor_ordering.append_sort(std::move(order), SortDescriptor::MergeMode::prepend); m_descriptor_ordering.collect_dependencies(m_table.unchecked_ptr()); + // Adding the new sort descriptor marked us as no longer in sync, but do_sort() + // will bring us back into sync + m_last_seen_versions.clear(); + get_dependencies(m_last_seen_versions); + do_sort(m_descriptor_ordering); } @@ -455,6 +461,7 @@ void TableView::do_sync() // - Table::get_backlink_view() // Here we sync with the respective source. m_last_seen_versions.clear(); + get_dependencies(m_last_seen_versions); if (m_collection_source) { m_key_values.clear(); @@ -493,8 +500,6 @@ void TableView::do_sync() } do_sort(m_descriptor_ordering); - - get_dependencies(m_last_seen_versions); } void TableView::do_sort(const DescriptorOrdering& ordering) @@ -504,22 +509,15 @@ void TableView::do_sort(const DescriptorOrdering& ordering) size_t sz = size(); if (sz == 0) return; + REALM_ASSERT(is_in_sync()); // Gather the current rows into a container we can use std algorithms on - size_t detached_ref_count = 0; BaseDescriptor::IndexPairs index_pairs; index_pairs.reserve(sz); - // always put any detached refs at the end of the sort - // FIXME: reconsider if this is the right thing to do - // FIXME: consider specialized implementations in derived classes - // (handling detached refs is not required in linkviews) for (size_t t = 0; t < sz; t++) { ObjKey key = get_key(t); - if (m_table->is_valid(key)) { - index_pairs.emplace_back(key, t); - } - else - ++detached_ref_count; + REALM_ASSERT_DEBUG(m_table->is_valid(key)); + index_pairs.emplace_back(key, t); } const int num_descriptors = int(ordering.size()); @@ -541,8 +539,6 @@ void TableView::do_sort(const DescriptorOrdering& ordering) for (auto& pair : index_pairs) { m_key_values.add(pair.key_for_object); } - for (size_t t = 0; t < detached_ref_count; ++t) - m_key_values.add(null_key); } bool TableView::is_in_table_order() const diff --git a/src/realm/table_view.hpp b/src/realm/table_view.hpp index dc393f0a658..256d1a02d37 100644 --- a/src/realm/table_view.hpp +++ b/src/realm/table_view.hpp @@ -321,7 +321,12 @@ class TableView : public ObjList { bool has_changed() const { - return m_last_seen_versions != get_dependency_versions(); + // Once evaluated, a frozen TableView will never change again + if (m_table->is_frozen()) + return m_last_seen_versions.empty(); + m_current_versions.clear(); + get_dependencies(m_current_versions); + return m_last_seen_versions != m_current_versions; } // Sort m_key_values according to one column @@ -414,6 +419,7 @@ class TableView : public ObjList { }; mutable TableVersions m_last_seen_versions; + mutable TableVersions m_current_versions; KeyValues m_key_values; private: diff --git a/test/benchmark-common-tasks/main.cpp b/test/benchmark-common-tasks/main.cpp index 5fd45eec3ba..1b0edfb1a7e 100644 --- a/test/benchmark-common-tasks/main.cpp +++ b/test/benchmark-common-tasks/main.cpp @@ -197,13 +197,10 @@ struct BenchmarkWithStrings : BenchmarkWithStringsTable { BenchmarkWithStringsTable::before_all(group); WriteTransaction tr(group); TableRef t = tr.get_table(name()); + t->add_column(type_Int, "filter"); for (size_t i = 0; i < BASE_SIZE; ++i) { - std::stringstream ss; - ss << rand(); - auto s = ss.str(); - Obj obj = t->create_object(); - obj.set(m_col, s); + Obj obj = t->create_object().set_all(util::to_string(rand()), int64_t(i % 256)); m_keys.push_back(obj.get_key()); } tr.commit(); @@ -219,9 +216,7 @@ struct BenchmarkWithStringsFewDup : BenchmarkWithStringsTable { Random r; for (size_t i = 0; i < BASE_SIZE; ++i) { - std::stringstream ss; - ss << r.draw_int(0, BASE_SIZE / 2); - auto s = ss.str(); + auto s = util::to_string(r.draw_int(0, BASE_SIZE / 2)); Obj obj = t->create_object(); obj.set(m_col, s); m_keys.push_back(obj.get_key()); @@ -239,9 +234,7 @@ struct BenchmarkWithStringsManyDup : BenchmarkWithStringsTable { TableRef t = tr.get_table(name()); Random r; for (size_t i = 0; i < BASE_SIZE; ++i) { - std::stringstream ss; - ss << r.draw_int(0, 100); - auto s = ss.str(); + auto s = util::to_string(r.draw_int(0, 100)); Obj obj = t->create_object(); obj.set(m_col, s); m_keys.push_back(obj.get_key()); @@ -1259,10 +1252,10 @@ struct BenchmarkQueryChainedOrStrings : BenchmarkWithStringsTableForIn { } }; -struct BenchmarkSort : BenchmarkWithStrings { +struct BenchmarkSortStringDense : BenchmarkWithStrings { const char* name() const { - return "Sort"; + return "SortStringDense"; } void operator()(DBRef) @@ -1272,6 +1265,21 @@ struct BenchmarkSort : BenchmarkWithStrings { } }; +struct BenchmarkSortStringSparse : BenchmarkWithStrings { + const char* name() const + { + return "SortStringSparse"; + } + + void operator()(DBRef) + { + // Filter the view so that we're sorting a single row per leaf + ConstTableRef table = m_table; + TableView view = table->where().equal(table->get_column_key("filter"), 0).find_all(); + view.sort(m_col); + } +}; + struct BenchmarkEmptyCommit : Benchmark { const char* name() const { @@ -2263,7 +2271,8 @@ int benchmark_common_tasks_main() BENCH(IterateTableByIterator); BENCH(IterateTableByIteratorIndex); - BENCH(BenchmarkSort); + BENCH(BenchmarkSortStringDense); + BENCH(BenchmarkSortStringSparse); BENCH(BenchmarkSortInt); BENCH(BenchmarkSortIntList); BENCH(BenchmarkSortIntDictionary); diff --git a/test/object-store/benchmarks/results.cpp b/test/object-store/benchmarks/results.cpp index d40398133e8..9cf9b8342d2 100644 --- a/test/object-store/benchmarks/results.cpp +++ b/test/object-store/benchmarks/results.cpp @@ -437,14 +437,28 @@ TEST_CASE("Benchmark sectioned results", "[benchmark]") { realm->commit_transaction(); size_t section_count = GENERATE(1, 10, 1000, 10000); - auto key_fn = [&](Mixed value, const std::shared_ptr&) -> Mixed { - return table->get_object(value.get_link().get_obj_key()).get(col) % int64_t(section_count); + auto key_fn = [&](Results& results, size_t i) -> Mixed { + return results.get(i).get(col) % int64_t(section_count); }; - BENCHMARK("create and get section count") { - auto size = Results(realm, table).sectioned_results(key_fn).size(); - REQUIRE(size == section_count); - }; + SECTION("create and get section count") { + BENCHMARK("table") { + auto size = Results(realm, table).sectioned_results(key_fn).size(); + REQUIRE(size == section_count); + }; + BENCHMARK("tableview") { + auto size = Results(realm, table).filter(table->where()).sectioned_results(key_fn).size(); + REQUIRE(size == section_count); + }; + BENCHMARK("sorted tableview") { + auto size = Results(realm, table) + .sort({{"value", false}}) + .filter(table->where()) + .sectioned_results(key_fn) + .size(); + REQUIRE(size == section_count); + }; + } BENCHMARK_ADVANCED("iterate directly")(Catch::Benchmark::Chronometer meter) { diff --git a/test/object-store/results.cpp b/test/object-store/results.cpp index cfe6b8ef532..a0f454ac18f 100644 --- a/test/object-store/results.cpp +++ b/test/object-store/results.cpp @@ -3638,6 +3638,29 @@ TEST_CASE("results: snapshots") { REQUIRE_FALSE(snapshot.first()->is_valid()); REQUIRE_FALSE(snapshot.last()->is_valid()); } + + SECTION("sorting a snapshot results in live Results") { + auto table = r->read_group().get_table("class_object"); + write([=] { + table->create_object(); + }); + + auto snapshot = Results(r, table).snapshot(); + auto sorted = snapshot.sort({{"value", true}}); + REQUIRE(snapshot.size() == 1); + REQUIRE(sorted.size() == 1); + + write([=] { + table->clear(); + }); + + REQUIRE(snapshot.size() == 1); + REQUIRE(sorted.size() == 0); + + // Create a second sorted Results derived from the now-stale snapshot + sorted = snapshot.sort({{"value", true}}); + REQUIRE(sorted.size() == 0); + } } TEST_CASE("results: distinct") { diff --git a/test/object-store/sectioned_results.cpp b/test/object-store/sectioned_results.cpp index d625a43babc..1b3e67a8e76 100644 --- a/test/object-store/sectioned_results.cpp +++ b/test/object-store/sectioned_results.cpp @@ -495,10 +495,10 @@ TEST_CASE("sectioned results", "[sectioned_results]") { Results results(r, table); auto sorted = results.sort({{"name_col", true}}); int algo_run_count = 0; - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, const SharedRealm& realm) { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) { algo_run_count++; - auto obj = Object(realm, value.get_link()); - auto v = obj.get_column_value("name_col"); + auto obj = results.get(i); + auto v = obj.get("name_col"); return v.prefix(1); }); REQUIRE(algo_run_count == 0); @@ -570,11 +570,9 @@ TEST_CASE("sectioned results", "[sectioned_results]") { } SECTION("reset section callback") { - sectioned_results.reset_section_callback([&](Mixed value, SharedRealm realm) { + sectioned_results.reset_section_callback([&](Results& results, size_t i) { algo_run_count++; - auto obj = Object(realm, value.get_link()); - auto v = obj.get_column_value("name_col"); - return v.prefix(2); + return results.get(i).get("name_col").prefix(2); }); REQUIRE(algo_run_count == 0); REQUIRE(sectioned_results.size() == 3); @@ -591,10 +589,9 @@ TEST_CASE("sectioned results", "[sectioned_results]") { REQUIRE(algo_run_count == 5); algo_run_count = 0; - sectioned_results.reset_section_callback([&](Mixed value, SharedRealm realm) { + sectioned_results.reset_section_callback([&](Results& results, size_t i) { algo_run_count++; - auto obj = Object(realm, value.get_link()); - return obj.get_column_value("name_col").contains("o"); + return results.get(i).get("name_col").contains("o"); }); REQUIRE(algo_run_count == 0); REQUIRE(sectioned_results.size() == 2); @@ -606,8 +603,8 @@ TEST_CASE("sectioned results", "[sectioned_results]") { SECTION("correctly asserts key") { // Should throw on Object being a section key. - auto sr = sorted.sectioned_results([](Mixed value, SharedRealm) { - return value.get_link(); + auto sr = sorted.sectioned_results([](Results& r, size_t i) { + return r.get(i); }); REQUIRE_EXCEPTION(sr.size(), InvalidArgument, "Links are not supported as section keys."); // Trigger calculation @@ -623,9 +620,9 @@ TEST_CASE("sectioned results", "[sectioned_results]") { r->commit_transaction(); // Should throw on `type_TypedLink` being a section key. - sr = sorted.sectioned_results([&](Mixed value, SharedRealm realm) { - auto obj = Object(realm, value.get_link()); - return Mixed(obj.obj().get(col_typed_link)); + sr = sorted.sectioned_results([&](Results& results, size_t i) { + auto obj = results.get(i); + return Mixed(obj.get(col_typed_link)); }); REQUIRE_EXCEPTION(sr.size(), InvalidArgument, "Links are not supported as section keys."); // Trigger calculation @@ -1053,10 +1050,10 @@ TEST_CASE("sectioned results", "[sectioned_results]") { // Descending sorted = results.sort({{"name_col", false}}); - sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm realm) { + sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) { algo_run_count++; - auto obj = Object(realm, value.get_link()); - auto v = obj.get_column_value("name_col"); + auto obj = results.get(i); + auto v = obj.get("name_col"); return v.prefix(1); }); @@ -1108,10 +1105,9 @@ TEST_CASE("sectioned results", "[sectioned_results]") { auto str_list = o1.get_list(array_string_col); r->commit_transaction(); List lst(r, o1, array_string_col); - sectioned_results = lst.sort({{"self", true}}).sectioned_results([&algo_run_count](Mixed value, SharedRealm) { + sectioned_results = lst.sort({{"self", true}}).sectioned_results([&algo_run_count](Results& r, size_t i) { algo_run_count++; - auto v = value.get_string(); - return v.prefix(1); + return r.get(i).prefix(1); }); SectionedResultsChangeSet changes; @@ -1154,12 +1150,10 @@ TEST_CASE("sectioned results", "[sectioned_results]") { REQUIRE_INDICES(changes.deletions[1], 0, 1); // Descending - sectioned_results = - lst.sort({{"self", false}}).sectioned_results([&algo_run_count](Mixed value, SharedRealm) { - algo_run_count++; - auto v = value.get_string(); - return v.prefix(1); - }); + sectioned_results = lst.sort({{"self", false}}).sectioned_results([&algo_run_count](Results& r, size_t i) { + algo_run_count++; + return r.get(i).prefix(1); + }); token = sectioned_results.add_notification_callback([&](SectionedResultsChangeSet c) { changes = c; @@ -1543,9 +1537,9 @@ TEST_CASE("sectioned results link notification bug", "[sectioned_results]") { Results results(r, transaction_table); auto sorted = results.sort({{"date", false}}); - auto sectioned_results = sorted.sectioned_results([](Mixed value, SharedRealm realm) { - auto obj = Object(realm, value.get_link()); - auto ts = obj.get_column_value("date"); + auto sectioned_results = sorted.sectioned_results([](Results& results, size_t i) { + auto obj = results.get(i); + auto ts = obj.get("date"); auto tp = ts.get_time_point(); auto day = std::chrono::floor(tp); return Timestamp{day}; @@ -1626,9 +1620,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("primitives section correctly with sort ascending") { auto sorted = results.sort({{"self", true}}); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); REQUIRE(sectioned_results.size() == TestType::expected_size); auto size = sectioned_results.size(); @@ -1652,9 +1646,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("primitives section correctly with sort decending") { auto sorted = results.sort({{"self", false}}); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); std::reverse(exp_values_sorted.begin(), exp_values_sorted.end()); std::reverse(exp_keys.begin(), exp_keys.end()); @@ -1677,9 +1671,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("notifications") { auto sorted = results.sort({{"self", true}}); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); SectionedResultsChangeSet changes; @@ -1702,9 +1696,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("notifications on section") { auto sorted = results.sort({{"self", true}}); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); auto section1 = sectioned_results[0]; auto section2 = sectioned_results[1]; @@ -1761,9 +1755,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("frozen primitive") { auto sorted = results.sort({{"self", true}}); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); auto frozen_realm = r->freeze(); auto frozen_sr = sectioned_results.freeze(frozen_realm); @@ -1787,9 +1781,9 @@ TEMPLATE_TEST_CASE("sectioned results primitive types", "[sectioned_results]", c SECTION("frozen results primitive") { auto frozen_realm = r->freeze(); auto sorted = results.sort({{"self", true}}).freeze(frozen_realm); - auto sectioned_results = sorted.sectioned_results([&algo_run_count](Mixed value, SharedRealm) -> Mixed { + auto sectioned_results = sorted.sectioned_results([&algo_run_count](Results& results, size_t i) -> Mixed { algo_run_count++; - return TestType::comparison_value(value); + return TestType::comparison_value(results.get_any(i)); }); auto size = sectioned_results.size(); REQUIRE(size == TestType::expected_size);