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

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

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

91.7
/src/realm/object-store/object_accessor.hpp
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
#ifndef REALM_OS_OBJECT_ACCESSOR_HPP
20
#define REALM_OS_OBJECT_ACCESSOR_HPP
21

22
#include <realm/object-store/object.hpp>
23

24
#include <realm/object-store/feature_checks.hpp>
25
#include <realm/object-store/list.hpp>
26
#include <realm/object-store/dictionary.hpp>
27
#include <realm/object-store/set.hpp>
28
#include <realm/object-store/dictionary.hpp>
29
#include <realm/object-store/object_schema.hpp>
30
#include <realm/object-store/object_store.hpp>
31
#include <realm/object-store/results.hpp>
32
#include <realm/object-store/schema.hpp>
33
#include <realm/object-store/shared_realm.hpp>
34

35
#include <realm/util/assert.hpp>
36
#include <realm/util/optional.hpp>
37
#include <realm/table_view.hpp>
38

39
#include <string>
40

41
namespace realm {
42
template <typename ValueType, typename ContextType>
43
void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueType value, CreatePolicy policy)
44
{
192✔
45
    auto& property = property_for_name(prop_name);
192✔
46
    validate_property_for_setter(property);
192✔
47
    set_property_value_impl(ctx, property, value, policy, false);
192✔
48
}
192✔
49

50
template <typename ValueType, typename ContextType>
51
void Object::set_property_value(ContextType& ctx, const Property& property, ValueType value, CreatePolicy policy)
52
{
1✔
53
    set_property_value_impl(ctx, property, value, policy, false);
1✔
54
}
1✔
55

56
template <typename ValueType, typename ContextType>
57
ValueType Object::get_property_value(ContextType& ctx, const Property& property) const
58
{
1✔
59
    return get_property_value_impl<ValueType>(ctx, property);
1✔
60
}
1✔
61

62
template <typename ValueType, typename ContextType>
63
ValueType Object::get_property_value(ContextType& ctx, StringData prop_name) const
64
{
289✔
65
    return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
289✔
66
}
289✔
67

68
namespace {
69
template <typename ValueType, typename ContextType>
70
struct ValueUpdater {
71
    ContextType& ctx;
72
    Property const& property;
73
    ValueType& value;
74
    Obj& obj;
75
    ColKey col;
76
    CreatePolicy policy;
77
    bool is_default;
78

79
    void operator()(Obj*)
80
    {
133✔
81
        ContextType child_ctx(ctx, obj, property);
133✔
82
        auto policy2 = policy;
133✔
83
        policy2.create = false;
133✔
84
        auto link = child_ctx.template unbox<Obj>(value, policy2);
133✔
85
        if (!policy.copy && link && link.get_table()->is_embedded())
133!
86
            throw InvalidArgument("Cannot set a link to an existing managed embedded object");
1✔
87

88
        ObjKey curr_link;
132✔
89
        if (policy.diff)
132!
90
            curr_link = obj.get<ObjKey>(col);
7✔
91
        if (!link || link.get_table()->is_embedded())
132!
92
            link = child_ctx.template unbox<Obj>(value, policy, curr_link);
73✔
93
        if (!policy.diff || curr_link != link.get_key()) {
132!
94
            if (!link || !link.get_table()->is_embedded())
125!
95
                obj.set(col, link.get_key());
100✔
96
        }
125✔
97
    }
132✔
98

99
    template <typename T>
100
    void operator()(T*)
101
    {
10,037✔
102
        bool attr_changed = !policy.diff;
10,037✔
103
        auto new_val = ctx.template unbox<T>(value, policy);
10,037✔
104

105
        if (!attr_changed) {
10,037!
106
            auto old_val = obj.get<T>(col);
87✔
107

108
            if constexpr (std::is_same<T, realm::Mixed>::value) {
87✔
109
                attr_changed = !new_val.is_same_type(old_val);
×
110
            }
×
111

112
            attr_changed = attr_changed || new_val != old_val;
87!
113
        }
87✔
114

115
        if (attr_changed)
10,037!
116
            obj.set(col, new_val, is_default);
9,970✔
117
    }
10,037✔
118
};
119
} // namespace
120

