• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / nicola.cabiddu_1040

26 Sep 2023 05:08PM UTC coverage: 91.056% (-1.9%) from 92.915%
nicola.cabiddu_1040

Pull #6766

Evergreen

nicola-cab
several fixes and final client reset algo for collection in mixed
Pull Request #6766: Client Reset for collections in mixed / nested collections

97128 of 178458 branches covered (0.0%)

1524 of 1603 new or added lines in 5 files covered. (95.07%)

4511 existing lines in 109 files now uncovered.

236619 of 259862 relevant lines covered (91.06%)

7169640.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.32
/src/realm/replication.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/replication.hpp>
20
#include <realm/util/logger.hpp>
21

22
#include <realm/list.hpp>
23
#include <realm/path.hpp>
24
#include <iostream>
25

26
using namespace realm;
27
using namespace realm::util;
28

29
const char* Replication::history_type_name(int type)
30
{
18✔
31
    switch (type) {
18✔
32
        case hist_None:
✔
UNCOV
33
            return "None";
×
UNCOV
34
        case hist_OutOfRealm:
✔
UNCOV
35
            return "Local out of Realm";
×
36
        case hist_InRealm:
12✔
37
            return "Local in-Realm";
12✔
38
        case hist_SyncClient:
6✔
39
            return "SyncClient";
6✔
40
        case hist_SyncServer:
✔
UNCOV
41
            return "SyncServer";
×
UNCOV
42
        default:
✔
UNCOV
43
            return "Unknown";
×
44
    }
18✔
45
}
18✔
46

47
void Replication::initialize(DB&)
48
{
140,208✔
49
    // Nothing needs to be done here
68,928✔
50
}
140,208✔
51

52
void Replication::do_initiate_transact(Group&, version_type, bool)
53
{
1,373,250✔
54
    char* data = m_stream.get_data();
1,373,250✔
55
    size_t size = m_stream.get_size();
1,373,250✔
56
    m_encoder.set_buffer(data, data + size);
1,373,250✔
57
}
1,373,250✔
58

59
Replication::version_type Replication::prepare_commit(version_type orig_version)
60
{
1,343,457✔
61
    char* data = m_stream.get_data();
1,343,457✔
62
    size_t size = m_encoder.write_position() - data;
1,343,457✔
63
    version_type new_version = prepare_changeset(data, size, orig_version); // Throws
1,343,457✔
64
    return new_version;
1,343,457✔
65
}
1,343,457✔
66

67
void Replication::add_class(TableKey table_key, StringData name, Table::Type type)
68
{
202,476✔
69
    if (auto logger = get_logger()) {
202,476✔
70
        if (type == Table::Type::Embedded) {
162,408✔
71
            logger->log(util::Logger::Level::debug, "Add %1 class '%2'", type, name);
19,125✔
72
        }
19,125✔
73
        else {
143,283✔
74
            logger->log(util::Logger::Level::debug, "Add class '%1'", name);
143,283✔
75
        }
143,283✔
76
    }
162,408✔
77
    unselect_all();
202,476✔
78
    m_encoder.insert_group_level_table(table_key); // Throws
202,476✔
79
}
202,476✔
80

81
void Replication::add_class_with_primary_key(TableKey tk, StringData name, DataType pk_type, StringData pk_name, bool,
82
                                             Table::Type table_type)
83
{
129,822✔
84
    if (auto logger = get_logger()) {
129,822✔
85
        logger->log(util::Logger::Level::debug, "Add %1 class '%2' with primary key property '%3' of %4", table_type,
100,581✔
86
                    Group::table_name_to_class_name(name), pk_name, pk_type);
100,581✔
87
    }
100,581✔
88
    REALM_ASSERT(table_type != Table::Type::Embedded);
129,822✔
89
    unselect_all();
129,822✔
90
    m_encoder.insert_group_level_table(tk); // Throws
129,822✔
91
}
129,822✔
92

