Skip to content

Commit 5f8cca1

Browse files
committed
Add ability to get path to modified collections in object notifications
1 parent 687bb98 commit 5f8cca1

24 files changed

+214
-55
lines changed

src/realm.h

+21
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,13 @@ RLM_API bool realm_object_changes_is_deleted(const realm_object_changes_t*);
19461946
*/
19471947
RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_object_changes_t*);
19481948

1949+
/**
1950+
* Get the number of paths to embedded collections that were modified.
1951+
*
1952+
* This function cannot fail.
1953+
*/
1954+
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t*);
1955+
19491956
/**
19501957
* Get the column keys for the properties that were modified in an object
19511958
* notification.
@@ -1960,6 +1967,20 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
19601967
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t*,
19611968
realm_property_key_t* out_modified, size_t max);
19621969

1970+
/**
1971+
* Get the column keys for the properties that were modified in an object
1972+
* notification.
1973+
*
1974+
* This function cannot fail.
1975+
*
1976+
* @param out_modified Where the paths should be written. May be NULL.
1977+
* @param max The maximum number of paths to write.
1978+
* @return The number of paths written to @a out_modified, or the number
1979+
* of modified paths if @a out_modified is NULL.
1980+
*/
1981+
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t*, realm_string_t* out_modified,
1982+
size_t max);
1983+
19631984
/**
19641985
* Get the number of various types of changes in a collection notification.
19651986
*

src/realm/collection.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class DummyParent : public CollectionParent {
3939
{
4040
return {};
4141
}
42+
void translate_path(const StablePath&, Path&) const final {}
4243
void add_index(Path&, const Index&) const noexcept final {}
4344
size_t find_index(const Index&) const noexcept final
4445
{

src/realm/collection_parent.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
9090
// Return path from owning object
9191
virtual StablePath get_stable_path() const = 0;
9292
// Add a translation of Index to PathElement
93+
virtual void translate_path(const StablePath&, Path&) const = 0;
9394
virtual void add_index(Path& path, const Index& ndx) const = 0;
9495
// Return position of Index held by child
9596
virtual size_t find_index(const Index& ndx) const = 0;
@@ -110,9 +111,9 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
110111
friend class CollectionList;
111112

112113
#ifdef REALM_DEBUG
113-
static constexpr size_t s_max_level = 4;
114+
static constexpr int s_max_level = 4;
114115
#else
115-
static constexpr size_t s_max_level = 100;
116+
static constexpr int s_max_level = 100;
116117
#endif
117118
uint8_t m_level = 0;
118119

src/realm/dictionary.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,28 @@ Dictionary::Iterator Dictionary::find(Mixed key) const noexcept
634634
return end();
635635
}
636636

637+
638+
void Dictionary::translate_path(const StablePath& stable_path, Path& path) const
639+
{
640+
auto& index = stable_path[m_level];
641+
auto ndx = find_index(index);
642+
StringData key = do_get_key(ndx).get_string();
643+
path.emplace_back(key);
644+
if (stable_path.size() > size_t(m_level) + 1) {
645+
Mixed val = do_get(ndx);
646+
if (val.is_type(type_Dictionary)) {
647+
DummyParent parent(this->get_table(), val.get_ref());
648+
Dictionary dict(parent, 0);
649+
dict.translate_path(stable_path, path);
650+
}
651+
else if (val.is_type(type_List)) {
652+
DummyParent parent(this->get_table(), val.get_ref());
653+
Lst<Mixed> list(parent, 0);
654+
list.translate_path(stable_path, path);
655+
}
656+
}
657+
}
658+
637659
void Dictionary::add_index(Path& path, const Index& index) const
638660
{
639661
auto ndx = m_values->find_key(index.get_salt());

src/realm/dictionary.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle
202202
{
203203
return Base::get_stable_path();
204204
}
205+
void translate_path(const StablePath& stable_path, Path& path) const final;
205206

206207
void add_index(Path& path, const Index& ndx) const final;
207208
size_t find_index(const Index&) const final;

src/realm/list.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,26 @@ void Lst<Mixed>::set_collection_ref(Index index, ref_type ref, CollectionType ty
763763
m_tree->set(ndx, Mixed(ref, type));
764764
}
765765

766+
void Lst<Mixed>::translate_path(const StablePath& stable_path, Path& path) const
767+
{
768+
auto& index = stable_path[m_level];
769+
auto ndx = find_index(index);
770+
path.emplace_back(ndx);
771+
if (stable_path.size() > size_t(m_level) + 1) {
772+
Mixed val = get(ndx);
773+
if (val.is_type(type_Dictionary)) {
774+
DummyParent parent(this->get_table(), val.get_ref());
775+
Dictionary dict(parent, 0);
776+
dict.translate_path(stable_path, path);
777+
}
778+
else if (val.is_type(type_List)) {
779+
DummyParent parent(this->get_table(), val.get_ref());
780+
Lst<Mixed> list(parent, 0);
781+
list.translate_path(stable_path, path);
782+
}
783+
}
784+
}
785+
766786
void Lst<Mixed>::add_index(Path& path, const Index& index) const
767787
{
768788
auto ndx = m_tree->find_key(index.get_salt());

src/realm/list.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
466466
{
467467
return Base::get_stable_path();
468468
}
469+
void translate_path(const StablePath& stable_path, Path& path) const final;
469470

470471
ColKey get_col_key() const noexcept override
471472
{

src/realm/obj.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,16 @@ CollectionPtr Obj::get_collection_ptr(const Path& path) const
20902090
return collection;
20912091
}
20922092

2093+
void Obj::translate_path(const StablePath& stable_path, Path& path) const
2094+
{
2095+
ColKey col_key = m_table->get_column_key(stable_path[0]);
2096+
path.emplace_back(m_table->get_column_name(col_key));
2097+
if (stable_path.size() > 1) {
2098+
CollectionBasePtr collection = get_collection_ptr(col_key);
2099+
dynamic_cast<CollectionParent*>(collection.get())->translate_path(stable_path, path);
2100+
}
2101+
}
2102+
20932103
CollectionPtr Obj::get_collection_by_stable_path(const StablePath& path) const
20942104
{
20952105
// First element in path is phony column key

src/realm/obj.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class Obj {
7272
Path get_short_path() const noexcept;
7373
ColKey get_col_key() const noexcept;
7474
StablePath get_stable_path() const noexcept;
75+
void translate_path(const StablePath&, Path&) const;
7576
void add_index(Path& path, const CollectionParent::Index& ndx) const;
7677

7778
TableRef get_table() const noexcept
@@ -441,6 +442,10 @@ class ObjCollectionParent final : public Obj, public CollectionParent {
441442
{
442443
return Obj::get_stable_path();
443444
}
445+
void translate_path(const StablePath& stable_path, Path& path) const override
446+
{
447+
Obj::translate_path(stable_path, path);
448+
}
444449
void add_index(Path& path, const Index& ndx) const override
445450
{
446451
Obj::add_index(path, ndx);

src/realm/object-store/binding_context.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class BindingContext {
165165
// Populated with information about which columns were changed
166166
// May be shorter than the actual number of columns if the later columns
167167
// are not modified
168-
std::unordered_map<int64_t, ColumnInfo> changes;
168+
std::unordered_map<ColKey, ColumnInfo> changes;
169169

170170
// Simple lexographic ordering
171171
friend bool operator<(ObserverState const& lft, ObserverState const& rgt)

src/realm/object-store/c_api/notifications.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
113113
return changes->columns.size();
114114
}
115115

116+
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t* changes)
117+
{
118+
return changes->modified_paths.size();
119+
}
120+
116121
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t* changes,
117122
realm_property_key_t* out_properties, size_t max)
118123
{
@@ -130,6 +135,39 @@ RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_c
130135
return i;
131136
}
132137

138+
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t* const_changes,
139+
realm_string_t* out_paths, size_t max)
140+
{
141+
if (!out_paths)
142+
return const_changes->modified_paths.size();
143+
144+
realm_object_changes_t* changes = const_cast<realm_object_changes_t*>(const_changes);
145+
changes->path_buffer.resize(changes->modified_paths.size());
146+
size_t i = 0;
147+
for (const auto& p : changes->modified_paths) {
148+
if (i >= max) {
149+
break;
150+
}
151+
std::string& path = changes->path_buffer[i];
152+
path = p[0].get_key();
153+
for (auto path_elem = p.begin() + 1; path_elem != p.end(); ++path_elem) {
154+
if (path_elem->is_key()) {
155+
path += '.';
156+
path += path_elem->get_key();
157+
}
158+
else {
159+
char buffer[10];
160+
sprintf(buffer, "[%u]", unsigned(path_elem->get_ndx()));
161+
path += buffer;
162+
}
163+
}
164+
out_paths[i].data = path.data();
165+
out_paths[i].size = path.size();
166+
++i;
167+
}
168+
return i;
169+
}
170+
133171
RLM_API realm_notification_token_t* realm_list_add_notification_callback(realm_list_t* list,
134172
realm_userdata_t userdata,
135173
realm_free_userdata_func_t free,

src/realm/object-store/c_api/types.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ struct realm_object_changes : realm::c_api::WrapC, realm::CollectionChangeSet {
428428
{
429429
return new realm_object_changes{static_cast<const realm::CollectionChangeSet&>(*this)};
430430
}
431+
std::vector<std::string> path_buffer;
431432
};
432433

433434
struct realm_collection_changes : realm::c_api::WrapC, realm::CollectionChangeSet {

src/realm/object-store/collection_notifications.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ struct CollectionChangeSet {
105105

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

109-
std::set<StableIndex> paths;
110+
std::set<StableIndex> stable_indexes;
110111

111112
bool empty() const noexcept
112113
{

src/realm/object-store/impl/collection_change_builder.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -704,5 +704,5 @@ CollectionChangeSet CollectionChangeBuilder::finalize() &&
704704

705705
return {std::move(deletions), std::move(insertions), std::move(modifications_in_old),
706706
std::move(modifications), std::move(moves), collection_root_was_deleted,
707-
collection_was_cleared, std::move(columns)};
707+
collection_was_cleared, std::move(columns), std::move(modified_paths)};
708708
}

src/realm/object-store/impl/list_notifier.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ void ListNotifier::run()
132132

133133
// Modifications to nested values in Mixed are recorded in replication as
134134
// StableIndex and we have to look up the actual index afterwards
135-
if (m_change.paths.size()) {
135+
if (m_change.stable_indexes.size()) {
136136
REALM_ASSERT(m_collection_parent);
137137
REALM_ASSERT(m_type == PropertyType::Mixed);
138-
for (auto& p : m_change.paths) {
138+
for (auto& p : m_change.stable_indexes) {
139139
if (auto ndx = m_collection_parent->find_index(p); ndx != realm::not_found)
140140
m_change.modifications.add(ndx);
141141
}

src/realm/object-store/impl/object_notifier.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,19 @@ void ObjectNotifier::run()
100100

101101
const auto& change = it->second;
102102

103-
auto column_modifications = change.get_columns_modified(m_obj_key);
104-
if (!column_modifications)
103+
auto path_modifications = change.get_paths_modified(m_obj_key);
104+
if (!path_modifications)
105105
return;
106106

107107
// Finally we add all changes to `m_change` which is later used to notify about the changed columns.
108108
m_change.modifications.add(0);
109-
for (auto col : *column_modifications) {
110-
m_change.columns[col.value].add(0);
109+
auto obj = m_table->get_object(m_obj_key);
110+
for (const StablePath& stable_path : *path_modifications) {
111+
m_change.columns[m_table->get_column_key(stable_path[0]).value].add(0);
112+
if (stable_path.size() > 1) {
113+
Path path;
114+
obj.translate_path(stable_path, path);
115+
m_change.modified_paths.emplace_back(std::move(path));
116+
}
111117
}
112118
}

src/realm/object-store/impl/results_notifier.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,9 @@ void ListResultsNotifier::run()
375375

376376
// Modifications to nested values in Mixed are recorded in replication as
377377
// StableIndex and we have to look up the actual index afterwards
378-
if (m_change.paths.size()) {
378+
if (m_change.stable_indexes.size()) {
379379
if (auto coll = dynamic_cast<CollectionParent*>(m_list.get())) {
380-
for (auto& p : m_change.paths) {
380+
for (auto& p : m_change.stable_indexes) {
381381
if (auto ndx = coll->find_index(p); ndx != realm::not_found)
382382
m_change.modifications.add(ndx);
383383
}

src/realm/object-store/impl/transact_log_handler.cpp

+16-11
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ void KVOAdapter::before(Transaction& sg)
110110
m_invalidated.push_back(observer.info);
111111
continue;
112112
}
113-
auto column_modifications = table.get_columns_modified(key);
114-
if (column_modifications) {
115-
for (auto col : *column_modifications) {
116-
observer.changes[col.value].kind = BindingContext::ColumnInfo::Kind::Set;
113+
auto tbl = sg.get_table(observer.table_key);
114+
if (auto path_modifications = table.get_paths_modified(key)) {
115+
for (const StablePath& path : *path_modifications) {
116+
observer.changes[tbl->get_column_key(path[0])].kind = BindingContext::ColumnInfo::Kind::Set;
117117
}
118118
}
119119
}
@@ -123,7 +123,7 @@ void KVOAdapter::before(Transaction& sg)
123123
// We may have pre-emptively marked the column as modified if the
124124
// LinkList was selected but the actual changes made ended up being
125125
// a no-op
126-
list.observer->changes.erase(list.col_key.value);
126+
list.observer->changes.erase(list.col_key);
127127
continue;
128128
}
129129
// If the containing row was deleted then changes will be empty
@@ -132,7 +132,7 @@ void KVOAdapter::before(Transaction& sg)
132132
continue;
133133
}
134134
// otherwise the column should have been marked as modified
135-
auto it = list.observer->changes.find(list.col_key.value);
135+
auto it = list.observer->changes.find(list.col_key);
136136
REALM_ASSERT(it != list.observer->changes.end());
137137
auto& builder = list.builder;
138138
auto& changes = it->second;
@@ -364,15 +364,17 @@ class TransactLogObserver : public TransactLogValidationMixin {
364364
return true;
365365
}
366366

367-
bool select_collection(ColKey col, ObjKey obj, const StablePath& path)
367+
bool select_collection(ColKey, ObjKey obj, const StablePath& path)
368368
{
369-
modify_object(col, obj);
369+
if (m_active_table) {
370+
m_active_table->modifications_add(obj, path);
371+
}
370372
auto table = current_table();
371373
m_active_collection = nullptr;
372374
for (auto& c : m_info.collections) {
373375
if (c.table_key == table && c.obj_key == obj && c.path.is_prefix_of(path)) {
374376
if (c.path.size() != path.size()) {
375-
c.changes->paths.insert(path[c.path.size()]);
377+
c.changes->stable_indexes.insert(path[c.path.size()]);
376378
}
377379
// If there are multiple exact matches for this collection we
378380
// use the first and then propagate the data to the others later
@@ -463,8 +465,11 @@ class TransactLogObserver : public TransactLogValidationMixin {
463465

464466
bool modify_object(ColKey col, ObjKey key)
465467
{
466-
if (m_active_table)
467-
m_active_table->modifications_add(key, col);
468+
if (m_active_table) {
469+
StablePath path;
470+
path.push_back(StableIndex(col, 0));
471+
m_active_table->modifications_add(key, path);
472+
}
468473
return true;
469474
}
470475

0 commit comments

Comments
 (0)