121
template <typename ValueType, typename ContextType>
122
void Object::set_property_value_impl(ContextType& ctx, const Property& property, ValueType value, CreatePolicy policy,
123
                                     bool is_default)
124
{
10,708✔
125
    ctx.will_change(*this, property);
10,708✔
126

127
    ColKey col{property.column_key};
10,708✔
128
    if (!is_collection(property.type) && is_nullable(property.type) && ctx.is_null(value)) {
10,708✔
129
        if (!policy.diff || !m_obj.is_null(col)) {
28!
130
            if (property.type == PropertyType::Object) {
28!
131
                if (!is_default)
4!
132
                    m_obj.set_null(col);
4✔
133
            }
4✔
134
            else {
24✔
135
                m_obj.set_null(col, is_default);
24✔
136
            }
24✔
137
        }
28✔
138

139
        ctx.did_change();
28✔
140
        return;
28✔
141
    }
28✔
142

143
    if (is_array(property.type)) {
10,680✔
144
        if (property.type == PropertyType::LinkingObjects)
416!
145
            throw ReadOnlyPropertyException(m_object_schema->name, property.name);
1✔
146

147
        ContextType child_ctx(ctx, m_obj, property);
415✔
148
        List list(m_realm, m_obj, col);
415✔
149
        list.assign(child_ctx, value, policy);
415✔
150
        ctx.did_change();
415✔
151
        return;
415✔
152
    }
416✔
153

154
    if (is_dictionary(property.type)) {
10,264✔
155
        ContextType child_ctx(ctx, m_obj, property);
48✔
156
        object_store::Dictionary dict(m_realm, m_obj, col);
48✔
157
        dict.assign(child_ctx, value, policy);
48✔
158
        ctx.did_change();
48✔
159
        return;
48✔
160
    }
48✔
161

162
    if (is_set(property.type)) {
10,216✔
163
        if (property.type == PropertyType::LinkingObjects)
×
164
            throw ReadOnlyPropertyException(m_object_schema->name, property.name);
×
165

166
        ContextType child_ctx(ctx, m_obj, property);
×
167
        object_store::Set set(m_realm, m_obj, col);
×
168
        set.assign(child_ctx, value, policy);
×
169
        ctx.did_change();
×
170
        return;
×
171
    }
×
172

173
    if (property.type == PropertyType::Mixed) {
10,216✔
174
        Mixed new_val = ctx.template unbox<Mixed>(value, policy);
46✔
175
        if (new_val.is_type(type_Dictionary)) {
46✔
176
            ContextType child_ctx(ctx, m_obj, property);
14✔
177
            m_obj.set_collection(col, CollectionType::Dictionary);
14✔
178
            object_store::Dictionary dict(m_realm, m_obj, col);
14✔
179
            dict.assign(child_ctx, value, policy);
14✔
180
            ctx.did_change();
14✔
181
            return;
14✔
182
        }
14✔
183
        if (new_val.is_type(type_List)) {
32✔
184
            ContextType child_ctx(ctx, m_obj, property);
6✔
185
            m_obj.set_collection(col, CollectionType::List);
6✔
186
            List list(m_realm, m_obj, col);
6✔
187
            list.assign(child_ctx, value, policy);
6✔
188
            ctx.did_change();
6✔
189
            return;
6✔
190
        }
6✔
191
        bool attr_changed = !policy.diff;
26✔
192

193
        if (!attr_changed) {
26✔
194
            auto old_val = m_obj.get<Mixed>(col);
9✔
195
            attr_changed = !new_val.is_same_type(old_val) || new_val != old_val;
9!
196
        }
9✔
197

198
        if (attr_changed)
26✔
199
            m_obj.set(col, new_val, is_default);
25✔
200
    }
26✔
201
    else {
10,170✔
202
        ValueUpdater<ValueType, ContextType> updater{ctx, property, value, m_obj, col, policy, is_default};
10,170✔
203
        switch_on_type(property.type, updater);
10,170✔
204
    }
10,170✔
205
    ctx.did_change();
10,196✔
206
}
10,196✔
207