93
void Replication::erase_class(TableKey tk, StringData table_name, size_t)
94
{
7,704✔
95
    if (auto logger = get_logger()) {
7,704✔
96
        logger->log(util::Logger::Level::debug, "Remove class '%1'", Group::table_name_to_class_name(table_name));
24✔
97
    }
24✔
98
    unselect_all();
7,704✔
99
    m_encoder.erase_class(tk); // Throws
7,704✔
100
}
7,704✔
101

102

103
void Replication::insert_column(const Table* t, ColKey col_key, DataType type, StringData col_name,
104
                                Table* target_table)
105
{
750,858✔
106
    if (auto logger = get_logger()) {
750,858✔
107
        const char* collection_type = "";
697,740✔
108
        if (col_key.is_collection()) {
697,740✔
109
            if (col_key.is_list()) {
209,739✔
110
                collection_type = "list ";
94,872✔
111
            }
94,872✔
112
            else if (col_key.is_dictionary()) {
114,867✔
113
                collection_type = "dictionary ";
52,692✔
114
            }
52,692✔
115
            else {
62,175✔
116
                collection_type = "set ";
62,175✔
117
            }
62,175✔
118
        }
209,739✔
119
        if (target_table) {
697,740✔
120
            logger->log(util::Logger::Level::debug, "On class '%1': Add property '%2' %3linking '%4'",
75,039✔
121
                        t->get_class_name(), col_name, collection_type, target_table->get_class_name());
75,039✔
122
        }
75,039✔
123
        else {
622,701✔
124
            logger->log(util::Logger::Level::debug, "On class '%1': Add property '%2' %3of %4", t->get_class_name(),
622,701✔
125
                        col_name, collection_type, type);
622,701✔
126
        }
622,701✔
127
    }
697,740✔
128
    select_table(t);                  // Throws
750,858✔
129
    m_encoder.insert_column(col_key); // Throws
750,858✔
130
}
750,858✔
131

132
void Replication::erase_column(const Table* t, ColKey col_key)
133
{
519✔
134
    if (auto logger = get_logger()) {
519✔
135
        logger->log(util::Logger::Level::debug, "On class '%1': Remove property '%2'", t->get_class_name(),
438✔
136
                    t->get_column_name(col_key));
438✔
137
    }
438✔
138
    select_table(t);                 // Throws
519✔
139
    m_encoder.erase_column(col_key); // Throws
519✔
140
}
519✔
141

142
void Replication::create_object(const Table* t, GlobalKey id)
143
{
4,305,000✔
144
    if (auto logger = get_logger()) {
4,305,000✔
145
        logger->log(util::Logger::Level::debug, "Create object '%1'", t->get_class_name());
2,622,783✔
146
    }
2,622,783✔
147
    select_table(t);                              // Throws
4,305,000✔
148
    m_encoder.create_object(id.get_local_key(0)); // Throws
4,305,000✔
149
}
4,305,000✔
150

151
void Replication::create_object_with_primary_key(const Table* t, ObjKey key, Mixed pk)
152
{
448,239✔
153
    if (auto logger = get_logger()) {
448,239✔
154
        logger->log(util::Logger::Level::debug, "Create object '%1' with primary key %2", t->get_class_name(), pk);
191,094✔
155
    }
191,094✔
156
    select_table(t);              // Throws
448,239✔
157
    m_encoder.create_object(key); // Throws
448,239✔
158
}
448,239✔
159

160
void Replication::remove_object(const Table* t, ObjKey key)
161
{
5,009,175✔
162
    if (auto logger = get_logger()) {
5,009,175✔
163
        if (t->is_embedded()) {
2,459,265✔
164
            logger->log(util::Logger::Level::debug, "Remove embedded object '%1'", t->get_class_name());
8,817✔
165
        }
8,817✔
166
        else if (t->get_primary_key_column()) {
2,450,448✔
167
            logger->log(util::Logger::Level::debug, "Remove object '%1' with primary key %2", t->get_class_name(),
42,900✔
168
                        t->get_primary_key(key));
42,900✔
169
        }
42,900✔
170
        else {
2,407,548✔
171
            logger->log(util::Logger::Level::debug, "Remove object '%1'[%2]", t->get_class_name(), key);
2,407,548✔
172
        }
2,407,548✔
173
    }
2,459,265✔
174
    select_table(t);              // Throws
5,009,175✔
175
    m_encoder.remove_object(key); // Throws
5,009,175✔
176
}
5,009,175✔
177

