Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to get path to modified collections in object notifications #7356

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1946,6 +1946,13 @@ RLM_API bool realm_object_changes_is_deleted(const realm_object_changes_t*);
*/
RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_object_changes_t*);

/**
* Get the number of paths to embedded collections that were modified.
*
* This function cannot fail.
*/
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t*);

/**
* Get the column keys for the properties that were modified in an object
* notification.
Expand All @@ -1960,6 +1967,20 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t*,
realm_property_key_t* out_modified, size_t max);

/**
* Get the column keys for the properties that were modified in an object
* notification.
*
* This function cannot fail.
*
* @param out_modified Where the paths should be written. May be NULL.
* @param max The maximum number of paths to write.
* @return The number of paths written to @a out_modified, or the number
* of modified paths if @a out_modified is NULL.
*/
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t*, realm_string_t* out_modified,
size_t max);

/**
* Get the number of various types of changes in a collection notification.
*
Expand Down
1 change: 1 addition & 0 deletions src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class DummyParent : public CollectionParent {
{
return {};
}
void translate_path(const StablePath&, Path&) const final {}
void add_index(Path&, const Index&) const noexcept final {}
size_t find_index(const Index&) const noexcept final
{
Expand Down
5 changes: 3 additions & 2 deletions src/realm/collection_parent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
// Return path from owning object
virtual StablePath get_stable_path() const = 0;
// Add a translation of Index to PathElement
virtual void translate_path(const StablePath&, Path&) const = 0;
virtual void add_index(Path& path, const Index& ndx) const = 0;
// Return position of Index held by child
virtual size_t find_index(const Index& ndx) const = 0;
Expand All @@ -110,9 +111,9 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
friend class CollectionList;

#ifdef REALM_DEBUG
static constexpr size_t s_max_level = 4;
static constexpr int s_max_level = 4;
#else
static constexpr size_t s_max_level = 100;
static constexpr int s_max_level = 100;
#endif
uint8_t m_level = 0;

Expand Down
22 changes: 22 additions & 0 deletions src/realm/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,28 @@ Dictionary::Iterator Dictionary::find(Mixed key) const noexcept
return end();
}


void Dictionary::translate_path(const StablePath& stable_path, Path& path) const
{
auto& index = stable_path[m_level];
auto ndx = find_index(index);
StringData key = do_get_key(ndx).get_string();
path.emplace_back(key);
if (stable_path.size() > size_t(m_level) + 1) {
Mixed val = do_get(ndx);
if (val.is_type(type_Dictionary)) {
DummyParent parent(this->get_table(), val.get_ref());
Dictionary dict(parent, 0);
dict.translate_path(stable_path, path);
}
else if (val.is_type(type_List)) {
DummyParent parent(this->get_table(), val.get_ref());
Lst<Mixed> list(parent, 0);
list.translate_path(stable_path, path);
}
}
}

void Dictionary::add_index(Path& path, const Index& index) const
{
auto ndx = m_values->find_key(index.get_salt());
Expand Down
1 change: 1 addition & 0 deletions src/realm/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle
{
return Base::get_stable_path();
}
void translate_path(const StablePath& stable_path, Path& path) const final;

void add_index(Path& path, const Index& ndx) const final;
size_t find_index(const Index&) const final;
Expand Down
20 changes: 20 additions & 0 deletions src/realm/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,26 @@ void Lst<Mixed>::set_collection_ref(Index index, ref_type ref, CollectionType ty
m_tree->set(ndx, Mixed(ref, type));
}

void Lst<Mixed>::translate_path(const StablePath& stable_path, Path& path) const
{
auto& index = stable_path[m_level];
auto ndx = find_index(index);
path.emplace_back(ndx);
if (stable_path.size() > size_t(m_level) + 1) {
Mixed val = get(ndx);
if (val.is_type(type_Dictionary)) {
DummyParent parent(this->get_table(), val.get_ref());
Dictionary dict(parent, 0);
dict.translate_path(stable_path, path);
}
else if (val.is_type(type_List)) {
DummyParent parent(this->get_table(), val.get_ref());
Lst<Mixed> list(parent, 0);
list.translate_path(stable_path, path);
}
}
}

void Lst<Mixed>::add_index(Path& path, const Index& index) const
{
auto ndx = m_tree->find_key(index.get_salt());
Expand Down
1 change: 1 addition & 0 deletions src/realm/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
{
return Base::get_stable_path();
}
void translate_path(const StablePath& stable_path, Path& path) const final;

ColKey get_col_key() const noexcept override
{
Expand Down
10 changes: 10 additions & 0 deletions src/realm/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,16 @@ CollectionPtr Obj::get_collection_ptr(const Path& path) const
return collection;
}

void Obj::translate_path(const StablePath& stable_path, Path& path) const
{
ColKey col_key = m_table->get_column_key(stable_path[0]);
path.emplace_back(m_table->get_column_name(col_key));
if (stable_path.size() > 1) {
CollectionBasePtr collection = get_collection_ptr(col_key);
dynamic_cast<CollectionParent*>(collection.get())->translate_path(stable_path, path);
}
}

CollectionPtr Obj::get_collection_by_stable_path(const StablePath& path) const
{
// First element in path is phony column key
Expand Down
5 changes: 5 additions & 0 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class Obj {
Path get_short_path() const noexcept;
ColKey get_col_key() const noexcept;
StablePath get_stable_path() const noexcept;
void translate_path(const StablePath&, Path&) const;
void add_index(Path& path, const CollectionParent::Index& ndx) const;

TableRef get_table() const noexcept
Expand Down Expand Up @@ -441,6 +442,10 @@ class ObjCollectionParent final : public Obj, public CollectionParent {
{
return Obj::get_stable_path();
}
void translate_path(const StablePath& stable_path, Path& path) const override
{
Obj::translate_path(stable_path, path);
}
void add_index(Path& path, const Index& ndx) const override
{
Obj::add_index(path, ndx);
Expand Down
2 changes: 1 addition & 1 deletion src/realm/object-store/binding_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class BindingContext {
// Populated with information about which columns were changed
// May be shorter than the actual number of columns if the later columns
// are not modified
std::unordered_map<int64_t, ColumnInfo> changes;
std::unordered_map<ColKey, ColumnInfo> changes;

// Simple lexographic ordering
friend bool operator<(ObserverState const& lft, ObserverState const& rgt)
Expand Down
38 changes: 38 additions & 0 deletions src/realm/object-store/c_api/notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
return changes->columns.size();
}

RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t* changes)
{
return changes->modified_paths.size();
}

RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t* changes,
realm_property_key_t* out_properties, size_t max)
{
Expand All @@ -130,6 +135,39 @@ RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_c
return i;
}

RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t* const_changes,
realm_string_t* out_paths, size_t max)
{
if (!out_paths)
return const_changes->modified_paths.size();

realm_object_changes_t* changes = const_cast<realm_object_changes_t*>(const_changes);
changes->path_buffer.resize(changes->modified_paths.size());
size_t i = 0;
for (const auto& p : changes->modified_paths) {
if (i >= max) {
break;
}
std::string& path = changes->path_buffer[i];
path = p[0].get_key();
for (auto path_elem = p.begin() + 1; path_elem != p.end(); ++path_elem) {
if (path_elem->is_key()) {
path += '.';
path += path_elem->get_key();
}
else {
char buffer[10];
sprintf(buffer, "[%u]", unsigned(path_elem->get_ndx()));
path += buffer;
}
}
out_paths[i].data = path.data();
out_paths[i].size = path.size();
++i;
}
return i;
}

RLM_API realm_notification_token_t* realm_list_add_notification_callback(realm_list_t* list,
realm_userdata_t userdata,
realm_free_userdata_func_t free,
Expand Down
1 change: 1 addition & 0 deletions src/realm/object-store/c_api/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ struct realm_object_changes : realm::c_api::WrapC, realm::CollectionChangeSet {
{
return new realm_object_changes{static_cast<const realm::CollectionChangeSet&>(*this)};
}
std::vector<std::string> path_buffer;
};

struct realm_collection_changes : realm::c_api::WrapC, realm::CollectionChangeSet {
Expand Down
3 changes: 2 additions & 1 deletion src/realm/object-store/collection_notifications.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ struct CollectionChangeSet {

// Per-column version of `modifications`
std::unordered_map<int64_t, IndexSet> columns;
std::vector<Path> modified_paths;

std::set<StableIndex> paths;
std::set<StableIndex> stable_indexes;

bool empty() const noexcept
{
Expand Down
2 changes: 1 addition & 1 deletion src/realm/object-store/impl/collection_change_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,5 +704,5 @@ CollectionChangeSet CollectionChangeBuilder::finalize() &&

return {std::move(deletions), std::move(insertions), std::move(modifications_in_old),
std::move(modifications), std::move(moves), collection_root_was_deleted,
collection_was_cleared, std::move(columns)};
collection_was_cleared, std::move(columns), std::move(modified_paths)};
}
4 changes: 2 additions & 2 deletions src/realm/object-store/impl/list_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ void ListNotifier::run()

// Modifications to nested values in Mixed are recorded in replication as
// StableIndex and we have to look up the actual index afterwards
if (m_change.paths.size()) {
if (m_change.stable_indexes.size()) {
REALM_ASSERT(m_collection_parent);
REALM_ASSERT(m_type == PropertyType::Mixed);
for (auto& p : m_change.paths) {
for (auto& p : m_change.stable_indexes) {
if (auto ndx = m_collection_parent->find_index(p); ndx != realm::not_found)
m_change.modifications.add(ndx);
}
Expand Down
14 changes: 10 additions & 4 deletions src/realm/object-store/impl/object_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,19 @@ void ObjectNotifier::run()

const auto& change = it->second;

auto column_modifications = change.get_columns_modified(m_obj_key);
if (!column_modifications)
auto path_modifications = change.get_paths_modified(m_obj_key);
if (!path_modifications)
return;

// Finally we add all changes to `m_change` which is later used to notify about the changed columns.
m_change.modifications.add(0);
for (auto col : *column_modifications) {
m_change.columns[col.value].add(0);
auto obj = m_table->get_object(m_obj_key);
for (const StablePath& stable_path : *path_modifications) {
m_change.columns[m_table->get_column_key(stable_path[0]).value].add(0);
if (stable_path.size() > 1) {
Path path;
obj.translate_path(stable_path, path);
m_change.modified_paths.emplace_back(std::move(path));
}
}
}
4 changes: 2 additions & 2 deletions src/realm/object-store/impl/results_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ void ListResultsNotifier::run()

// Modifications to nested values in Mixed are recorded in replication as
// StableIndex and we have to look up the actual index afterwards
if (m_change.paths.size()) {
if (m_change.stable_indexes.size()) {
if (auto coll = dynamic_cast<CollectionParent*>(m_list.get())) {
for (auto& p : m_change.paths) {
for (auto& p : m_change.stable_indexes) {
if (auto ndx = coll->find_index(p); ndx != realm::not_found)
m_change.modifications.add(ndx);
}
Expand Down
27 changes: 16 additions & 11 deletions src/realm/object-store/impl/transact_log_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ void KVOAdapter::before(Transaction& sg)
m_invalidated.push_back(observer.info);
continue;
}
auto column_modifications = table.get_columns_modified(key);
if (column_modifications) {
for (auto col : *column_modifications) {
observer.changes[col.value].kind = BindingContext::ColumnInfo::Kind::Set;
auto tbl = sg.get_table(observer.table_key);
if (auto path_modifications = table.get_paths_modified(key)) {
for (const StablePath& path : *path_modifications) {
observer.changes[tbl->get_column_key(path[0])].kind = BindingContext::ColumnInfo::Kind::Set;
}
}
}
Expand All @@ -123,7 +123,7 @@ void KVOAdapter::before(Transaction& sg)
// We may have pre-emptively marked the column as modified if the
// LinkList was selected but the actual changes made ended up being
// a no-op
list.observer->changes.erase(list.col_key.value);
list.observer->changes.erase(list.col_key);
continue;
}
// If the containing row was deleted then changes will be empty
Expand All @@ -132,7 +132,7 @@ void KVOAdapter::before(Transaction& sg)
continue;
}
// otherwise the column should have been marked as modified
auto it = list.observer->changes.find(list.col_key.value);
auto it = list.observer->changes.find(list.col_key);
REALM_ASSERT(it != list.observer->changes.end());
auto& builder = list.builder;
auto& changes = it->second;
Expand Down Expand Up @@ -364,15 +364,17 @@ class TransactLogObserver : public TransactLogValidationMixin {
return true;
}

bool select_collection(ColKey col, ObjKey obj, const StablePath& path)
bool select_collection(ColKey, ObjKey obj, const StablePath& path)
{
modify_object(col, obj);
if (m_active_table) {
m_active_table->modifications_add(obj, path);
}
auto table = current_table();
m_active_collection = nullptr;
for (auto& c : m_info.collections) {
if (c.table_key == table && c.obj_key == obj && c.path.is_prefix_of(path)) {
if (c.path.size() != path.size()) {
c.changes->paths.insert(path[c.path.size()]);
c.changes->stable_indexes.insert(path[c.path.size()]);
}
// If there are multiple exact matches for this collection we
// use the first and then propagate the data to the others later
Expand Down Expand Up @@ -463,8 +465,11 @@ class TransactLogObserver : public TransactLogValidationMixin {

bool modify_object(ColKey col, ObjKey key)
{
if (m_active_table)
m_active_table->modifications_add(key, col);
if (m_active_table) {
StablePath path;
path.push_back(StableIndex(col, 0));
m_active_table->modifications_add(key, path);
}
return true;
}

Expand Down
Loading
Loading