208
template <typename ValueType, typename ContextType>
209
ValueType Object::get_property_value_impl(ContextType& ctx, const Property& property) const
210
{
285✔
211
    verify_attached();
285✔
212

213
    ColKey column{property.column_key};
285✔
214
    if (is_nullable(property.type) && m_obj.is_null(column))
285✔
215
        return ctx.null_value();
34✔
216
    if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
251✔
217
        return ctx.box(List(m_realm, m_obj, column));
101✔
218
    if (is_set(property.type) && property.type != PropertyType::LinkingObjects)
150!
219
        return ctx.box(object_store::Set(m_realm, m_obj, column));
×
220
    if (is_dictionary(property.type))
150✔
221
        return ctx.box(object_store::Dictionary(m_realm, m_obj, column));
2✔
222

223
    switch (property.type & ~PropertyType::Flags) {
148✔
224
        case PropertyType::Bool:
10✔
225
            return ctx.box(m_obj.get<bool>(column));
10✔
226
        case PropertyType::Int:
35✔
227
            return is_nullable(property.type) ? ctx.box(*m_obj.get<util::Optional<int64_t>>(column))
35✔
228
                                              : ctx.box(m_obj.get<int64_t>(column));
35✔
229
        case PropertyType::Float:
8✔
230
            return ctx.box(m_obj.get<float>(column));
8✔
231
        case PropertyType::Double:
8✔
232
            return ctx.box(m_obj.get<double>(column));
8✔
233
        case PropertyType::String:
11✔
234
            return ctx.box(m_obj.get<StringData>(column));
11✔
235
        case PropertyType::Data:
4✔
236
            return ctx.box(m_obj.get<BinaryData>(column));
4✔
237
        case PropertyType::Date:
8✔
238
            return ctx.box(m_obj.get<Timestamp>(column));
8✔
239
        case PropertyType::ObjectId:
8✔
240
            return is_nullable(property.type) ? ctx.box(m_obj.get<util::Optional<ObjectId>>(column))
8✔
241
                                              : ctx.box(m_obj.get<ObjectId>(column));
8✔
242
        case PropertyType::Decimal:
8✔
243
            return ctx.box(m_obj.get<Decimal>(column));
8✔
244
        case PropertyType::UUID:
5✔
245
            return is_nullable(property.type) ? ctx.box(m_obj.get<util::Optional<UUID>>(column))
5!
246
                                              : ctx.box(m_obj.get<UUID>(column));
5✔
247
        case PropertyType::Mixed: {
19✔
248
            Mixed value = m_obj.get<Mixed>(column);
19✔
249
            if (value.is_type(type_Dictionary)) {
19✔
250
                return ctx.box(object_store::Dictionary(m_realm, m_obj, column));
7✔
251
            }
7✔
252
            if (value.is_type(type_List)) {
12✔
253
                return ctx.box(List(m_realm, m_obj, column));
4✔
254
            }
4✔
255
            return ctx.box(value);
8✔
256
        }
12✔
257
        case PropertyType::Object: {
22✔
258
            auto linkObjectSchema = m_realm->schema().find(property.object_type);
22✔
259
            auto linked = const_cast<Obj&>(m_obj).get_linked_object(column);
22✔
260
            return ctx.box(Object(m_realm, *linkObjectSchema, linked, m_obj, column));
22✔
261
        }
12✔
262
        case PropertyType::LinkingObjects: {
2✔
263
            auto target_object_schema = m_realm->schema().find(property.object_type);
2✔
264
            auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
2✔
265
            return ctx.box(Results(m_realm, m_obj, target_object_schema->table_key, link_property->column_key));
2✔
266
        }
12✔
267
        default:
✔
268
            REALM_UNREACHABLE();
269
    }
148✔
270
}
148✔
271

272
template <typename ValueType, typename ContextType>
273
Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm, StringData object_type, ValueType value,
274
                      CreatePolicy policy, ObjKey current_obj, Obj* out_row)
275
{
7,544✔
276
    auto object_schema = realm->schema().find(object_type);
7,544✔
277
    REALM_ASSERT(object_schema != realm->schema().end());
7,544✔
278
    return create(ctx, realm, *object_schema, value, policy, current_obj, out_row);
7,544✔
279
}
7,544✔
280