178
inline void Replication::select_obj(ObjKey key)
179
{
16,711,974✔
180
    if (key == m_selected_obj) {
16,711,974✔
181
        return;
2,942,757✔
182
    }
2,942,757✔
183
    if (auto logger = get_logger()) {
13,769,217✔
184
        if (logger->would_log(util::Logger::Level::debug)) {
2,972,028✔
185
            auto class_name = m_selected_table->get_class_name();
2,970,528✔
186
            if (m_selected_table->get_primary_key_column()) {
2,970,528✔
187
                auto pk = m_selected_table->get_primary_key(key);
168,063✔
188
                logger->log(util::Logger::Level::debug, "Mutating object '%1' with primary key %2", class_name, pk);
168,063✔
189
            }
168,063✔
190
            else if (m_selected_table->is_embedded()) {
2,802,465✔
191
                auto obj = m_selected_table->get_object(key);
34,440✔
192
                logger->log(util::Logger::Level::debug, "Mutating object '%1' with path '%2'", class_name,
34,440✔
193
                            obj.get_id());
34,440✔
194
            }
34,440✔
195
            else {
2,768,025✔
196
                logger->log(util::Logger::Level::debug, "Mutating anonymous object '%1'[%2]", class_name, key);
2,768,025✔
197
            }
2,768,025✔
198
        }
2,970,528✔
199
    }
2,972,028✔
200
    m_selected_obj = key;
13,769,217✔
201
}
13,769,217✔
202

203
void Replication::do_set(const Table* t, ColKey col_key, ObjKey key, _impl::Instruction variant)
204
{
16,445,823✔
205
    if (variant != _impl::Instruction::instr_SetDefault) {
16,445,823✔
206
        select_table(t); // Throws
16,444,317✔
207
        select_obj(key);
16,444,317✔
208
        m_encoder.modify_object(col_key, key); // Throws
16,444,317✔
209
    }
16,444,317✔
210
}
16,445,823✔
211

212
void Replication::set(const Table* t, ColKey col_key, ObjKey key, Mixed value, _impl::Instruction variant)
213
{
16,433,145✔
214
    do_set(t, col_key, key, variant); // Throws
16,433,145✔
215
    if (auto logger = get_logger()) {
16,433,145✔
216
        if (logger->would_log(util::Logger::Level::trace)) {
3,113,199✔
217
            if (col_key.get_type() == col_type_Link && value.is_type(type_Link)) {
36!
UNCOV
218
                auto target_table = t->get_opposite_table(col_key);
×
UNCOV
219
                if (target_table->is_embedded()) {
×
UNCOV
220
                    logger->log(util::Logger::Level::trace, "   Creating embedded object '%1' in '%2'",
×
UNCOV
221
                                target_table->get_class_name(), t->get_column_name(col_key));
×
UNCOV
222
                }
×
UNCOV
223
                else if (target_table->get_primary_key_column()) {
×
UNCOV
224
                    auto link = value.get<ObjKey>();
×
UNCOV
225
                    auto pk = target_table->get_primary_key(link);
×
UNCOV
226
                    logger->log(util::Logger::Level::trace, "   Linking object '%1' with primary key %2 from '%3'",
×
UNCOV
227
                                target_table->get_class_name(), pk, t->get_column_name(col_key));
×
UNCOV
228
                }
×
UNCOV
229
                else {
×
UNCOV
230
                    logger->log(util::Logger::Level::trace, "   Linking object '%1'[%2] from '%3'",
×
UNCOV
231
                                target_table->get_class_name(), key, t->get_column_name(col_key));
×
UNCOV
232
                }
×
UNCOV
233
            }
×
234
            else {
36✔
235
                logger->log(util::Logger::Level::trace, "   Set '%1' to %2", t->get_column_name(col_key),
36✔
236
                            value.to_string(util::Logger::max_width_of_value));
36✔
237
            }
36✔
238
        }
36✔
239
    }
3,113,199✔
240
}
16,433,145✔
241

