diff --git a/src/realm/collection.cpp b/src/realm/collection.cpp index 73415f8a91d..636c89f6838 100644 --- a/src/realm/collection.cpp +++ b/src/realm/collection.cpp @@ -96,8 +96,9 @@ std::pair CollectionBase::get_open_close_strings(size_ { std::string open_str; std::string close_str; - auto ck = get_col_key(); + auto collection_type = get_collection_type(); Table* target_table = get_target_table().unchecked_ptr(); + auto ck = get_col_key(); auto type = ck.get_type(); if (type == col_type_LinkList) type = col_type_Link; @@ -107,29 +108,31 @@ std::pair CollectionBase::get_open_close_strings(size_ if (output_mode == output_mode_xjson_plus) { open_str = std::string("{ ") + (is_embedded ? "\"$embedded" : "\"$link"); - open_str += collection_type_name(ck, true); + open_str += collection_type_name(collection_type, true); open_str += "\": "; close_str += " }"; } if ((link_depth_reached && output_mode != output_mode_xjson) || output_mode == output_mode_xjson_plus) { open_str += "{ \"table\": \"" + std::string(target_table->get_name()) + "\", "; - open_str += ((is_embedded || ck.is_dictionary()) ? "\"value" : "\"key"); - if (ck.is_collection()) - open_str += "s"; + open_str += ((is_embedded || collection_type == CollectionType::Dictionary) ? "\"values" : "\"keys"); open_str += "\": "; close_str += "}"; } } else { if (output_mode == output_mode_xjson_plus) { - if (ck.is_set()) { - open_str = "{ \"$set\": "; - close_str = " }"; - } - else if (ck.is_dictionary()) { - open_str = "{ \"$dictionary\": "; - close_str = " }"; + switch (collection_type) { + case CollectionType::List: + break; + case CollectionType::Set: + open_str = "{ \"$set\": "; + close_str = " }"; + break; + case CollectionType::Dictionary: + open_str = "{ \"$dictionary\": "; + close_str = " }"; + break; } } } diff --git a/src/realm/collection.hpp b/src/realm/collection.hpp index 78a977bb951..04e52c70eac 100644 --- a/src/realm/collection.hpp +++ b/src/realm/collection.hpp @@ -90,6 +90,9 @@ class CollectionBase : public Collection { /// the internal state of the accessor if it has changed. virtual bool has_changed() const noexcept = 0; + /// Get collection type (set, list, dictionary) + virtual CollectionType get_collection_type() const noexcept = 0; + /// Returns true if the accessor is in the attached state. By default, this /// checks if the owning object is still valid. virtual bool is_attached() const @@ -154,21 +157,23 @@ class CollectionBase : public Collection { std::pair get_open_close_strings(size_t link_depth, JSONOutputMode output_mode) const; }; -inline std::string_view collection_type_name(ColKey col, bool uppercase = false) +inline std::string_view collection_type_name(CollectionType col_type, bool uppercase = false) { - if (col.is_list()) - return uppercase ? "List" : "list"; - if (col.is_set()) - return uppercase ? "Set" : "set"; - if (col.is_dictionary()) - return uppercase ? "Dictionary" : "dictionary"; + switch (col_type) { + case CollectionType::List: + return uppercase ? "List" : "list"; + case CollectionType::Set: + return uppercase ? "Set" : "set"; + case CollectionType::Dictionary: + return uppercase ? "Dictionary" : "dictionary"; + } return ""; } inline void CollectionBase::validate_index(const char* msg, size_t index, size_t size) const { if (index >= size) { - throw OutOfBounds(util::format("%1 on %2 '%3.%4'", msg, collection_type_name(get_col_key()), + throw OutOfBounds(util::format("%1 on %2 '%3.%4'", msg, collection_type_name(get_collection_type()), get_table()->get_class_name(), get_property_name()), index, size); } @@ -382,6 +387,11 @@ class CollectionBaseImpl : public Interface, protected ArrayParent { return false; } + CollectionType get_collection_type() const noexcept override + { + return Interface::s_collection_type; + } + void set_owner(const Obj& obj, ColKey ck) override { m_obj_mem = obj; @@ -471,7 +481,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent { ref_type get_collection_ref() const noexcept { try { - return m_parent->get_collection_ref(m_index); + return m_parent->get_collection_ref(m_index, Interface::s_collection_type); } catch (const KeyNotFound&) { return ref_type(0); @@ -480,7 +490,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent { void set_collection_ref(ref_type ref) { - m_parent->set_collection_ref(m_index, ref); + m_parent->set_collection_ref(m_index, ref, Interface::s_collection_type); } UpdateStatus get_update_status() const noexcept diff --git a/src/realm/collection_list.cpp b/src/realm/collection_list.cpp index 1e85974dbf8..1e0f94af623 100644 --- a/src/realm/collection_list.cpp +++ b/src/realm/collection_list.cpp @@ -39,7 +39,7 @@ CollectionList::CollectionList(std::shared_ptr parent, ColKey , m_col_key(col_key) , m_top(*m_alloc) , m_refs(*m_alloc) - , m_key_type(coll_type == CollectionType::List ? type_Int : type_String) + , m_coll_type(coll_type) { m_top.set_parent(this, 0); m_refs.set_parent(&m_top, 1); @@ -51,7 +51,7 @@ CollectionList::CollectionList(CollectionParent* obj, ColKey col_key) , m_col_key(col_key) , m_top(*m_alloc) , m_refs(*m_alloc) - , m_key_type(get_table()->get_nested_column_type(col_key, 0) == CollectionType::List ? type_Int : type_String) + , m_coll_type(get_table()->get_nested_column_type(col_key, 0)) { m_top.set_parent(this, 0); m_refs.set_parent(&m_top, 1); @@ -61,14 +61,14 @@ CollectionList::~CollectionList() {} bool CollectionList::init_from_parent(bool allow_create) const { - auto ref = m_parent->get_collection_ref(m_index); + auto ref = m_parent->get_collection_ref(m_index, m_coll_type); if ((ref || allow_create) && !m_keys) { - switch (m_key_type) { - case type_String: { + switch (m_coll_type) { + case CollectionType::Dictionary: { m_keys.reset(new BPlusTree(*m_alloc)); break; } - case type_Int: { + case CollectionType::List: { m_keys.reset(new BPlusTree(*m_alloc)); break; } @@ -170,19 +170,19 @@ bool CollectionList::update_if_needed() const ref_type CollectionList::get_child_ref(size_t) const noexcept { - return m_parent->get_collection_ref(m_col_key); + return m_parent->get_collection_ref(m_col_key, m_coll_type); } void CollectionList::update_child_ref(size_t, ref_type ref) { - m_parent->set_collection_ref(m_index, ref); + m_parent->set_collection_ref(m_index, ref, m_coll_type); } CollectionBasePtr CollectionList::insert_collection(size_t ndx) { REALM_ASSERT(get_table()->get_nesting_levels(m_col_key) == m_level); ensure_created(); - REALM_ASSERT(m_key_type == type_Int); + REALM_ASSERT(m_coll_type == CollectionType::List); auto int_keys = static_cast*>(m_keys.get()); int64_t key = 0; if (auto max = bptree_maximum(*int_keys, nullptr)) { @@ -202,7 +202,7 @@ CollectionBasePtr CollectionList::insert_collection(StringData key) { REALM_ASSERT(get_table()->get_nesting_levels(m_col_key) == m_level); ensure_created(); - REALM_ASSERT(m_key_type == type_String); + REALM_ASSERT(m_coll_type == CollectionType::Dictionary); auto string_keys = static_cast*>(m_keys.get()); StringData actual; IteratorAdapter help(string_keys); @@ -231,7 +231,7 @@ CollectionBasePtr CollectionList::get_collection(size_t ndx) const if (ndx >= sz) { throw OutOfBounds("CollectionList::get_collection_ptr()", ndx, sz); } - if (m_key_type == type_Int) { + if (m_coll_type == CollectionType::List) { auto int_keys = static_cast*>(m_keys.get()); index = int_keys->get(ndx); } @@ -246,7 +246,7 @@ CollectionBasePtr CollectionList::get_collection(size_t ndx) const CollectionListPtr CollectionList::insert_collection_list(size_t ndx) { ensure_created(); - REALM_ASSERT(m_key_type == type_Int); + REALM_ASSERT(m_coll_type == CollectionType::List); auto int_keys = static_cast*>(m_keys.get()); int64_t key = 0; if (auto max = bptree_maximum(*int_keys, nullptr)) { @@ -263,7 +263,7 @@ CollectionListPtr CollectionList::insert_collection_list(size_t ndx) CollectionListPtr CollectionList::insert_collection_list(StringData key) { ensure_created(); - REALM_ASSERT(m_key_type == type_String); + REALM_ASSERT(m_coll_type == CollectionType::Dictionary); auto string_keys = static_cast*>(m_keys.get()); StringData actual; IteratorAdapter help(string_keys); @@ -289,7 +289,7 @@ CollectionListPtr CollectionList::get_collection_list(size_t ndx) const if (ndx >= sz) { throw OutOfBounds("CollectionList::get_collection_ptr()", ndx, sz); } - if (m_key_type == type_Int) { + if (m_coll_type == CollectionType::List) { auto int_keys = static_cast*>(m_keys.get()); index = int_keys->get(ndx); } @@ -303,7 +303,7 @@ CollectionListPtr CollectionList::get_collection_list(size_t ndx) const void CollectionList::remove(size_t ndx) { - REALM_ASSERT(m_key_type == type_Int); + REALM_ASSERT(m_coll_type == CollectionType::List); auto int_keys = static_cast*>(m_keys.get()); const auto sz = int_keys->size(); if (ndx >= sz) { @@ -331,7 +331,7 @@ void CollectionList::remove(size_t ndx) void CollectionList::remove(StringData key) { - REALM_ASSERT(m_key_type == type_String); + REALM_ASSERT(m_coll_type == CollectionType::Dictionary); auto string_keys = static_cast*>(m_keys.get()); IteratorAdapter help(string_keys); auto it = std::lower_bound(help.begin(), help.end(), key); @@ -347,10 +347,10 @@ void CollectionList::remove(StringData key) bump_content_version(); } -ref_type CollectionList::get_collection_ref(Index index) const noexcept +ref_type CollectionList::get_collection_ref(Index index, CollectionType) const noexcept { size_t ndx; - if (m_key_type == type_Int) { + if (m_coll_type == CollectionType::List) { auto int_keys = static_cast*>(m_keys.get()); ndx = int_keys->find_first(mpark::get(index)); } @@ -361,10 +361,10 @@ ref_type CollectionList::get_collection_ref(Index index) const noexcept return ndx == realm::not_found ? 0 : m_refs.get(ndx); } -void CollectionList::set_collection_ref(Index index, ref_type ref) +void CollectionList::set_collection_ref(Index index, ref_type ref, CollectionType) { size_t ndx; - if (m_key_type == type_Int) { + if (m_coll_type == CollectionType::List) { auto int_keys = static_cast*>(m_keys.get()); ndx = int_keys->find_first(mpark::get(index)); } @@ -380,7 +380,7 @@ void CollectionList::set_collection_ref(Index index, ref_type ref) auto CollectionList::get_index(size_t ndx) const noexcept -> Index { - if (m_key_type == type_Int) { + if (m_coll_type == CollectionType::List) { auto int_keys = static_cast*>(m_keys.get()); return int_keys->get(ndx); } @@ -431,7 +431,7 @@ void CollectionList::to_json(std::ostream& out, size_t link_depth, JSONOutputMod util::FunctionRef fn) const { bool is_leaf = m_level == get_table()->get_nesting_levels(m_col_key); - bool is_dictionary = m_key_type == type_String; + bool is_dictionary = m_coll_type == CollectionType::Dictionary; auto sz = size(); auto string_keys = static_cast*>(m_keys.get()); diff --git a/src/realm/collection_list.hpp b/src/realm/collection_list.hpp index 566445bc64c..1c4029ee666 100644 --- a/src/realm/collection_list.hpp +++ b/src/realm/collection_list.hpp @@ -76,8 +76,8 @@ class CollectionList final : public Collection, Index get_index(size_t ndx) const noexcept; - ref_type get_collection_ref(Index index) const noexcept final; - void set_collection_ref(Index index, ref_type ref) final; + ref_type get_collection_ref(Index index, CollectionType) const noexcept final; + void set_collection_ref(Index index, ref_type ref, CollectionType) final; // If this list is at the outermost nesting level, use these functions to // get the leaf collections @@ -112,7 +112,7 @@ class CollectionList final : public Collection, mutable Array m_top; mutable std::unique_ptr m_keys; mutable BPlusTree m_refs; - DataType m_key_type; + CollectionType m_coll_type; mutable uint_fast64_t m_content_version = 0; diff --git a/src/realm/collection_parent.hpp b/src/realm/collection_parent.hpp index 9587c2e7abe..29d560fa419 100644 --- a/src/realm/collection_parent.hpp +++ b/src/realm/collection_parent.hpp @@ -99,9 +99,9 @@ class CollectionParent { /// Get owning object virtual const Obj& get_object() const noexcept = 0; /// Get the top ref from pareht - virtual ref_type get_collection_ref(Index) const noexcept = 0; - /// Set the top ref from pareht - virtual void set_collection_ref(Index, ref_type ref) = 0; + virtual ref_type get_collection_ref(Index, CollectionType) const = 0; + /// Set the top ref in parent + virtual void set_collection_ref(Index, ref_type ref, CollectionType) = 0; // Used when inserting a new link. You will not remove existing links in this process void set_backlink(ColKey col_key, ObjLink new_link) const; @@ -145,11 +145,11 @@ class DummyParent : public CollectionParent { return true; } const Obj& get_object() const noexcept final; - ref_type get_collection_ref(Index) const noexcept final + ref_type get_collection_ref(Index, CollectionType) const final { return m_ref; } - void set_collection_ref(Index, ref_type) {} + void set_collection_ref(Index, ref_type, CollectionType) {} }; } // namespace realm diff --git a/src/realm/dictionary.hpp b/src/realm/dictionary.hpp index 188fc2bbca6..eb39706972b 100644 --- a/src/realm/dictionary.hpp +++ b/src/realm/dictionary.hpp @@ -394,6 +394,10 @@ class DictionaryLinkValues final : public ObjCollectionBase { { return m_source.has_changed(); } + CollectionType get_collection_type() const noexcept override + { + return CollectionType::List; + } // Overrides of ObjCollectionBase: UpdateStatus do_update_if_needed() const final diff --git a/src/realm/list.hpp b/src/realm/list.hpp index 887f2ccc50a..4ce9ec243e8 100644 --- a/src/realm/list.hpp +++ b/src/realm/list.hpp @@ -68,6 +68,7 @@ class LstBase : public CollectionBase { virtual void swap(size_t ndx1, size_t ndx2) = 0; protected: + static constexpr CollectionType s_collection_type = CollectionType::List; void swap_repl(Replication* repl, size_t ndx1, size_t ndx2) const; }; @@ -399,6 +400,10 @@ class LnkLst final : public ObjCollectionBase { const Obj& get_obj() const noexcept final; bool has_changed() const noexcept final; ColKey get_col_key() const noexcept final; + CollectionType get_collection_type() const noexcept override + { + return CollectionType::List; + } // Overriding members of LstBase: std::unique_ptr clone() const override diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index d03927e576b..fb8f9249a96 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -1658,6 +1658,24 @@ void Obj::set_int(ColKey col_key, int64_t value) sync(fields); } +void Obj::set_ref(ColKey col_key, ref_type value, CollectionType type) +{ + update_if_needed(); + + ColKey::Idx col_ndx = col_key.get_index(); + Allocator& alloc = get_alloc(); + alloc.bump_content_version(); + Array fallback(alloc); + Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); + REALM_ASSERT(col_ndx.val + 1 < fields.size()); + ArrayMixed values(alloc); + values.set_parent(&fields, col_ndx.val + 1); + values.init_from_parent(); + values.set(m_row_ndx, Mixed(value, type)); + + sync(fields); +} + void Obj::add_backlink(ColKey backlink_col_key, ObjKey origin_key) { ColKey::Idx backlink_col_ndx = backlink_col_key.get_index(); @@ -2262,14 +2280,31 @@ ref_type Obj::Internal::get_ref(const Obj& obj, ColKey col_key) return to_ref(obj._get(col_key.get_index())); } -ref_type Obj::get_collection_ref(Index index) const noexcept +ref_type Obj::get_collection_ref(Index index, CollectionType type) const { - return to_ref(_get(mpark::get(index).get_index())); + ColKey col_key = mpark::get(index); + if (col_key.is_collection()) { + return to_ref(_get(col_key.get_index())); + } + if (col_key.get_type() == col_type_Mixed) { + auto val = _get(col_key.get_index()); + if (val.is_null() || !val.is_type(DataType(int(type)))) { + throw IllegalOperation("Not proper collection type"); + } + return val.get_ref(); + } + return 0; } -void Obj::set_collection_ref(Index index, ref_type ref) +void Obj::set_collection_ref(Index index, ref_type ref, CollectionType type) { - set_int(mpark::get(index), from_ref(ref)); + ColKey col_key = mpark::get(index); + if (col_key.is_collection()) { + set_int(col_key, from_ref(ref)); + return; + } + REALM_ASSERT(col_key.get_type() == col_type_Mixed); + set_ref(col_key, ref, type); } } // namespace realm diff --git a/src/realm/obj.hpp b/src/realm/obj.hpp index 7b0ffa8a293..45471b74d03 100644 --- a/src/realm/obj.hpp +++ b/src/realm/obj.hpp @@ -83,8 +83,8 @@ class Obj : public CollectionParent { { return *this; } - ref_type get_collection_ref(Index index) const noexcept final; - void set_collection_ref(Index index, ref_type ref) final; + ref_type get_collection_ref(Index, CollectionType) const final; + void set_collection_ref(Index, ref_type, CollectionType) final; // Operator overloads bool operator==(const Obj& other) const; @@ -402,6 +402,7 @@ class Obj : public CollectionParent { } void set_int(ColKey col_key, int64_t value); + void set_ref(ColKey col_key, ref_type value, CollectionType type); void add_backlink(ColKey backlink_col, ObjKey origin_key); bool remove_one_backlink(ColKey backlink_col, ObjKey origin_key); void nullify_link(ColKey origin_col, ObjLink target_key) &&; diff --git a/src/realm/object-store/dictionary.cpp b/src/realm/object-store/dictionary.cpp index efed961ae9e..102008f074f 100644 --- a/src/realm/object-store/dictionary.cpp +++ b/src/realm/object-store/dictionary.cpp @@ -116,6 +116,10 @@ class DictionaryKeyAdapter : public CollectionBase { { m_dictionary->set_owner(std::move(parent), index); } + CollectionType get_collection_type() const noexcept override + { + return CollectionType::List; + } private: std::shared_ptr m_dictionary; diff --git a/src/realm/object-store/results.cpp b/src/realm/object-store/results.cpp index eaa94e2245c..30509858622 100644 --- a/src/realm/object-store/results.cpp +++ b/src/realm/object-store/results.cpp @@ -34,7 +34,8 @@ namespace realm { [[noreturn]] static void unsupported_operation(ColKey column, Table const& table, const char* operation) { auto type = ObjectSchema::from_core_type(column); - std::string_view collection_type = column.is_collection() ? collection_type_name(column) : "property"; + std::string_view collection_type = + column.is_collection() ? collection_type_name(table.get_collection_type(column, 0)) : "property"; const char* column_type = string_for_property_type(type & ~PropertyType::Collection); throw IllegalOperation(util::format("Operation '%1' not supported for %2%3 %4 '%5.%6'", operation, column_type, column.is_nullable() ? "?" : "", collection_type, table.get_class_name(), diff --git a/src/realm/set.hpp b/src/realm/set.hpp index 68a5b7d1884..cd6a14f3f84 100644 --- a/src/realm/set.hpp +++ b/src/realm/set.hpp @@ -39,6 +39,8 @@ class SetBase : public CollectionBase { virtual std::pair erase_any(Mixed value) = 0; protected: + static constexpr CollectionType s_collection_type = CollectionType::Set; + void insert_repl(Replication* repl, size_t index, Mixed value) const; void erase_repl(Replication* repl, size_t index, Mixed value) const; void clear_repl(Replication* repl) const; @@ -332,6 +334,10 @@ class LnkSet final : public ObjCollectionBase { bool is_attached() const final; bool has_changed() const noexcept final; ColKey get_col_key() const noexcept final; + CollectionType get_collection_type() const noexcept override + { + return CollectionType::Set; + } // Overriding members of SetBase: SetBasePtr clone() const override