281
template <typename ValueType, typename ContextType>
282
Mixed as_mixed(ContextType& ctx, ValueType& value, PropertyType type)
283
{
7,692✔
284
    if (!value)
7,692✔
285
        return {};
1✔
286
    return switch_on_type(type, [&](auto* t) {
7,691✔
287
        return Mixed(ctx.template unbox<NonObjTypeT<decltype(*t)>>(*value));
7,691✔
288
    });
7,691✔
289
}
7,692✔
290

291
template <typename ValueType, typename ContextType>
292
Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm, ObjectSchema const& object_schema,
293
                      ValueType value, CreatePolicy policy, ObjKey current_obj, Obj* out_row)
294
{
7,990✔
295
    realm->verify_in_write();
7,990✔
296

297
    // When setting each property, we normally want to skip over the primary key
298
    // as that's set as part of object creation. However, during migrations the
299
    // property marked as the primary key in the schema may not currently be
300
    // considered a primary key by core, and so will need to be set.
301
    bool skip_primary = true;
7,990✔
302
    // If the input value is missing values for any of the properties we want to
303
    // set the property to the default value for new objects, but leave it
304
    // untouched for existing objects.
305
    bool created = false;
7,990✔
306

307
    Obj obj;
7,990✔
308
    auto table = realm->read_group().get_table(object_schema.table_key);
7,990✔
309

310
    // Asymmetric objects cannot be updated through Object::create.
311
    if (object_schema.table_type == ObjectSchema::ObjectType::TopLevelAsymmetric) {
7,990✔
312
        REALM_ASSERT(!policy.update);
215!
313
        REALM_ASSERT(!current_obj);
215!
314
        REALM_ASSERT(object_schema.primary_key_property());
215!
315
    }
215✔
316

317
    // If there's a primary key, we need to first check if an object with the
318
    // same primary key already exists. If it does, we either update that object
319
    // or throw an exception if updating is disabled.
320
    if (auto primary_prop = object_schema.primary_key_property()) {
7,990✔
321
        auto primary_value =
7,732✔
322
            ctx.value_for_property(value, *primary_prop, primary_prop - &object_schema.persisted_properties[0]);
7,732✔
323
        if (!primary_value)
7,732✔
324
            primary_value = ctx.default_value_for_property(object_schema, *primary_prop);
4✔
325
        if (!primary_value && !is_nullable(primary_prop->type))
7,732!
326
            throw MissingPropertyValueException(object_schema.name, primary_prop->name);
×
327

328
        // When changing the primary key of a table, we remove the existing pk (if any), call
329
        // the migration function, then add the new pk (if any). This means that we can't call
330
        // create_object_with_primary_key(), and creating duplicate primary keys is allowed as
331
        // long as they're unique by the end of the migration.
332
        if (table->get_primary_key_column() == ColKey{}) {
7,732✔
333
            REALM_ASSERT(realm->is_in_migration());
40!
334
            if (policy.update) {
40!
335
                if (auto key = get_for_primary_key_in_migration(ctx, *table, *primary_prop, *primary_value))
1!
336
                    obj = table->get_object(key);
1✔
337
            }
1✔
338
            if (!obj)
40!
339
                skip_primary = false;
39✔
340
        }
40✔
341
        else {
7,692✔
342
            obj = table->create_object_with_primary_key(as_mixed(ctx, primary_value, primary_prop->type), &created);
7,692✔
343
            if (!created && !policy.update) {
7,692✔
344
                if (!realm->is_in_migration()) {
3✔
345
                    auto pk_val = primary_value ? ctx.print(*primary_value) : "null";
2✔
346
                    throw ObjectAlreadyExists(object_schema.name, pk_val);
2✔
347
                }
2✔
348
                table->set_primary_key_column(ColKey{});
1✔
349
                skip_primary = false;
1✔
350
                obj = {};
1✔
351
            }
1✔
352
        }
7,692✔
353
    }
7,732✔
354

355
    // No primary key (possibly temporarily due to migrations). If we're
356
    // currently performing a recursive update on an existing object tree then
357
    // an object key was passed in that we need to look up, and otherwise we
358
    // need to create the new object.
359
    if (!obj) {
7,988✔
360
        if (current_obj)
298✔
361
            obj = table->get_object(current_obj);
54✔
362
        else if (object_schema.table_type == ObjectSchema::ObjectType::Embedded)
244✔
363
            obj = ctx.create_embedded_object();
25✔
364
        else
219✔
365
            obj = table->create_object();
219✔
366
        created = !policy.diff || !current_obj;
298!
367
    }
298✔
368

369
    Object object(realm, object_schema, obj);
7,988✔
370
    // KVO in Cocoa requires that the obj ivar on the wrapper object be set
371
    // *before* we start setting the properties, so it passes in a pointer to
372
    // that.
373
    if (out_row && object_schema.table_type != ObjectSchema::ObjectType::TopLevelAsymmetric)
7,988!
374
        *out_row = obj;
×
375
    for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
29,175✔
376
        auto& prop = object_schema.persisted_properties[i];
21,188✔
377
        // If table has primary key, it must have been set during object creation
378
        if (prop.is_primary && skip_primary)
21,188✔
379
            continue;
7,689✔
380

381
        auto v = ctx.value_for_property(value, prop, i);
13,499✔
382
        if (!created && !v)
13,499✔
383
            continue;
244✔
384

385
        bool is_default = false;
13,255✔
386
        if (!v) {
13,255✔
387
            v = ctx.default_value_for_property(object_schema, prop);
2,830✔
388
            is_default = true;
2,830✔
389
        }
2,830✔
390
        // We consider null or a missing value to be equivalent to an empty
391
        // array/set for historical reasons; the original implementation did this
392
        // accidentally and it's not worth changing.
393
        if ((!v || ctx.is_null(*v)) && !is_nullable(prop.type) && !is_collection(prop.type)) {
13,255✔
394
            if (prop.is_primary || !ctx.allow_missing(value))
1!
395
                throw MissingPropertyValueException(object_schema.name, prop.name);
1✔
396
        }
1✔
397
        if (v)
13,254✔
398
            object.set_property_value_impl(ctx, prop, *v, policy, is_default);
10,449✔
399
    }
