diff --git a/CHANGELOG.md b/CHANGELOG.md index ebcc7300a4f..f780971da1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,27 @@ ---------------------------------------------- +# 14.10.4 Release notes + +### Enhancements +* None. + +### Fixed +* When a public name is defined on a property, calling `realm::Results::sort()` or `realm::Results::distinct()` with the internal name could throw an error like `Cannot sort on key path 'NAME': property 'PersonObject.NAME' does not exist`. ([realm/realm-js#6779](https://github.com/realm/realm-js/issues/6779), since v12.12.0) + +### Breaking changes +* None. + +### Compatibility +* Fileformat: Generates files with format v24. Reads and automatically upgrade from fileformat v10. If you want to upgrade from an earlier file format version you will have to use RealmCore v13.x.y or earlier. + +----------- + +### Internals +* Fix a thread sanitizer failure in the "unregister connection change listener during callback" test ([PR #7871](https://github.com/realm/realm-core/pull/7871)). + +---------------------------------------------- + # 14.10.3 Release notes ### Enhancements diff --git a/Package.swift b/Package.swift index 4d4c1ad1b23..8ba0259c22c 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription import Foundation -let versionStr = "14.10.3" +let versionStr = "14.10.4" let versionPieces = versionStr.split(separator: "-") let versionCompontents = versionPieces[0].split(separator: ".") let versionExtra = versionPieces.count > 1 ? versionPieces[1] : "" diff --git a/dependencies.yml b/dependencies.yml index 930a99026a1..d10b1e07d0f 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -1,7 +1,7 @@ PACKAGE_NAME: realm-core -VERSION: 14.10.3 +VERSION: 14.10.4 OPENSSL_VERSION: 3.2.0 ZLIB_VERSION: 1.2.13 # https://github.com/10gen/baas/commits -# 9d1b4d6 is 2024 May 8 -BAAS_VERSION: 9d1b4d628babadfb606ebcadb93b1e5cae3c9565 +# 2f308db is 2024 July 10 +BAAS_VERSION: 2f308db6f65333728a101d1fecbb792f9659a5ce diff --git a/src/realm/array.cpp b/src/realm/array.cpp index 7486c8092c9..b942413a8d6 100644 --- a/src/realm/array.cpp +++ b/src/realm/array.cpp @@ -1068,40 +1068,6 @@ bool QueryStateFindAll::match(size_t index) noexcept return (m_limit > m_match_count); } -void Array::typed_print(std::string prefix) const -{ - std::cout << "Generic Array " << header_to_string(get_header()) << " @ " << m_ref; - if (!is_attached()) { - std::cout << " Unattached"; - return; - } - if (size() == 0) { - std::cout << " Empty" << std::endl; - return; - } - std::cout << " size = " << size() << " {"; - if (has_refs()) { - std::cout << std::endl; - for (unsigned n = 0; n < size(); ++n) { - auto pref = prefix + " " + to_string(n) + ":\t"; - RefOrTagged rot = get_as_ref_or_tagged(n); - if (rot.is_ref() && rot.get_as_ref()) { - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - std::cout << pref; - a.typed_print(pref); - } - else if (rot.is_tagged()) { - std::cout << pref << rot.get_as_int() << std::endl; - } - } - std::cout << prefix << "}" << std::endl; - } - else { - std::cout << " Leaf of unknown type }" << std::endl; - } -} - ref_type ArrayPayload::typed_write(ref_type ref, _impl::ArrayWriterBase& out, Allocator& alloc) { Array arr(alloc); diff --git a/src/realm/array.hpp b/src/realm/array.hpp index 047349846f8..aee1168920b 100644 --- a/src/realm/array.hpp +++ b/src/realm/array.hpp @@ -507,8 +507,6 @@ class Array : public Node, public ArrayParent { /// log2. Possible results {0, 1, 2, 4, 8, 16, 32, 64} static uint8_t bit_width(int64_t value); - void typed_print(std::string prefix) const; - protected: friend class NodeTree; void copy_on_write(); diff --git a/src/realm/bplustree.cpp b/src/realm/bplustree.cpp index b07e68d0e2b..02ea994b303 100644 --- a/src/realm/bplustree.cpp +++ b/src/realm/bplustree.cpp @@ -867,37 +867,6 @@ ref_type BPlusTreeBase::typed_write(ref_type ref, _impl::ArrayWriterBase& out, A return written_node.write(out); } -void BPlusTreeBase::typed_print(std::string prefix, Allocator& alloc, ref_type root, ColumnType col_type) -{ - char* header = alloc.translate(root); - Array a(alloc); - a.init_from_ref(root); - if (NodeHeader::get_is_inner_bptree_node_from_header(header)) { - std::cout << "{" << std::endl; - REALM_ASSERT(a.has_refs()); - for (unsigned j = 0; j < a.size(); ++j) { - auto pref = prefix + " " + std::to_string(j) + ":\t"; - RefOrTagged rot = a.get_as_ref_or_tagged(j); - if (rot.is_ref() && rot.get_as_ref()) { - if (j == 0) { - std::cout << pref << "BPTree offsets as ArrayUnsigned as "; - Array a(alloc); - a.init_from_ref(rot.get_as_ref()); - a.typed_print(prefix); - } - else { - std::cout << pref << "Subtree beeing "; - BPlusTreeBase::typed_print(pref, alloc, rot.get_as_ref(), col_type); - } - } - } - } - else { - std::cout << "BPTree Leaf[" << col_type << "] as "; - a.typed_print(prefix); - } -} - size_t BPlusTreeBase::size_from_header(const char* header) { auto node_size = Array::get_size_from_header(header); diff --git a/src/realm/bplustree.hpp b/src/realm/bplustree.hpp index e5ef8cac8db..5eb92512993 100644 --- a/src/realm/bplustree.hpp +++ b/src/realm/bplustree.hpp @@ -230,8 +230,6 @@ class BPlusTreeBase { } static ref_type typed_write(ref_type, _impl::ArrayWriterBase&, Allocator&, TypedWriteFunc); - static void typed_print(std::string prefix, Allocator& alloc, ref_type root, ColumnType col_type); - protected: template diff --git a/src/realm/cluster.cpp b/src/realm/cluster.cpp index 65e7c8ef89c..c1207f0d54f 100644 --- a/src/realm/cluster.cpp +++ b/src/realm/cluster.cpp @@ -1700,76 +1700,4 @@ ref_type Cluster::typed_write(ref_type ref, _impl::ArrayWriterBase& out) const } return written_cluster.write(out); } - -void Cluster::typed_print(std::string prefix) const -{ - REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header())); - std::cout << "Cluster of size " << size() << " " << header_to_string(get_header()) << std::endl; - const auto table = get_owning_table(); - for (unsigned j = 0; j < size(); ++j) { - RefOrTagged rot = get_as_ref_or_tagged(j); - auto pref = prefix + " " + std::to_string(j) + ":\t"; - if (rot.is_ref() && rot.get_as_ref()) { - if (j == 0) { - std::cout << pref << "Keys as ArrayUnsigned as "; - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - a.typed_print(pref); - } - else { - auto col_key = table->m_leaf_ndx2colkey[j - 1]; - auto col_type = col_key.get_type(); - auto col_attr = col_key.get_attrs(); - std::string attr_string; - if (col_attr.test(col_attr_Dictionary)) - attr_string = "Dict:"; - if (col_attr.test(col_attr_List)) - attr_string = "List:"; - if (col_attr.test(col_attr_Set)) - attr_string = "Set:"; - if (col_attr.test(col_attr_Nullable)) - attr_string += "Null:"; - std::cout << pref << "Column[" << attr_string << col_type << "] as "; - // special cases for the types we want to compress - if (col_attr.test(col_attr_List) || col_attr.test(col_attr_Set)) { - // That is a single bplustree - // propagation of nullable missing here? - // handling of mixed missing here? - BPlusTreeBase::typed_print(pref, m_alloc, rot.get_as_ref(), col_type); - } - else if (col_attr.test(col_attr_Dictionary)) { - Array dict_top(m_alloc); - dict_top.init_from_ref(rot.get_as_ref()); - if (dict_top.size() == 0) { - std::cout << "{ empty }" << std::endl; - continue; - } - std::cout << "{" << std::endl; - auto ref0 = dict_top.get_as_ref(0); - if (ref0) { - auto p = pref + " 0:\t"; - std::cout << p; - BPlusTreeBase::typed_print(p, m_alloc, ref0, col_type); - } - if (dict_top.size() == 1) { - continue; // is this really possible? or should all dicts have both trees? - } - auto ref1 = dict_top.get_as_ref(1); - if (ref1) { - auto p = pref + " 1:\t"; - std::cout << p; - BPlusTreeBase::typed_print(p, m_alloc, dict_top.get_as_ref(1), col_type); - } - } - else { - // handle all other cases as generic arrays - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - a.typed_print(pref); - } - } - } - } -} - } // namespace realm diff --git a/src/realm/cluster.hpp b/src/realm/cluster.hpp index 477ef36b65c..ae0f166a68c 100644 --- a/src/realm/cluster.hpp +++ b/src/realm/cluster.hpp @@ -214,13 +214,6 @@ class ClusterNode : public Array { } virtual ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out) const = 0; - virtual void typed_print(std::string prefix) const - { - static_cast(get_owning_table()); - std::cout << "ClusterNode as "; - Array::typed_print(prefix); - } - protected: #if REALM_MAX_BPNODE_SIZE > 256 static constexpr int node_shift_factor = 8; @@ -328,7 +321,6 @@ class Cluster : public ClusterNode { void verify() const; void dump_objects(int64_t key_offset, std::string lead) const override; virtual ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out) const override; - virtual void typed_print(std::string prefix) const override; static void remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey col, const std::vector& keys, CascadeState& state); static void remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey col, diff --git a/src/realm/cluster_tree.cpp b/src/realm/cluster_tree.cpp index fd5e1991dad..6884cf9b8ca 100644 --- a/src/realm/cluster_tree.cpp +++ b/src/realm/cluster_tree.cpp @@ -165,43 +165,6 @@ class ClusterNodeInner : public ClusterNode { return written_node.write(out); } - virtual void typed_print(std::string prefix) const override - { - REALM_ASSERT(get_is_inner_bptree_node_from_header(get_header())); - REALM_ASSERT(has_refs()); - std::cout << "ClusterNodeInner " << header_to_string(get_header()) << std::endl; - for (unsigned j = 0; j < size(); ++j) { - RefOrTagged rot = get_as_ref_or_tagged(j); - auto pref = prefix + " " + std::to_string(j) + ":\t"; - if (rot.is_ref() && rot.get_as_ref()) { - if (j == 0) { - std::cout << pref << "Keys as ArrayUnsigned as "; - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - a.typed_print(pref); - } - else { - auto header = m_alloc.translate(rot.get_as_ref()); - MemRef m(header, rot.get_as_ref(), m_alloc); - if (get_is_inner_bptree_node_from_header(header)) { - ClusterNodeInner a(m_alloc, m_tree_top); - a.init(m); - std::cout << pref; - a.typed_print(pref); - } - else { - Cluster a(j, m_alloc, m_tree_top); - a.init(m); - std::cout << pref; - a.typed_print(pref); - } - } - } - // just ignore entries, which are not refs. - } - Array::typed_print(prefix); - } - private: static constexpr size_t s_key_ref_index = 0; static constexpr size_t s_sub_tree_depth_index = 1; diff --git a/src/realm/cluster_tree.hpp b/src/realm/cluster_tree.hpp index 44afb09c5a2..f0bcb9c48bc 100644 --- a/src/realm/cluster_tree.hpp +++ b/src/realm/cluster_tree.hpp @@ -195,17 +195,6 @@ class ClusterTree { return m_root->typed_write(ref, out); } - void typed_print(std::string prefix) const - { - if (m_root) { - std::cout << prefix << "ClusterTree as "; - m_root->typed_print(prefix); - } - else { - std::cout << "Emtpy ClusterTree" << std::endl; - } - } - protected: friend class Obj; friend class Cluster; diff --git a/src/realm/group.cpp b/src/realm/group.cpp index 70de9a71ae2..960e07d3d61 100644 --- a/src/realm/group.cpp +++ b/src/realm/group.cpp @@ -960,48 +960,6 @@ ref_type Group::typed_write_tables(_impl::ArrayWriterBase& out) const } return dest.write(out); } -void Group::table_typed_print(std::string prefix, ref_type ref) const -{ - REALM_ASSERT(m_top.get_as_ref(1) == ref); - Array a(m_alloc); - a.init_from_ref(ref); - REALM_ASSERT(a.has_refs()); - for (unsigned j = 0; j < a.size(); ++j) { - auto pref = prefix + " " + to_string(j) + ":\t"; - RefOrTagged rot = a.get_as_ref_or_tagged(j); - if (rot.is_tagged() || rot.get_as_ref() == 0) - continue; - auto table_accessor = do_get_table(j); - REALM_ASSERT(table_accessor); - table_accessor->typed_print(pref, rot.get_as_ref()); - } -} -void Group::typed_print(std::string prefix) const -{ - std::cout << "Group top array" << std::endl; - for (unsigned j = 0; j < m_top.size(); ++j) { - auto pref = prefix + " " + to_string(j) + ":\t"; - RefOrTagged rot = m_top.get_as_ref_or_tagged(j); - if (rot.is_ref() && rot.get_as_ref()) { - if (j == 1) { - // Tables - std::cout << pref << "All Tables" << std::endl; - table_typed_print(pref, rot.get_as_ref()); - } - else { - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - std::cout << pref; - a.typed_print(pref); - } - } - else { - std::cout << pref << rot.get_as_int() << std::endl; - } - } - std::cout << "}" << std::endl; -} - ref_type Group::DefaultTableWriter::write_names(_impl::OutputStream& out) { diff --git a/src/realm/group.hpp b/src/realm/group.hpp index 7204f26b258..9145d2b9c5a 100644 --- a/src/realm/group.hpp +++ b/src/realm/group.hpp @@ -516,8 +516,6 @@ class Group : public ArrayParent { } #endif ref_type typed_write_tables(_impl::ArrayWriterBase& out) const; - void table_typed_print(std::string prefix, ref_type ref) const; - void typed_print(std::string prefix) const; protected: static constexpr size_t s_table_name_ndx = 0; @@ -1129,10 +1127,6 @@ class Group::TableWriter { virtual ref_type write_names(_impl::OutputStream&) = 0; virtual ref_type write_tables(_impl::OutputStream&) = 0; virtual HistoryInfo write_history(_impl::OutputStream&) = 0; - void typed_print(std::string prefix) - { - m_group->typed_print(prefix); - } virtual ~TableWriter() noexcept {} diff --git a/src/realm/node.hpp b/src/realm/node.hpp index 57630f4812a..e00ea30691d 100644 --- a/src/realm/node.hpp +++ b/src/realm/node.hpp @@ -263,11 +263,6 @@ class Node : public NodeHeader { } } - void typed_print(int) const - { - std::cout << "Generic Node ERROR\n"; - } - protected: /// The total size in bytes (including the header) of a new empty /// array. Must be a multiple of 8 (i.e., 64-bit aligned). diff --git a/src/realm/object-store/results.cpp b/src/realm/object-store/results.cpp index e9584d2b410..8d959ae05a7 100644 --- a/src/realm/object-store/results.cpp +++ b/src/realm/object-store/results.cpp @@ -871,6 +871,9 @@ static std::vector parse_keypath(StringData keypath, Schema c begin = sep + (sep != end); auto prop = object_schema->property_for_public_name(key); + if (!prop) { + prop = object_schema->property_for_name(key); + } check(prop, "property '%1.%2' does not exist", object_schema->name, key); if (is_dictionary(prop->type)) { check(index.length(), "missing dictionary key"); diff --git a/src/realm/spec.hpp b/src/realm/spec.hpp index c539dee7b93..188ea05f20d 100644 --- a/src/realm/spec.hpp +++ b/src/realm/spec.hpp @@ -78,11 +78,6 @@ class Spec { void set_ndx_in_parent(size_t) noexcept; void verify() const; - void typed_print(std::string prefix) const - { - std::cout << prefix << "Spec as "; - m_top.typed_print(prefix); - } private: // Underlying array structure. diff --git a/src/realm/table.cpp b/src/realm/table.cpp index b279d2a5205..acbb234fb0b 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -3488,32 +3487,6 @@ ref_type Table::typed_write(ref_type ref, _impl::ArrayWriterBase& out) const return dest.write(out); } -void Table::typed_print(std::string prefix, ref_type ref) const -{ - REALM_ASSERT(ref == m_top.get_mem().get_ref()); - std::cout << prefix << "Table with key = " << m_key << " " << NodeHeader::header_to_string(m_top.get_header()) - << " {" << std::endl; - for (unsigned j = 0; j < m_top.size(); ++j) { - auto pref = prefix + " " + to_string(j) + ":\t"; - auto rot = m_top.get_as_ref_or_tagged(j); - if (rot.is_ref() && rot.get_as_ref()) { - if (j == 0) { - m_spec.typed_print(pref); - } - else if (j == 2) { - m_clusters.typed_print(pref); - } - else { - Array a(m_alloc); - a.init_from_ref(rot.get_as_ref()); - std::cout << pref; - a.typed_print(pref); - } - } - } - std::cout << prefix << "}" << std::endl; -} - StringInterner* Table::get_string_interner(ColKey::Idx idx) const { REALM_ASSERT(idx.val < m_string_interners.size()); diff --git a/src/realm/table.hpp b/src/realm/table.hpp index fd6e72c6bde..d2aaea6038a 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -538,7 +538,6 @@ class Table { ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out, bool deep, bool only_modified, bool compress) const; - void typed_print(std::string prefix, ref_type ref) const; private: template diff --git a/test/object-store/results.cpp b/test/object-store/results.cpp index f76788ace5b..03d684ef1e7 100644 --- a/test/object-store/results.cpp +++ b/test/object-store/results.cpp @@ -4983,7 +4983,7 @@ TEST_CASE("results: public name declared", "[results]") { realm->commit_transaction(); Results r(realm, table); - SECTION("sorted") { + SECTION("sorted by public_name") { auto sorted = r.sort({{"public_value", true}}); REQUIRE(sorted.limit(0).size() == 0); REQUIRE_ORDER(sorted.limit(1), 2); @@ -4992,7 +4992,16 @@ TEST_CASE("results: public name declared", "[results]") { REQUIRE_ORDER(sorted.limit(100), 2, 6, 3, 7, 0, 4, 1, 5); } - SECTION("distinct") { + SECTION("sorted by name") { + auto sorted = r.sort({{"value", true}}); + REQUIRE(sorted.limit(0).size() == 0); + REQUIRE_ORDER(sorted.limit(1), 2); + REQUIRE_ORDER(sorted.limit(2), 2, 6); + REQUIRE_ORDER(sorted.limit(8), 2, 6, 3, 7, 0, 4, 1, 5); + REQUIRE_ORDER(sorted.limit(100), 2, 6, 3, 7, 0, 4, 1, 5); + } + + SECTION("distinct by public_name") { auto sorted = r.distinct({"public_value"}); REQUIRE(sorted.limit(0).size() == 0); REQUIRE_ORDER(sorted.limit(1), 0); @@ -5005,6 +5014,20 @@ TEST_CASE("results: public name declared", "[results]") { REQUIRE_ORDER(sorted.limit(2), 2, 3); REQUIRE_ORDER(sorted.limit(8), 2, 3, 0, 1); } + + SECTION("distinct by name") { + auto sorted = r.distinct({"value"}); + REQUIRE(sorted.limit(0).size() == 0); + REQUIRE_ORDER(sorted.limit(1), 0); + REQUIRE_ORDER(sorted.limit(2), 0, 1); + REQUIRE_ORDER(sorted.limit(8), 0, 1, 2, 3); + + sorted = r.sort({{"value", true}}).distinct({"value"}); + REQUIRE(sorted.limit(0).size() == 0); + REQUIRE_ORDER(sorted.limit(1), 2); + REQUIRE_ORDER(sorted.limit(2), 2, 3); + REQUIRE_ORDER(sorted.limit(8), 2, 3, 0, 1); + } } TEST_CASE("notifications: objects with PK recreated", "[results]") { diff --git a/test/object-store/sync/session/connection_change_notifications.cpp b/test/object-store/sync/session/connection_change_notifications.cpp index d9c52546acf..8a9fd2591cd 100644 --- a/test/object-store/sync/session/connection_change_notifications.cpp +++ b/test/object-store/sync/session/connection_change_notifications.cpp @@ -69,7 +69,7 @@ TEST_CASE("sync: Connection state changes", "[sync][session][connection change]" auto token1 = session->register_connection_change_callback( [&](SyncSession::ConnectionState, SyncSession::ConnectionState) { - listener1_call_cnt = listener1_call_cnt + 1; + ++listener1_call_cnt; }); session->unregister_connection_change_callback(token1); // One call may have been in progress when unregistered @@ -87,16 +87,18 @@ TEST_CASE("sync: Connection state changes", "[sync][session][connection change]" } SECTION("unregister connection change listener during callback") { - uint64_t token1; - std::atomic listener1_call_cnt(0); - std::atomic listener2_called(false); + int listener1_call_cnt = 0; auto session = sync_session( user, "/connection-state-changes-3", [](auto, auto) {}, SyncSessionStopPolicy::AfterChangesUploaded); - token1 = session->register_connection_change_callback( + std::mutex mutex; + std::unique_lock lock(mutex); + uint64_t token1 = session->register_connection_change_callback( [&](SyncSession::ConnectionState, SyncSession::ConnectionState) { - listener1_call_cnt = listener1_call_cnt + 1; + std::lock_guard lock(mutex); + ++listener1_call_cnt; session->unregister_connection_change_callback(token1); }); + lock.unlock(); EventLoop::main().run_until([&] { return sessions_are_active(*session); @@ -105,6 +107,7 @@ TEST_CASE("sync: Connection state changes", "[sync][session][connection change]" return sessions_are_connected(*session); }); + bool listener2_called = false; session->register_connection_change_callback([&](SyncSession::ConnectionState, SyncSession::ConnectionState) { listener2_called = true; });