242
void Replication::nullify_link(const Table* t, ColKey col_key, ObjKey key)
243
{
759✔
244
    select_table(t); // Throws
759✔
245
    select_obj(key);
759✔
246
    m_encoder.modify_object(col_key, key); // Throws
759✔
247
    if (auto logger = get_logger()) {
759✔
248
        logger->log(util::Logger::Level::trace, "   Nullify '%1'", t->get_column_name(col_key));
669✔
249
    }
669✔
250
}
759✔
251

252
void Replication::add_int(const Table* t, ColKey col_key, ObjKey key, int_fast64_t value)
253
{
10,626✔
254
    do_set(t, col_key, key); // Throws
10,626✔
255
    if (auto logger = get_logger()) {
10,626✔
256
        logger->log(util::Logger::Level::trace, "   Adding %1 to '%2'", value, t->get_column_name(col_key));
90✔
257
    }
90✔
258
}
10,626✔
259

260

261
Path Replication::get_prop_name(Path&& path) const
262
{
32,457✔
263
    Path ret(std::move(path));
32,457✔
264
    auto col_key = ret[0].get_col_key();
32,457✔
265
    auto prop_name = m_selected_table->get_column_name(col_key);
32,457✔
266
    ret[0] = PathElement(prop_name);
32,457✔
267
    return ret;
32,457✔
268
}
32,457✔
269

270
void Replication::log_collection_operation(const char* operation, const CollectionBase& collection, Mixed value,
271
                                           Mixed index) const
272
{
18✔
273
    auto logger = get_logger();
18✔
274
    auto path = collection.get_short_path();
18✔
275
    auto col_key = path[0].get_col_key();
18✔
276
    auto prop_name = m_selected_table->get_column_name(col_key);
18✔
277
    path[0] = PathElement(prop_name);
18✔
278
    std::string position;
18✔
279
    if (!index.is_null()) {
18✔
280
        position = util::format(" at position %1", index);
12✔
281
    }
12✔
282
    if (Table::is_link_type(col_key.get_type()) && value.is_type(type_Link)) {
18!
UNCOV
283
        auto target_table = m_selected_table->get_opposite_table(col_key);
×
UNCOV
284
        if (target_table->is_embedded()) {
×
UNCOV
285
            logger->log(util::Logger::Level::trace, "   %1 embedded object '%2' in %3%4 ", operation,
×
UNCOV
286
                        target_table->get_class_name(), path, position);
×
UNCOV
287
        }
×
UNCOV
288
        else if (target_table->get_primary_key_column()) {
×
UNCOV
289
            auto link = value.get<ObjKey>();
×
UNCOV
290
            auto pk = target_table->get_primary_key(link);
×
UNCOV
291
            logger->log(util::Logger::Level::trace, "   %1 object '%2' with primary key %3 in %4%5", operation,
×
UNCOV
292
                        target_table->get_class_name(), pk, path, position);
×
UNCOV
293
        }
×
UNCOV
294
        else {
×
UNCOV
295
            auto link = value.get<ObjKey>();
×
UNCOV
296
            logger->log(util::Logger::Level::trace, "   %1 object '%2'[%3] in %4%5", operation,
×
UNCOV
297
                        target_table->get_class_name(), link, path, position);
×
UNCOV
298
        }
×
UNCOV
299
    }
×
300
    else {
18✔
301
        logger->log(util::Logger::Level::trace, "   %1 %2 in %3%4", operation,
18✔
302
                    value.to_string(util::Logger::max_width_of_value), path, position);
18✔
303
    }
18✔
304
}
18✔
305
void Replication::list_insert(const CollectionBase& list, size_t list_ndx, Mixed value, size_t)
306
{
1,165,473✔
307
    select_collection(list);                                     // Throws
1,165,473✔
308
    m_encoder.collection_insert(list.translate_index(list_ndx)); // Throws
1,165,473✔
309
    if (auto logger = get_logger()) {
1,165,473✔
310
        if (logger->would_log(util::Logger::Level::trace)) {
519,606✔
311
            log_collection_operation("Insert", list, value, int64_t(list_ndx));
6✔
312
        }
6✔
313
    }
519,606✔
314
}
1,165,473✔
315