13,254✔
400
    if (object_schema.table_type == ObjectSchema::ObjectType::TopLevelAsymmetric) {
7,987✔
401
        return Object{};
213✔
402
    }
213✔
403
    return object;
7,774✔
404
}
7,987✔
405

406
template <typename ValueType, typename ContextType>
407
Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm, StringData object_type,
408
                                   ValueType primary_value)
409
{
17✔
410
    auto object_schema = realm->schema().find(object_type);
17✔
411
    REALM_ASSERT(object_schema != realm->schema().end());
17✔
412
    return get_for_primary_key(ctx, realm, *object_schema, primary_value);
17✔
413
}
17✔
414

415
template <typename ValueType, typename ContextType>
416
Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
417
                                   const ObjectSchema& object_schema, ValueType primary_value)
418
{
17✔
419
    auto primary_prop = object_schema.primary_key_property();
17✔
420
    if (!primary_prop) {
17✔
421
        throw MissingPrimaryKeyException(object_schema.name);
×
422
    }
×
423

424
    TableRef table;
17✔
425
    if (object_schema.table_key)
17✔
426
        table = realm->read_group().get_table(object_schema.table_key);
17✔
427
    if (!table)
17✔
428
        return Object(realm, object_schema, Obj());
×
429
    if (ctx.is_null(primary_value) && !is_nullable(primary_prop->type))
17!
430
        throw NotNullable(util::format("Invalid null value for non-nullable primary key '%1.%2'.", object_schema.name,
×
431
                                       primary_prop->name));
×
432

433
    auto primary_key_value = switch_on_type(primary_prop->type, [&](auto* t) {
17✔
434
        return Mixed(ctx.template unbox<NonObjTypeT<decltype(*t)>>(primary_value));
17✔
435
    });
17✔
436
    auto key = table->find_primary_key(primary_key_value);
17✔
437
    return Object(realm, object_schema, key ? table->get_object(key) : Obj{});
17✔
438
}
17✔
439

440
template <typename ValueType, typename ContextType>
441
ObjKey Object::get_for_primary_key_in_migration(ContextType& ctx, Table const& table, const Property& primary_prop,
442
                                                ValueType&& primary_value)
