Skip to content

Commit

Permalink
Make comparing Groups more comprehensive
Browse files Browse the repository at this point in the history
We can now compare collections and embedded objects.
We can compare Groups that are not in the same realm.
  • Loading branch information
jedelbo committed Dec 17, 2021
1 parent 22bda1f commit a44c6d7
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 154 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ GPATH
GRTAGS
GTAGS

/cover_html
/gcovr.xml
/tmp
doc/html
/html
coverage-*

# CMake artifacts
CMakeFiles
Expand Down
24 changes: 13 additions & 11 deletions src/realm/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1423,19 +1423,21 @@ void Group::update_refs(ref_type top_ref) noexcept

bool Group::operator==(const Group& g) const
{
auto keys_this = get_table_keys();
auto keys_g = g.get_table_keys();
size_t n = keys_this.size();
if (n != keys_g.size())
return false;
for (size_t i = 0; i < n; ++i) {
const StringData& table_name_1 = get_table_name(keys_this[i]);
const StringData& table_name_2 = g.get_table_name(keys_g[i]);
if (table_name_1 != table_name_2)
for (auto tk : get_table_keys()) {
const StringData& table_name = get_table_name(tk);

ConstTableRef table_1 = get_table(tk);
ConstTableRef table_2 = g.get_table(table_name);
if (!table_2)
return false;
if (table_1->get_primary_key_column().get_type() != table_2->get_primary_key_column().get_type()) {
return false;
}
if (table_1->is_embedded() != table_2->is_embedded())
return false;
if (table_1->is_embedded())
continue;

ConstTableRef table_1 = get_table(keys_this[i]);
ConstTableRef table_2 = g.get_table(keys_g[i]);
if (*table_1 != *table_2)
return false;
}
Expand Down
173 changes: 73 additions & 100 deletions src/realm/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,63 @@ Replication* Obj::get_replication() const

bool Obj::operator==(const Obj& other) const
{
size_t col_cnt = get_spec().get_public_column_count();
while (col_cnt--) {
ColKey key = m_table->spec_ndx2colkey(col_cnt);
if (cmp(other, key) != 0) {
return false;
for (auto ck : m_table->get_column_keys()) {
StringData col_name = m_table->get_column_name(ck);

auto compare_values = [&](Mixed val1, Mixed val2) {
if (val1.is_null()) {
if (!val2.is_null())
return false;
}
else {
if (val1.get_type() != val2.get_type())
return false;
if (val1.is_type(type_Link, type_TypedLink)) {
auto o1 = _get_linked_object(ck, val1);
auto o2 = other._get_linked_object(col_name, val2);
if (o1.m_table->is_embedded()) {
return o1 == o2;
}
else {
return o1.get_primary_key() == o2.get_primary_key();
}
}
else {
if (val1 != val2)
return false;
}
}
return true;
};

if (!ck.is_collection()) {
if (!compare_values(get_any(ck), other.get_any(col_name)))
return false;
}
else {
auto coll1 = get_collection_ptr(ck);
auto coll2 = other.get_collection_ptr(col_name);
size_t sz = coll1->size();
if (coll2->size() != sz)
return false;
if (ck.is_list() || ck.is_set()) {
for (size_t i = 0; i < sz; i++) {
if (!compare_values(coll1->get_any(i), coll2->get_any(i)))
return false;
}
}
if (ck.is_dictionary()) {
auto dict1 = dynamic_cast<Dictionary*>(coll1.get());
auto dict2 = dynamic_cast<Dictionary*>(coll2.get());
for (size_t i = 0; i < sz; i++) {
auto [key, value] = dict1->get_pair(i);
auto val2 = dict2->try_get(key);
if (!val2)
return false;
if (!compare_values(value, *val2))
return false;
}
}
}
}
return true;
Expand Down Expand Up @@ -439,94 +491,6 @@ Mixed Obj::get_primary_key() const
return col ? get_any(col) : Mixed{get_key()};
}

template <class T>
inline int Obj::cmp(const Obj& other, ColKey::Idx col_ndx) const
{
T val1 = _get<T>(col_ndx);
T val2 = other._get<T>(col_ndx);

if (val1 < val2) {
return -1;
}

if (val1 > val2) {
return 1;
}

return 0;
}

template <>
inline int Obj::cmp<StringData>(const Obj& other, ColKey::Idx col_ndx) const
{
StringData a = _get<StringData>(col_ndx);
StringData b = other._get<StringData>(col_ndx);

if (a.is_null()) {
return b.is_null() ? 0 : -1;
}

if (b.is_null()) {
return 1;
}

if (a == b) {
return 0;
}

return utf8_compare(a, b) ? -1 : 1;
}

int Obj::cmp(const Obj& other, ColKey col_key) const
{
other.check_valid();
ColKey::Idx col_ndx = col_key.get_index();
ColumnAttrMask attr = col_key.get_attrs();
REALM_ASSERT(!attr.test(col_attr_List)); // TODO: implement comparison of lists

switch (DataType(col_key.get_type())) {
case type_Int:
if (attr.test(col_attr_Nullable))
return cmp<util::Optional<Int>>(other, col_ndx);
else
return cmp<Int>(other, col_ndx);
case type_Bool:
return cmp<Bool>(other, col_ndx);
case type_Float:
return cmp<Float>(other, col_ndx);
case type_Double:
return cmp<Double>(other, col_ndx);
case type_String:
return cmp<String>(other, col_ndx);
case type_Binary:
return cmp<Binary>(other, col_ndx);
case type_Mixed:
return cmp<Mixed>(other, col_ndx);
case type_Timestamp:
return cmp<Timestamp>(other, col_ndx);
case type_Decimal:
return cmp<Decimal128>(other, col_ndx);
case type_ObjectId:
if (attr.test(col_attr_Nullable))
return cmp<util::Optional<ObjectId>>(other, col_ndx);
else
return cmp<ObjectId>(other, col_ndx);
case type_UUID:
if (attr.test(col_attr_Nullable))
return cmp<util::Optional<UUID>>(other, col_ndx);
else
return cmp<UUID>(other, col_ndx);
case type_Link:
return cmp<ObjKey>(other, col_ndx);
case type_TypedLink:
return cmp<ObjLink>(other, col_ndx);
case type_LinkList:
REALM_ASSERT(false);
break;
}
return 0;
}

/* FIXME: Make this one fast too!
template <>
ObjKey Obj::_get(size_t col_ndx) const
Expand All @@ -535,13 +499,18 @@ ObjKey Obj::_get(size_t col_ndx) const
}
*/

Obj Obj::get_linked_object(ColKey link_col_key) const
Obj Obj::_get_linked_object(ColKey link_col_key, Mixed link) const
{
TableRef target_table = get_target_table(link_col_key);
ObjKey key = get<ObjKey>(link_col_key);
Obj obj;
if (key) {
obj = target_table->get_object(key);
if (!link.is_null()) {
TableRef target_table;
if (link.is_type(type_TypedLink)) {
target_table = m_table->get_parent_group()->get_table(link.get_link().get_table_key());
}
else {
target_table = get_target_table(link_col_key);
}
obj = target_table->get_object(link.get<ObjKey>());
}
return obj;
}
Expand Down Expand Up @@ -741,10 +710,9 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const
index = Mixed(int64_t(i));
}
else if (next_col_key.get_attrs().test(col_attr_Dictionary)) {
ObjLink link = get_link();
auto dict = obj.get_dictionary(next_col_key);
for (auto it : dict) {
if (it.second.is_type(type_TypedLink) && it.second.get_link() == link) {
if (it.second.is_type(type_TypedLink) && it.second.get_link() == get_link()) {
index = it.first;
break;
}
Expand Down Expand Up @@ -2131,6 +2099,11 @@ CollectionBasePtr Obj::get_collection_ptr(ColKey col_key) const
return {};
}

CollectionBasePtr Obj::get_collection_ptr(StringData col_name) const
{
return get_collection_ptr(get_column_key(col_name));
}

LinkCollectionPtr Obj::get_linkcollection_ptr(ColKey col_key) const
{
if (col_key.is_list()) {
Expand Down
27 changes: 15 additions & 12 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ class Obj {
return get<U>(get_column_key(col_name));
}
bool is_unresolved(ColKey col_key) const;
int cmp(const Obj& other, ColKey col_key) const;

size_t get_link_count(ColKey col_key) const;

Expand Down Expand Up @@ -262,8 +261,14 @@ class Obj {

void assign(const Obj& other);

Obj get_linked_object(ColKey link_col_key) const;
Obj get_linked_object(StringData link_col_name) const;
Obj get_linked_object(ColKey link_col_key) const
{
return _get_linked_object(link_col_key, get_any(link_col_key));
}
Obj get_linked_object(StringData link_col_name) const
{
return get_linked_object(get_column_key(link_col_name));
}

template <typename U>
Lst<U> get_list(ColKey col_key) const;
Expand Down Expand Up @@ -304,6 +309,7 @@ class Obj {
Dictionary get_dictionary(StringData col_name) const;

CollectionBasePtr get_collection_ptr(ColKey col_key) const;
CollectionBasePtr get_collection_ptr(StringData col_name) const;
LinkCollectionPtr get_linkcollection_ptr(ColKey col_key) const;

void assign_pk_and_backlinks(const Obj& other);
Expand Down Expand Up @@ -370,9 +376,6 @@ class Obj {
template <typename U>
U _get(ColKey::Idx col_ndx) const;

template <class T>
int cmp(const Obj& other, ColKey::Idx col_ndx) const;
int cmp(const Obj& other, ColKey::Idx col_ndx) const;
ObjKey get_backlink(ColKey backlink_col, size_t backlink_ndx) const;
// Return all backlinks from a specific backlink column
std::vector<ObjKey> get_all_backlinks(ColKey backlink_col) const;
Expand All @@ -399,6 +402,12 @@ class Obj {
return m_row_ndx;
}

Obj _get_linked_object(ColKey link_col_key, Mixed link) const;
Obj _get_linked_object(StringData link_col_name, Mixed link) const
{
return _get_linked_object(get_column_key(link_col_name), link);
}

void set_int(ColKey col_key, int64_t value);
void add_backlink(ColKey backlink_col, ObjKey origin_key);
bool remove_one_backlink(ColKey backlink_col, ObjKey origin_key);
Expand Down Expand Up @@ -549,12 +558,6 @@ std::vector<U> Obj::get_list_values(ColKey col_key) const
return values;
}

inline Obj Obj::get_linked_object(StringData link_col_name) const
{
ColKey col = get_column_key(link_col_name);
return get_linked_object(col);
}

template <class Val>
inline Obj& Obj::_set(size_t col_ndx, Val v)
{
Expand Down
31 changes: 19 additions & 12 deletions src/realm/table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2716,25 +2716,32 @@ size_t Table::compute_aggregated_byte_size() const noexcept
return stats_2.allocated;
}


bool Table::compare_objects(const Table& t) const
bool Table::operator==(const Table& t) const
{
if (size() != t.size()) {
return false;
}

auto it1 = begin();
auto it2 = t.begin();
auto e = end();

while (it1 != e) {
if (*it1 == *it2) {
++it1;
++it2;
// Check columns
for (auto ck : this->get_column_keys()) {
auto name = get_column_name(ck);
auto other_ck = t.get_column_key(name);
auto attrs = ck.get_attrs();
if (!other_ck || other_ck.get_attrs() != attrs) {
return false;
}
}
auto pk_col = get_primary_key_column();
for (auto o : *this) {
Obj other_o;
if (pk_col) {
auto pk = o.get_any(pk_col);
other_o = t.get_object_with_primary_key(pk);
}
else {
return false;
other_o = t.get_object(o.get_key());
}
if (!(other_o && o == other_o))
return false;
}

return true;
Expand Down
5 changes: 0 additions & 5 deletions src/realm/table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1268,11 +1268,6 @@ inline bool Table::is_group_level() const noexcept
return bool(get_parent_group());
}

inline bool Table::operator==(const Table& t) const
{
return m_spec == t.m_spec && compare_objects(t); // Throws
}

inline bool Table::operator!=(const Table& t) const
{
return !(*this == t); // Throws
Expand Down
Loading

0 comments on commit a44c6d7

Please sign in to comment.