316
void Replication::list_set(const CollectionBase& list, size_t list_ndx, Mixed value)
317
{
40,290✔
318
    select_collection(list);                                  // Throws
40,290✔
319
    m_encoder.collection_set(list.translate_index(list_ndx)); // Throws
40,290✔
320
    if (auto logger = get_logger()) {
40,290✔
321
        if (logger->would_log(util::Logger::Level::trace)) {
8,946✔
UNCOV
322
            log_collection_operation("Set", list, value, int64_t(list_ndx));
×
UNCOV
323
        }
×
324
    }
8,946✔
325
}
40,290✔
326

327
void Replication::list_erase(const CollectionBase& list, size_t link_ndx)
328
{
208,500✔
329
    select_collection(list);                                    // Throws
208,500✔
330
    m_encoder.collection_erase(list.translate_index(link_ndx)); // Throws
208,500✔
331
    if (auto logger = get_logger()) {
208,500✔
332
        logger->log(util::Logger::Level::trace, "   Erase '%1' at position %2", get_prop_name(list.get_short_path()),
6,654✔
333
                    link_ndx);
6,654✔
334
    }
6,654✔
335
}
208,500✔
336

337
void Replication::list_move(const CollectionBase& list, size_t from_link_ndx, size_t to_link_ndx)
338
{
1,638✔
339
    select_collection(list);                                                                           // Throws
1,638✔
340
    m_encoder.collection_move(list.translate_index(from_link_ndx), list.translate_index(to_link_ndx)); // Throws
1,638✔
341
    if (auto logger = get_logger()) {
1,638✔
342
        logger->log(util::Logger::Level::trace, "   Move %1 to %2 in '%3'", from_link_ndx, to_link_ndx,
1,512✔
343
                    get_prop_name(list.get_short_path()));
1,512✔
344
    }
1,512✔
345
}
1,638✔
346

347
void Replication::set_insert(const CollectionBase& set, size_t set_ndx, Mixed value)
348
{
60,720✔
349
    select_collection(set);               // Throws
60,720✔
350
    m_encoder.collection_insert(set_ndx); // Throws
60,720✔
351
    if (auto logger = get_logger()) {
60,720✔
352
        if (logger->would_log(util::Logger::Level::trace)) {
59,796✔
353
            log_collection_operation("Insert", set, value, Mixed());
6✔
354
        }
6✔
355
    }
59,796✔
356
}
60,720✔
357

358
void Replication::set_erase(const CollectionBase& set, size_t set_ndx, Mixed value)
359
{
11,562✔
360
    select_collection(set);              // Throws
11,562✔
361
    m_encoder.collection_erase(set_ndx); // Throws
11,562✔
362
    if (auto logger = get_logger()) {
11,562✔
363
        logger->log(util::Logger::Level::trace, "   Erase %1 from '%2'", value, get_prop_name(set.get_short_path()));
11,412✔
364
    }
11,412✔
365
}
11,562✔
366

367
void Replication::set_clear(const CollectionBase& set)
368
{
2,430✔
369
    select_collection(set);                 // Throws
2,430✔
370
    m_encoder.collection_clear(set.size()); // Throws
2,430✔
371
    if (auto logger = get_logger()) {
2,430✔
372
        logger->log(util::Logger::Level::trace, "   Clear '%1'", get_prop_name(set.get_short_path()));
2,346✔
373
    }
2,346✔
374
}
2,430✔
375