443
{
1✔
444
    bool is_null = ctx.is_null(primary_value);
1✔
445
    if (is_null && !is_nullable(primary_prop.type))
1!
446
        throw NotNullable(util::format("Invalid null value for non-nullable primary key '%1.%2'.",
×
447
                                       table.get_class_name(), primary_prop.name));
×
448
    if (primary_prop.type == PropertyType::String) {
1!
449
        return table.find_first(primary_prop.column_key, ctx.template unbox<StringData>(primary_value));
×
450
    }
×
451
    if (primary_prop.type == PropertyType::ObjectId) {
1!
452
        if (is_nullable(primary_prop.type)) {
×
453
            return table.find_first(primary_prop.column_key,
×
454
                                    ctx.template unbox<util::Optional<ObjectId>>(primary_value));
×
455
        }
×
456
        return table.find_first(primary_prop.column_key, ctx.template unbox<ObjectId>(primary_value));
×
457
    }
×
458
    else if (primary_prop.type == PropertyType::UUID) {
1!
459
        if (is_nullable(primary_prop.type)) {
×
460
            return table.find_primary_key(ctx.template unbox<util::Optional<UUID>>(primary_value));
×
461
        }
×
462
        return table.find_primary_key(ctx.template unbox<UUID>(primary_value));
×
463
    }
×
464
    if (is_nullable(primary_prop.type))
1!
465
        return table.find_first(primary_prop.column_key, ctx.template unbox<util::Optional<int64_t>>(primary_value));
×
466
    return table.find_first(primary_prop.column_key, ctx.template unbox<int64_t>(primary_value));
1✔
467
}
1✔
468

469
namespace _impl {
470

471
template <typename Context>
472
auto box_mixed(Context& ctx, const object_store::Collection& collection, const PathElement& path, Mixed value)
473
{
57✔
474
    if (value.is_type(type_Dictionary)) {
57!
475
        return ctx.box(collection.get_dictionary(path));
11✔
476
    }
11✔
477
    if (value.is_type(type_List)) {
46!
478
        return ctx.box(collection.get_list(path));
8✔
479
    }
8✔
480
    return ctx.box(value);
38✔
481
}
46✔
482

483
template <typename T, typename Context>
484
void assign_collection(Context& ctx, object_store::Collection& collection, const PathElement& path,
485
                       CollectionType type, T&& value, CreatePolicy policy)
486
{
40✔
487
    switch (type) {
40✔
488
        case CollectionType::List:
15!
489
            collection.get_list(path).assign(ctx, value, policy);
15✔
490
            return;
15✔
491
        case CollectionType::Dictionary:
25!
492
            collection.get_dictionary(path).assign(ctx, value, policy);
25✔
493
            return;
25✔
494
        default:
×
495
            REALM_UNREACHABLE();
496
    }
40✔
497
}
40✔
498

499
template <typename T>
500
inline std::optional<CollectionType> collection_type(T)
501
{
41,547✔
502
    return {};
41,547✔
503
}
41,547✔
504

505
inline std::optional<CollectionType> collection_type(Mixed m)
506
{
160✔
507
    static_assert(int(CollectionType::Dictionary) == int(type_Dictionary));
160✔
508
    static_assert(int(CollectionType::List) == int(type_List));
160✔
509
    if (m.is_type(type_Dictionary, type_List)) {
160✔
510
        return static_cast<CollectionType>(static_cast<int>(m.get_type()));
40✔
511
    }
40✔
512
    return {};
120✔
513
}
160✔
514

515
template <typename T>
516
bool compare(const T& a, const T& b)
517
{
17✔
518
    return a == b;
17✔
519
}
17✔
520
inline bool compare(const Obj& a, const Obj& b)
521
{
15✔
522
    return a.get_key() == b.get_key();
15✔
523
}
15✔
524
inline bool compare(Mixed a, Mixed b)
525
{
8✔
526
    return a.is_same_type(b) && a == b;
8✔
527
}
8✔
528

529
} // namespace _impl
530

531
template <typename Context>
532
auto List::get(Context& ctx, size_t row_ndx) const
533
{
112✔
534
    if (m_type == PropertyType::Mixed) {
112!
535
        return _impl::box_mixed(ctx, *this, row_ndx, get<Mixed>(row_ndx));
25✔
536
    }
25✔
537

538
    return dispatch([&](auto t) {
87✔
539
        return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx));
87✔
540
    });
87✔
541
}
112✔
542

543
template <typename T, typename Context>
544
size_t List::find(Context& ctx, T&& value) const
545
{
110✔
546
    return dispatch([&](auto t) {
110✔
547
        return this->find(ctx.template unbox<std::decay_t<decltype(*t)>>(value, CreatePolicy::Skip));
110✔
548
    });
110✔
549
}
110✔
550

551
template <typename T, typename Context>
552
void List::add(Context& ctx, T&& value, CreatePolicy policy)
553
{
41,640✔
554
    this->insert(ctx, size(), std::move(value), policy);
41,640✔
555
}
41,640✔
556

557
template <typename T, typename Context>
558
void List::insert(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
559
{
41,640✔
560
    if (m_is_embedded) {
41,640✔
561
        validate_embedded(ctx, value, policy);
41✔
562
        auto key = as<Obj>().create_and_insert_linked_object(list_ndx).get_key();
41✔
563
        ctx.template unbox<Obj>(value, policy, key);
41✔
564
        return;
41✔
565
    }
41✔
566
    dispatch([&](auto t) {
41,599✔
567
        using U = std::decay_t<decltype(*t)>;
41,599✔
568
        auto new_val = ctx.template unbox<U>(value, policy);
41,599✔
569
        if (auto type = _impl::collection_type(new_val)) {
41,599!
570
            insert_collection(list_ndx, *type);
12✔
571
            _impl::assign_collection(ctx, *this, list_ndx, *type, value, policy);
12✔
572
            return;
12✔
573
        }
12✔
574
        this->insert(list_ndx, new_val);
41,587✔
575
    });
41,587✔
576
}
41,599✔
577

578
template <typename T, typename Context>
579
void List::set(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
580
{
128✔
581
    if (m_is_embedded) {
128✔
582
        validate_embedded(ctx, value, policy);
9✔
583
        auto& list = as<Obj>();
9✔
584
        auto key = policy.diff ? list.get(list_ndx) : list.create_and_set_linked_object(list_ndx).get_key();
9!
585
        ctx.template unbox<Obj>(value, policy, key);
9✔
586
        return;
9✔
587
    }
9✔
588

589
    dispatch([&](auto t) {
119✔
590
        using U = std::decay_t<decltype(*t)>;
119✔
591
        U new_val;
119✔
592
        if constexpr (std::is_same_v<U, Obj>) {
119✔
593
            auto old_key = this->get<ObjKey>(list_ndx);
15✔
594
            new_val = ctx.template unbox<Obj>(value, policy, old_key);
15✔
595
        }
596
        else {
104✔
597
            new_val = ctx.template unbox<U>(value, policy);
104✔
598
        }
104✔
599

600
        if constexpr (std::is_same_v<U, realm::Mixed>) {
119✔
601
            if (auto type = _impl::collection_type(new_val)) {
25!
602
                set_collection(list_ndx, *type);
4✔
603
                _impl::assign_collection(ctx, *this, list_ndx, *type, value, policy);
4✔
604
                return;
4✔
605
            }
4✔
606
        }
25✔
607

608
        if (policy.diff) {
119!
609
            auto old_value = this->get<U>(list_ndx);
34✔
610
            if (_impl::compare(old_value, new_val))
34!
611
                return;
22✔
612
        }
34✔
613

614
        this->set(list_ndx, new_val);
97✔
615
    });
97✔
616
}
119✔
617

618
template <typename T, typename Context>
619
void List::assign(Context& ctx, T&& values, CreatePolicy policy)
620
{
436✔
621
    if (ctx.is_same_list(*this, values))
436!
622
        return;
2✔
623

624
    if (ctx.is_null(values)) {
434!
625
        remove_all();
21✔
626
        return;
21✔
627
    }
21✔
628

629
    if (!policy.diff)
413!
630
        remove_all();
366✔
631

632
    size_t sz = size();
413✔
633
    size_t index = 0;
413✔
634
    ctx.enumerate_collection(values, [&](auto&& element) {
712✔
635
        if (index >= sz) {
712✔
636
            this->add(ctx, element, policy);
672✔
637
        }
672✔
638
        else {
40✔
639
            // If index is within legal range, policy.diff must be true -
640
            // otherwise the list would have been cleared
641
            REALM_ASSERT(policy.diff);
40✔
642
            this->set(ctx, index, element, policy);
40✔
643
        }
40✔
644
        index++;
712✔
645
    });
712✔
646
    while (index < sz)
427!
647
        remove(--sz);
14✔
648
}
413✔
649

650
namespace object_store {
651

652
template <typename T, typename Context>
653
void Dictionary::insert(Context& ctx, StringData key, T&& value, CreatePolicy policy)
654
{
288✔
655
    if (ctx.is_null(value)) {
288✔
656
        this->insert(key, Mixed());
32✔
657
        return;
32✔
658
    }
32✔
659
    if (m_is_embedded) {
256✔
660
        validate_embedded(ctx, value, policy);
9✔
661
        auto old_value = dict().try_get(key);
9✔
662
        auto obj_key = (policy.diff && old_value && !old_value->is_null())
9!
663
                           ? old_value->get<ObjKey>()
9✔
664
                           : dict().create_and_insert_linked_object(key).get_key();
9✔
665
        ctx.template unbox<Obj>(value, policy, obj_key);
9✔
666
        return;
9✔
667
    };
247✔
668

669
    dispatch([&](auto t) {
247✔
670
        using U = std::decay_t<decltype(*t)>;
247✔
671
        U new_val;
247✔
672
        if constexpr (std::is_same_v<U, Obj>) {
247✔
673
            ObjKey old_key;
33✔
674
            if (auto old_value = dict().try_get(key); old_value && !old_value->is_null()) {
33!
675
                // Value is either null or an ObjKey
676
                old_key = old_value->get_link().get_obj_key();
×
677
            }
×
678
            new_val = ctx.template unbox<Obj>(value, policy, old_key);
33✔
679
        }
680
        else {
214✔
681
            new_val = ctx.template unbox<U>(value, policy);
214✔
682
        }
214✔
683

684
        if constexpr (std::is_same_v<U, realm::Mixed>) {
247✔
685
            if (auto type = _impl::collection_type(new_val)) {
83!
686
                insert_collection(key, *type);
24✔
687
                _impl::assign_collection(ctx, *this, key, *type, value, policy);
24✔
688
                return;
24✔
689
            }
24✔
690
        }
83✔
691

692
        if (policy.diff) {
247!
693
            if (auto old_value = dict().try_get(key)) {
21!
694
                if (_impl::compare(*old_value, new_val)) {
6!
695
                    return;
2✔
696
                }
2✔
697
            }
6✔
698
        }
21✔
699

700
        dict().insert(key, new_val);
245✔
701
    });
245✔
702
}
247✔
703

704
template <typename Context>
705
auto Dictionary::get(Context& ctx, StringData key) const
706
{
96✔
707
    if (m_type == PropertyType::Mixed) {
96✔
708
        return _impl::box_mixed(ctx, *this, key, get<Mixed>(key));
33✔
709
    }
33✔
710
    return dispatch([&](auto t) {
63✔
711
        return ctx.box(this->get<std::decay_t<decltype(*t)>>(key));
63✔
712
    });
63✔
713
}
96✔
714

715
template <typename T, typename Context>
716
void Dictionary::assign(Context& ctx, T&& values, CreatePolicy policy)
717
{
87✔
718
    if (ctx.is_same_dictionary(*this, values))
87!
719
        return;
×
720

721
    if (ctx.is_null(values)) {
87!
722
        remove_all();
×
723
        return;
×
724
    }
×
725

726
    if (!policy.diff)
87!
727
        remove_all();
68✔
728

729
    ctx.enumerate_dictionary(values, [&](StringData key, auto&& value) {
159✔
730
        this->insert(ctx, key, value, policy);
159✔
731
    });
159✔
732
}
87✔
733

734
} // namespace object_store
735
} // namespace realm
736

737
#endif // REALM_OS_OBJECT_ACCESSOR_HPP
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