Skip to content

Commit

Permalink
Fix query involving size of collection of links over links
Browse files Browse the repository at this point in the history
  • Loading branch information
jedelbo committed Aug 9, 2023
1 parent 8f3b310 commit adb35e4
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Fix failed assertion for unknown app server errors ([#6758](https://github.com/realm/realm-core/issues/6758), since v12.9.0).
* Running a query on @keys in a Dictionary would throw an exception ([#6831](https://github.com/realm/realm-core/issues/6831), since v13.15.1)
* Change JSON selialization format back to follow ISO 8601 - and add output of nanoseconds ([#6855](https://github.com/realm/realm-core/issues/6855), since 13.17.0)
* Testing the size of a collection of links against zero would sometimes fail (sometimes = "difficult to explain"). In particular: ([#6850](https://github.com/realm/realm-core/issues/6850), since v13.15.1)

### Breaking changes
* None.
Expand Down
25 changes: 0 additions & 25 deletions src/realm/cluster_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,31 +766,6 @@ ClusterTree::ClusterTree(Table* owner, Allocator& alloc, size_t top_position_for

ClusterTree::~ClusterTree() {}

size_t ClusterTree::size_from_ref(ref_type ref, Allocator& alloc)
{
size_t ret = 0;
if (ref) {
Array arr(alloc);
arr.init_from_ref(ref);
if (arr.is_inner_bptree_node()) {
ret = size_t(arr.get(2)) >> 1;
}
else {
int64_t rot = arr.get(0);
if (rot & 1) {
ret = size_t(rot) >> 1;
}
else {
ref_type key_ref = to_ref(rot);
MemRef mem(key_ref, alloc);
auto header = mem.get_addr();
ret = Node::get_size_from_header(header);
}
}
}
return ret;
}

std::unique_ptr<ClusterNode> ClusterTree::create_root_from_parent(ArrayParent* parent, size_t ndx_in_parent)
{
ref_type ref = parent->get_child_ref(ndx_in_parent);
Expand Down
2 changes: 0 additions & 2 deletions src/realm/cluster_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ class ClusterTree {
return m_root->nb_columns();
}

static size_t size_from_ref(ref_type, Allocator& alloc);

void destroy()
{
m_root->destroy_deep();
Expand Down
22 changes: 22 additions & 0 deletions src/realm/collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,26 @@ void check_for_last_unresolved(BPlusTree<ObjKey>* tree)
}
}

size_t get_collection_size_from_ref(ref_type ref, Allocator& alloc)
{
size_t ret = 0;
if (ref) {
Array arr(alloc);
arr.init_from_ref(ref);
if (arr.is_inner_bptree_node()) {
// This is a BPlusTree
ret = size_t(arr.back()) >> 1;
}
else if (arr.has_refs()) {
// This is a dictionary
auto key_ref = arr.get_as_ref(0);
ret = get_collection_size_from_ref(key_ref, alloc);
}
else {
ret = arr.size();
}
}
return ret;
}

} // namespace realm::_impl
4 changes: 4 additions & 0 deletions src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,10 @@ struct CollectionIterator {
size_t m_ndx = size_t(-1);
};

namespace _impl {
size_t get_collection_size_from_ref(ref_type, Allocator& alloc);
}

} // namespace realm

#endif // REALM_COLLECTION_HPP
1 change: 1 addition & 0 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ class Obj {
template <class>
friend class Lst;
friend class LnkLst;
friend class LinkCount;
friend class Dictionary;
friend class LinkMap;
template <class>
Expand Down
40 changes: 39 additions & 1 deletion src/realm/query_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class DictionarySize : public Columns<Dictionary> {
destination.init(list_refs.m_from_list, list_refs.size());
for (size_t i = 0; i < list_refs.size(); i++) {
ref_type ref = to_ref(list_refs[i].get_int());
size_t s = ClusterTree::size_from_ref(ref, alloc);
size_t s = _impl::get_collection_size_from_ref(ref, alloc);
destination.set(i, int64_t(s));
}
}
Expand Down Expand Up @@ -502,6 +502,44 @@ void ColumnListBase::get_lists(size_t index, Value<int64_t>& destination, size_t
}
}

void LinkCount::evaluate(size_t index, ValueBase& destination)
{
if (m_column_key) {
REALM_ASSERT(m_link_map.has_links());
std::vector<ObjKey> links = m_link_map.get_links(index);
auto sz = links.size();

if (sz == 0) {
destination.init(true, 0);
}
else {
destination.init(true, sz);
Allocator& alloc = m_link_map.get_target_table()->get_alloc();
for (size_t i = 0; i < sz; i++) {
const Obj obj = m_link_map.get_target_table()->get_object(links[i]);
auto val = obj._get<int64_t>(m_column_key.get_index());
size_t s;
if (val & 1) {
// It is a backlink column with just one value
s = 1;
}
else {
s = _impl::get_collection_size_from_ref(to_ref(val), alloc);
}
destination.set(i, int64_t(s));
}
}
}
else {
destination = Value<int64_t>(m_link_map.count_links(index));
}
}

std::string LinkCount::description(util::serializer::SerialisationState& state) const
{
return state.describe_columns(m_link_map, m_column_key) + util::serializer::value_separator + "@count";
}

Query Subexpr2<StringData>::equal(StringData sd, bool case_sensitive)
{
return string_compare<StringData, Equal, EqualIns>(*this, sd, case_sensitive);
Expand Down
24 changes: 15 additions & 9 deletions src/realm/query_expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,14 @@ class LinkMap final {
return !m_link_column_keys.empty();
}

ColKey pop_last()
{
ColKey col = m_link_column_keys.back();
m_link_column_keys.pop_back();
m_tables.pop_back();
return col;
}

private:
bool map_links(size_t column, ObjKey key, LinkMapFunction lm) const;
void map_links(size_t column, size_t row, LinkMapFunction lm) const;
Expand Down Expand Up @@ -2199,10 +2207,14 @@ class LinkCount : public Subexpr2<Int> {
LinkCount(const LinkMap& link_map)
: m_link_map(link_map)
{
if (m_link_map.get_nb_hops() > 1) {
m_column_key = m_link_map.pop_last();
}
}
LinkCount(LinkCount const& other)
: Subexpr2<Int>(other)
, m_link_map(other.m_link_map)
, m_column_key(other.m_column_key)
{
}

Expand Down Expand Up @@ -2231,19 +2243,13 @@ class LinkCount : public Subexpr2<Int> {
m_link_map.collect_dependencies(tables);
}

void evaluate(size_t index, ValueBase& destination) override
{
size_t count = m_link_map.count_links(index);
destination = Value<int64_t>(count);
}
void evaluate(size_t index, ValueBase& destination) override;

std::string description(util::serializer::SerialisationState& state) const override
{
return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator + "@count";
}
std::string description(util::serializer::SerialisationState& state) const override;

private:
LinkMap m_link_map;
ColKey m_column_key;
};

// Gives a count of all backlinks across all columns for the specified row.
Expand Down
2 changes: 1 addition & 1 deletion src/realm/util/serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ std::string SerialisationState::get_column_name(ConstTableRef table, ColKey col_
}
col_name = col_name.substr(0, pos) + "\\" + col_name.substr(pos);
pos += 2;
pos = col_name.find_first_of(" \b\n\r", pos);
pos = col_name.find_first_of(" \t\r\n", pos);
}
return col_name;
}
Expand Down
46 changes: 46 additions & 0 deletions test/test_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5633,4 +5633,50 @@ TEST(Query_AsymmetricObjects)
ErrorCodes::IllegalOperation);
}

TEST(Query_NestedLinkCount)
{
Group g;
auto table = g.add_table_with_primary_key("class_TestClass", type_Int, "id");
table->add_column_list(*table, "children");
table->add_column_dictionary(*table, "dictionary");
table->add_column_list(*table, "list");

Obj o1 = table->create_object_with_primary_key(1);
Obj o2 = table->create_object_with_primary_key(2);
Obj o3 = table->create_object_with_primary_key(3);

o2.get_linklist("children").add(o1.get_key());
auto dict = o2.get_dictionary("dictionary");
dict.insert("key", o3);
o3.get_linklist("children").add(o2.get_key());

auto q = table->query("children.list.@size == 0");
CHECK_EQUAL(q.count(), 2);

q = table->query("@links.TestClass.children.dictionary.@size == 0");
CHECK_EQUAL(q.count(), 1); // Only o2

q = table->query("@links.TestClass.children.list.@size == 0");
CHECK_EQUAL(q.count(), 2);

o3.get_dictionary("dictionary").insert("key", o1);
auto list = o3.get_linklist("list");
list.add(o1.get_key());
list.add(o1.get_key());
list.add(o1.get_key());
list.add(o1.get_key());
list.add(o1.get_key());

q = table->query("@links.TestClass.children.list.@size == 5");
CHECK_EQUAL(q.count(), 1);

dict.insert("key1", o3);
dict.insert("key2", o3);
dict.insert("key3", o3);
dict.insert("key4", o3);
q = table->query("@links.TestClass.children.dictionary.@size == 5");
CHECK_EQUAL(q.count(), 1); // Only o2
}


#endif // TEST_QUERY

0 comments on commit adb35e4

Please sign in to comment.