376
void Replication::do_select_table(const Table* table)
377
{
1,393,965✔
378
    m_encoder.select_table(table->get_key()); // Throws
1,393,965✔
379
    m_selected_table = table;
1,393,965✔
380
    m_selected_list = CollectionId();
1,393,965✔
381
    m_selected_obj = ObjKey();
1,393,965✔
382
}
1,393,965✔
383

384
void Replication::do_select_collection(const CollectionBase& list)
385
{
269,862✔
386
    select_table(list.get_table().unchecked_ptr());
269,862✔
387
    ColKey col_key = list.get_col_key();
269,862✔
388
    ObjKey key = list.get_owner_key();
269,862✔
389
    auto path = list.get_stable_path();
269,862✔
390

137,055✔
391
    select_obj(key);
269,862✔
392

137,055✔
393
    m_encoder.select_collection(col_key, key, path); // Throws
269,862✔
394
    m_selected_list = CollectionId(list.get_table()->get_key(), key, std::move(path));
269,862✔
395
}
269,862✔
396

397
void Replication::list_clear(const CollectionBase& list)
398
{
4,578✔
399
    select_collection(list);           // Throws
4,578✔
400
    m_encoder.collection_clear(list.size()); // Throws
4,578✔
401
    if (auto logger = get_logger()) {
4,578✔
402
        logger->log(util::Logger::Level::trace, "   Clear '%1'", get_prop_name(list.get_short_path()));
3,897✔
403
    }
3,897✔
404
}
4,578✔
405

406
void Replication::link_list_nullify(const Lst<ObjKey>& list, size_t link_ndx)
407
{
1,716✔
408
    select_collection(list);
1,716✔
409
    m_encoder.collection_erase(link_ndx);
1,716✔
410
    if (auto logger = get_logger()) {
1,716✔
411
        logger->log(util::Logger::Level::trace, "   Nullify '%1' position %2",
1,656✔
412
                    m_selected_table->get_column_name(list.get_col_key()), link_ndx);
1,656✔
413
    }
1,656✔
414
}
1,716✔
415

416
void Replication::dictionary_insert(const CollectionBase& dict, size_t ndx, Mixed key, Mixed value)
417
{
84,264✔
418
    select_collection(dict);
84,264✔
419
    m_encoder.collection_insert(ndx);
84,264✔
420
    if (auto logger = get_logger()) {
84,264✔
421
        if (logger->would_log(util::Logger::Level::trace)) {
70,584✔
422
            log_collection_operation("Insert", dict, value, key);
6✔
423
        }
6✔
424
    }
70,584✔
425
}
84,264✔
426

427
void Replication::dictionary_set(const CollectionBase& dict, size_t ndx, Mixed key, Mixed value)
428
{
8,328✔
429
    select_collection(dict);
8,328✔
430
    m_encoder.collection_set(ndx);
8,328✔
431
    if (auto logger = get_logger()) {
8,328✔
432
        if (logger->would_log(util::Logger::Level::trace)) {
8,178✔
UNCOV
433
            log_collection_operation("Set", dict, value, key);
×
UNCOV
434
        }
×
435
    }
8,178✔
436
}
8,328✔
437

438
void Replication::dictionary_erase(const CollectionBase& dict, size_t ndx, Mixed key)
439
{
11,952✔
440
    select_collection(dict);
11,952✔
441
    m_encoder.collection_erase(ndx);
11,952✔
442
    if (auto logger = get_logger()) {
11,952✔
443
        logger->log(util::Logger::Level::trace, "   Erase %1 from '%2'", key, get_prop_name(dict.get_short_path()));
4,332✔
444
    }
4,332✔
445
}
11,952✔
446

447
void Replication::dictionary_clear(const CollectionBase& dict)
448
{
2,352✔
449
    select_collection(dict);
2,352✔
450
    m_encoder.collection_clear(dict.size());
2,352✔
451
    if (auto logger = get_logger()) {
2,352✔
452
        logger->log(util::Logger::Level::trace, "   Clear '%1'", get_prop_name(dict.get_short_path()));
2,304✔
453
    }
2,304✔
454
}
2,352✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc