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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

92.04
/src/realm/table.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_TABLE_HPP
20
#define REALM_TABLE_HPP
21

22
#include "external/mpark/variant.hpp"
23
#include <algorithm>
24
#include <map>
25
#include <utility>
26
#include <typeinfo>
27
#include <memory>
28
#include <mutex>
29

30
#include <realm/util/features.h>
31
#include <realm/util/function_ref.hpp>
32
#include <realm/util/thread.hpp>
33
#include <realm/table_ref.hpp>
34
#include <realm/spec.hpp>
35
#include <realm/query.hpp>
36
#include <realm/cluster_tree.hpp>
37
#include <realm/keys.hpp>
38
#include <realm/global_key.hpp>
39

40
// Only set this to one when testing the code paths that exercise object ID
41
// hash collisions. It artificially limits the "optimistic" local ID to use
42
// only the lower 15 bits of the ID rather than the lower 63 bits, making it
43
// feasible to generate collisions within reasonable time.
44
#define REALM_EXERCISE_OBJECT_ID_COLLISION 0
45

46
namespace realm {
47

48
class BacklinkColumn;
49
template <class>
50
class BacklinkCount;
51
class ColKeys;
52
template <class>
53
class Columns;
54
class DictionaryLinkValues;
55
struct GlobalKey;
56
class Group;
57
class LinkChain;
58
class SearchIndex;
59
class SortDescriptor;
60
class StringIndex;
61
class Subexpr;
62
template <class>
63
class SubQuery;
64
class TableView;
65

66
struct Link {
67
};
68
typedef Link BackLink;
69

70

71
namespace _impl {
72
class TableFriend;
73
}
74
namespace util {
75
class Logger;
76
}
77
namespace query_parser {
78
class Arguments;
79
class KeyPathMapping;
80
class ParserDriver;
81
} // namespace query_parser
82

83
enum class ExpressionComparisonType : unsigned char {
84
    Any,
85
    All,
86
    None,
87
};
88

89
class Table {
90
public:
91
    /// The type of tables supported by a realm.
92
    /// Note: Any change to this enum is a file-format breaking change.
93
    /// Note: Enumeration value assignments must be kept in sync with
94
    /// <realm/object-store/object_schema.hpp>.
95
    enum class Type : uint8_t { TopLevel = 0, Embedded = 0x1, TopLevelAsymmetric = 0x2 };
96
    constexpr static uint8_t table_type_mask = 0x3;
97

98
    /// Construct a new freestanding top-level table with static
99
    /// lifetime. For debugging only.
100
    Table(Allocator& = Allocator::get_default());
101

102
    /// Construct a copy of the specified table as a new freestanding
103
    /// top-level table with static lifetime. For debugging only.
104
    Table(const Table&, Allocator& = Allocator::get_default());
105

106
    ~Table() noexcept;
107

108
    Allocator& get_alloc() const;
109

110
    /// Get the name of this table, if it has one. Only group-level tables have
111
    /// names. For a table of any other kind, this function returns the empty
112
    /// string.
113
    StringData get_name() const noexcept;
114

115
    // Get table name with class prefix removed
116
    StringData get_class_name() const noexcept;
117

118
    const char* get_state() const noexcept;
119

120
    /// If this table is a group-level table, the parent group is returned,
121
    /// otherwise null is returned.
122
    Group* get_parent_group() const noexcept;
123

124

125
    Replication* get_repl() const noexcept
126
    {
85,982,973✔
127
        return *m_repl;
85,982,973✔
128
    }
85,982,973✔
129

130
    // Whether or not elements can be null.
131
    bool is_nullable(ColKey col_key) const;
132

133
    // Whether or not the column is a list.
134
    bool is_list(ColKey col_key) const;
135

136
    //@{
137
    /// Conventience functions for inspecting the dynamic table type.
138
    ///
139
    bool is_embedded() const noexcept;   // true if table holds embedded objects
140
    bool is_asymmetric() const noexcept; // true if table is asymmetric
141
    Type get_table_type() const noexcept;
142
    size_t get_column_count() const noexcept;
143
    DataType get_column_type(ColKey column_key) const;
144
    StringData get_column_name(ColKey column_key) const;
145
    StringData get_column_name(StableIndex) const;
146
    ColumnAttrMask get_column_attr(ColKey column_key) const noexcept;
147
    DataType get_dictionary_key_type(ColKey column_key) const noexcept;
148
    ColKey get_column_key(StringData name) const noexcept;
149
    ColKey get_column_key(StableIndex) const noexcept;
150
    ColKeys get_column_keys() const;
151
    typedef util::Optional<std::pair<ConstTableRef, ColKey>> BacklinkOrigin;
152
    BacklinkOrigin find_backlink_origin(StringData origin_table_name, StringData origin_col_name) const noexcept;
153
    BacklinkOrigin find_backlink_origin(ColKey backlink_col) const noexcept;
154
    std::vector<std::pair<TableKey, ColKey>> get_incoming_link_columns() const noexcept;
155
    //@}
156

157
    // Primary key columns
158
    ColKey get_primary_key_column() const;
159
    void set_primary_key_column(ColKey col);
160
    void validate_primary_column();
161

162
    //@{
163
    /// Convenience functions for manipulating the dynamic table type.
164
    ///
165
    static const size_t max_column_name_length = 63;
166
    static const uint64_t max_num_columns = 0xFFFFUL; // <-- must be power of two -1
167

168
    // Add column holding primitive values. The optional CollectionType specifies if the
169
    // property is a collection. If collection type is not specified, the property is a single value.
170
    // If the vector contains a value - eg. CollectionType::Dictionary, the property is a dictionary
171
    // of the type specified.
172
    ColKey add_column(DataType type, StringData name, bool nullable = false, std::optional<CollectionType> = {},
173
                      DataType key_type = type_String);
174
    // As above, but the values are links to objects in the target table.
175
    ColKey add_column(Table& target, StringData name, std::optional<CollectionType> = {},
176
                      DataType key_type = type_String);
177

178
    // Map old functions to the more general interface above
179
    ColKey add_column_list(DataType type, StringData name, bool nullable = false)
180
    {
13,338✔
181
        return add_column(type, name, nullable, {CollectionType::List});
13,338✔
182
    }
13,338✔
183
    ColKey add_column_list(Table& target, StringData name)
184
    {
4,467✔
185
        return add_column(target, name, {CollectionType::List});
4,467✔
186
    }
4,467✔
187
    ColKey add_column_set(DataType type, StringData name, bool nullable = false)
188
    {
888✔
189
        return add_column(type, name, nullable, {CollectionType::Set});
888✔
190
    }
888✔
191
    ColKey add_column_set(Table& target, StringData name)
192
    {
120✔
193
        return add_column(target, name, {CollectionType::Set});
120✔
194
    }
120✔
195
    ColKey add_column_dictionary(DataType type, StringData name, bool nullable = false,
196
                                 DataType key_type = type_String)
197
    {
522✔
198
        return add_column(type, name, nullable, {CollectionType::Dictionary}, key_type);
522✔
199
    }
522✔
200
    ColKey add_column_dictionary(Table& target, StringData name, DataType key_type = type_String)
201
    {
258✔
202
        return add_column(target, name, {CollectionType::Dictionary}, key_type);
258✔
203
    }
258✔
204

205
    CollectionType get_collection_type(ColKey col_key) const;
206

207
    void remove_column(ColKey col_key);
208
    void rename_column(ColKey col_key, StringData new_name);
209
    bool valid_column(ColKey col_key) const noexcept;
210
    void check_column(ColKey col_key) const
211
    {
272,427,279✔
212
        if (REALM_UNLIKELY(!valid_column(col_key)))
272,427,279✔
213
            throw InvalidColumnKey();
137,518,674✔
214
    }
272,427,279✔
215
    // Change the type of a table. Only allowed to switch to/from TopLevel from/to Embedded.
216
    void set_table_type(Type new_type, bool handle_backlinks = false);
217
    //@}
218

219
    /// True for `col_type_Link` and `col_type_LinkList`.
220
    static bool is_link_type(ColumnType) noexcept;
221

222
    //@{
223

224
    /// has_search_index() returns true if, and only if a search index has been
225
    /// added to the specified column. Rather than throwing, it returns false if
226
    /// the table accessor is detached or the specified index is out of range.
227
    ///
228
    /// add_search_index() adds a search index to the specified column of the
229
    /// table. It has no effect if a search index has already been added to the
230
    /// specified column (idempotency).
231
    ///
232
    /// remove_search_index() removes the search index from the specified column
233
    /// of the table. It has no effect if the specified column has no search
234
    /// index. The search index cannot be removed from the primary key of a
235
    /// table.
236
    ///
237
    /// \param col_key The key of a column of the table.
238

239
    IndexType search_index_type(ColKey col_key) const noexcept;
240
    bool has_search_index(ColKey col_key) const noexcept
241
    {
4,572✔
242
        return search_index_type(col_key) == IndexType::General;
4,572✔
243
    }
4,572✔
244
    void add_search_index(ColKey col_key, IndexType type = IndexType::General);
245
    void add_fulltext_index(ColKey col_key)
246
    {
32✔
247
        add_search_index(col_key, IndexType::Fulltext);
32✔
248
    }
32✔
249
    void remove_search_index(ColKey col_key);
250

251
    void enumerate_string_column(ColKey col_key);
252
    bool is_enumerated(ColKey col_key) const noexcept;
253
    bool contains_unique_values(ColKey col_key) const;
254

255
    //@}
256

257
    /// If the specified column is optimized to store only unique values, then
258
    /// this function returns the number of unique values currently
259
    /// stored. Otherwise it returns zero. This function is mainly intended for
260
    /// debugging purposes.
261
    size_t get_num_unique_values(ColKey col_key) const;
262

263
    template <class T>
264
    Columns<T> column(ColKey col_key, util::Optional<ExpressionComparisonType> = util::none) const;
265
    template <class T>
266
    Columns<T> column(const Table& origin, ColKey origin_col_key) const;
267

268
    // BacklinkCount is a total count per row and therefore not attached to a specific column
269
    template <class T>
270
    BacklinkCount<T> get_backlink_count() const;
271

272
    template <class T>
273
    SubQuery<T> column(ColKey col_key, Query subquery) const;
274
    template <class T>
275
    SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery) const;
276

277
    // Table size and deletion
278
    bool is_empty() const noexcept;
279
    size_t size() const noexcept
280
    {
11,590,881✔
281
        return m_clusters.size();
11,590,881✔
282
    }
11,590,881✔
283
    size_t nb_unresolved() const noexcept
284
    {
2,936,130✔
285
        return m_tombstones ? m_tombstones->size() : 0;
2,529,330✔
286
    }
2,936,130✔
287

288
    //@{
289

290
    /// Object handling.
291

292
    enum class UpdateMode { never, changed, all };
293

294
    // Create an object with key. If the key is omitted, a key will be generated by the system
295
    Obj create_object(ObjKey key = {}, const FieldValues& = {});
296
    // Create an object with specific GlobalKey - or return already existing object
297
    // Potential tombstone will be resurrected
298
    Obj create_object(GlobalKey object_id, const FieldValues& = {});
299
    // Create an object with primary key. If an object with the given primary key already exists, it
300
    // will be returned and did_create (if supplied) will be set to false.
301
    // Potential tombstone will be resurrected
302
    Obj create_object_with_primary_key(const Mixed& primary_key, FieldValues&&, UpdateMode mode = UpdateMode::all,
303
                                       bool* did_create = nullptr);
304
    Obj create_object_with_primary_key(const Mixed& primary_key, bool* did_create = nullptr)
305
    {
555,465✔
306
        return create_object_with_primary_key(primary_key, {{}}, UpdateMode::all, did_create);
555,465✔
307
    }
555,465✔
308
    // Return key for existing object or return null key.
309
    ObjKey find_primary_key(Mixed value) const;
310
    // Return ObjKey for object identified by id. If objects does not exist, return null key
311
    // Important: This function must not be called for tables with primary keys.
312
    ObjKey get_objkey(GlobalKey id) const;
313
    // Return key for existing object or return unresolved key.
314
    // Important: This is to be used ONLY by the Sync client. SDKs should NEVER
315
    // observe an unresolved key. Ever.
316
    ObjKey get_objkey_from_primary_key(const Mixed& primary_key);
317
    // Return key for existing object or return unresolved key.
318
    // Important: This is to be used ONLY by the Sync client. SDKs should NEVER
319
    // observe an unresolved key. Ever.
320
    // Important (2): This function must not be called for tables with primary keys.
321
    ObjKey get_objkey_from_global_key(GlobalKey key);
322
    /// Create a number of objects and add corresponding keys to a vector
323
    void create_objects(size_t number, std::vector<ObjKey>& keys);
324
    /// Create a number of objects with keys supplied
325
    void create_objects(const std::vector<ObjKey>& keys);
326
    /// Does the key refer to an object within the table?
327
    bool is_valid(ObjKey key) const noexcept
328
    {
4,466,169✔
329
        return key && m_clusters.is_valid(key);
4,466,169✔
330
    }
4,466,169✔
331
    GlobalKey get_object_id(ObjKey key) const;
332
    Obj get_object(ObjKey key) const
333
    {
91,501,152✔
334
        REALM_ASSERT(!key.is_unresolved());
91,501,152✔
335
        return m_clusters.get(key);
91,501,152✔
336
    }
91,501,152✔
337
    Obj try_get_object(ObjKey key) const noexcept
338
    {
19,079,523✔
339
        return m_clusters.try_get_obj(key);
19,079,523✔
340
    }
19,079,523✔
341
    Obj get_object(size_t ndx) const
342
    {
8,832,414✔
343
        return m_clusters.get(ndx);
8,832,414✔
344
    }
8,832,414✔
345
    // Get object based on primary key
346
    Obj get_object_with_primary_key(Mixed pk) const;
347
    // Get primary key based on ObjKey
348
    Mixed get_primary_key(ObjKey key) const;
349
    // Get logical index for object. This function is not very efficient
350
    size_t get_object_ndx(ObjKey key) const noexcept
351
    {
12,108✔
352
        return m_clusters.get_ndx(key);
12,108✔
353
    }
12,108✔
354

355
    void dump_objects();
356

357
    bool traverse_clusters(ClusterTree::TraverseFunction func) const
358
    {
2,089,638✔
359
        return m_clusters.traverse(func);
2,089,638✔
360
    }
2,089,638✔
361

362
    /// remove_object() removes the specified object from the table.
363
    /// Any links from the specified object into objects residing in an embedded
364
    /// table will cause those objects to be deleted as well, and so on recursively.
365
    void remove_object(ObjKey key);
366
    /// remove_object_recursive() will delete linked rows if the removed link was the
367
    /// last one holding on to the row in question. This will be done recursively.
368
    void remove_object_recursive(ObjKey key);
369
    // Invalidate object. To be used by the Sync client.
370
    // - turns the object into a tombstone if links exist
371
    // - otherwise works just as remove_object()
372
    ObjKey invalidate_object(ObjKey key);
373
    // Remove several objects
374
    void batch_erase_objects(std::vector<ObjKey>& keys);
375
    Obj try_get_tombstone(ObjKey key) const
376
    {
10,026✔
377
        REALM_ASSERT(key.is_unresolved());
10,026✔
378
        REALM_ASSERT(m_tombstones);
10,026✔
379
        return m_tombstones->try_get_obj(key);
10,026✔
380
    }
10,026✔
381

382
    void clear();
383
    using Iterator = ClusterTree::Iterator;
384
    Iterator begin() const;
385
    Iterator end() const;
386
    void remove_object(const Iterator& it)
387
    {
620✔
388
        remove_object(it->get_key());
620✔
389
    }
620✔
390
    //@}
391

392

393
    TableRef get_link_target(ColKey column_key) noexcept;
394
    ConstTableRef get_link_target(ColKey column_key) const noexcept;
395

396
    static const size_t max_string_size = 0xFFFFF8 - Array::header_size - 1;
397
    static const size_t max_binary_size = 0xFFFFF8 - Array::header_size;
398

399
    static constexpr int_fast64_t max_integer = std::numeric_limits<int64_t>::max();
400
    static constexpr int_fast64_t min_integer = std::numeric_limits<int64_t>::min();
401

402
    /// Only group-level unordered tables can be used as origins or targets of
403
    /// links.
404
    bool is_group_level() const noexcept;
405

406
    /// A Table accessor obtained from a frozen transaction is also frozen.
407
    bool is_frozen() const noexcept
408
    {
404✔
409
        return m_is_frozen;
404✔
410
    }
404✔
411

412
    /// If this table is a group-level table, then this function returns the
413
    /// index of this table within the group. Otherwise it returns realm::npos.
414
    size_t get_index_in_group() const noexcept;
415
    TableKey get_key() const noexcept;
416

417
    uint64_t allocate_sequence_number();
418
    // Used by upgrade
419
    void set_sequence_number(uint64_t seq);
420
    void set_collision_map(ref_type ref);
421

422
    // Get the key of this table directly, without needing a Table accessor.
423
    static TableKey get_key_direct(Allocator& alloc, ref_type top_ref);
424

425
    // Aggregate functions
426
    size_t count_int(ColKey col_key, int64_t value) const;
427
    size_t count_string(ColKey col_key, StringData value) const;
428
    size_t count_float(ColKey col_key, float value) const;
429
    size_t count_double(ColKey col_key, double value) const;
430
    size_t count_decimal(ColKey col_key, Decimal128 value) const;
431

432
    // Aggregates return nullopt if the operation is not supported on the given column
433
    // Everything but `sum` returns `some(null)` if there are no non-null values
434
    // Sum returns `some(0)` if there are no non-null values.
435
    std::optional<Mixed> sum(ColKey col_key) const;
436
    std::optional<Mixed> min(ColKey col_key, ObjKey* = nullptr) const;
437
    std::optional<Mixed> max(ColKey col_key, ObjKey* = nullptr) const;
438
    std::optional<Mixed> avg(ColKey col_key, size_t* value_count = nullptr) const;
439

440
    // Will return pointer to search index accessor. Will return nullptr if no index
441
    SearchIndex* get_search_index(ColKey col) const noexcept;
442
    StringIndex* get_string_index(ColKey col) const noexcept;
443

444
    template <class T>
445
    ObjKey find_first(ColKey col_key, T value) const;
446

447
    ObjKey find_first_int(ColKey col_key, int64_t value) const;
448
    ObjKey find_first_bool(ColKey col_key, bool value) const;
449
    ObjKey find_first_timestamp(ColKey col_key, Timestamp value) const;
450
    ObjKey find_first_object_id(ColKey col_key, ObjectId value) const;
451
    ObjKey find_first_float(ColKey col_key, float value) const;
452
    ObjKey find_first_double(ColKey col_key, double value) const;
453
    ObjKey find_first_decimal(ColKey col_key, Decimal128 value) const;
454
    ObjKey find_first_string(ColKey col_key, StringData value) const;
455
    ObjKey find_first_binary(ColKey col_key, BinaryData value) const;
456
    ObjKey find_first_null(ColKey col_key) const;
457
    ObjKey find_first_uuid(ColKey col_key, UUID value) const;
458

459
    //    TableView find_all_link(Key target_key);
460
    TableView find_all_int(ColKey col_key, int64_t value);
461
    TableView find_all_int(ColKey col_key, int64_t value) const;
462
    TableView find_all_bool(ColKey col_key, bool value);
463
    TableView find_all_bool(ColKey col_key, bool value) const;
464
    TableView find_all_float(ColKey col_key, float value);
465
    TableView find_all_float(ColKey col_key, float value) const;
466
    TableView find_all_double(ColKey col_key, double value);
467
    TableView find_all_double(ColKey col_key, double value) const;
468
    TableView find_all_string(ColKey col_key, StringData value);
469
    TableView find_all_string(ColKey col_key, StringData value) const;
470
    TableView find_all_binary(ColKey col_key, BinaryData value);
471
    TableView find_all_binary(ColKey col_key, BinaryData value) const;
472
    TableView find_all_null(ColKey col_key);
473
    TableView find_all_null(ColKey col_key) const;
474

475
    TableView find_all_fulltext(ColKey col_key, StringData value) const;
476

477
    TableView get_sorted_view(ColKey col_key, bool ascending = true);
478
    TableView get_sorted_view(ColKey col_key, bool ascending = true) const;
479

480
    TableView get_sorted_view(SortDescriptor order);
481
    TableView get_sorted_view(SortDescriptor order) const;
482

483
    // Report the current content version. This is a 64-bit value which is bumped whenever
484
    // the content in the table changes.
485
    uint_fast64_t get_content_version() const noexcept;
486

487
    // Report the current instance version. This is a 64-bit value which is bumped
488
    // whenever the table accessor is recycled.
489
    uint_fast64_t get_instance_version() const noexcept;
490

491
    // Report the current storage version. This is a 64-bit value which is bumped
492
    // whenever the location in memory of any part of the table changes.
493
    uint_fast64_t get_storage_version(uint64_t instance_version) const;
494
    uint_fast64_t get_storage_version() const;
495
    void bump_storage_version() const noexcept;
496
    void bump_content_version() const noexcept;
497

498
    // Change the nullability of the column identified by col_key.
499
    // This might result in the creation of a new column and deletion of the old.
500
    // The column key to use going forward is returned.
501
    // If the conversion is from nullable to non-nullable, throw_on_null determines
502
    // the reaction to encountering a null value: If clear, null values will be
503
    // converted to default values. If set, a 'column_not_nullable' is thrown and the
504
    // table is unchanged.
505
    ColKey set_nullability(ColKey col_key, bool nullable, bool throw_on_null);
506

507
    // Iterate through (subset of) columns. The supplied function may abort iteration
508
    // by returning 'IteratorControl::Stop' (early out).
509
    template <typename Func>
510
    bool for_each_and_every_column(Func func) const
511
    {
32,169,270✔
512
        for (auto col_key : m_leaf_ndx2colkey) {
49,816,383✔
513
            if (!col_key)
49,816,383✔
514
                continue;
78✔
515
            if (func(col_key) == IteratorControl::Stop)
49,816,305✔
516
                return true;
×
517
        }
49,816,305✔
518
        return false;
32,169,270✔
519
    }
32,169,270✔
520
    template <typename Func>
521
    bool for_each_public_column(Func func) const
522
    {
109,329✔
523
        for (auto col_key : m_leaf_ndx2colkey) {
231,225✔
524
            if (!col_key)
231,225✔
525
                continue;
6✔
526
            if (col_key.get_type() == col_type_BackLink)
231,219✔
527
                continue;
7,293✔
528
            if (func(col_key) == IteratorControl::Stop)
223,926✔
529
                return true;
7,017✔
530
        }
223,926✔
531
        return false;
105,819✔
532
    }
109,329✔
533
    template <typename Func>
534
    bool for_each_backlink_column(Func func) const
535
    {
5,402,025✔
536
        // Could be optimized - to not iterate through all non-backlink columns:
2,700,823✔
537
        for (auto col_key : m_leaf_ndx2colkey) {
8,757,733✔
538
            if (!col_key)
8,757,733✔
539
                continue;
24✔
540
            if (col_key.get_type() != col_type_BackLink)
8,757,709✔
541
                continue;
7,930,501✔
542
            if (func(col_key) == IteratorControl::Stop)
827,208✔
543
                return true;
137,535✔
544
        }
827,208✔
545
        return false;
5,333,196✔
546
    }
5,402,025✔
547

548
private:
549
    template <class T>
550
    TableView find_all(ColKey col_key, T value);
551
    void build_column_mapping();
552
    ColKey generate_col_key(ColumnType ct, ColumnAttrMask attrs);
553
    void convert_column(ColKey from, ColKey to, bool throw_on_null);
554
    template <class F, class T>
555
    void change_nullability(ColKey from, ColKey to, bool throw_on_null);
556
    template <class F, class T>
557
    void change_nullability_list(ColKey from, ColKey to, bool throw_on_null);
558
    Obj create_linked_object();
559
    // Change the embedded property of a table. If switching to being embedded, the table must
560
    // not have a primary key and all objects must have exactly 1 backlink.
561
    void set_embedded(bool embedded, bool handle_backlinks);
562
    /// Changes type unconditionally. Called only from Group::do_get_or_add_table()
563
    void do_set_table_type(Type table_type);
564

565
public:
566
    // mapping between index used in leaf nodes (leaf_ndx) and index used in spec (spec_ndx)
567
    // as well as the full column key. A leaf_ndx can be obtained directly from the column key
568
    size_t colkey2spec_ndx(ColKey key) const;
569
    size_t leaf_ndx2spec_ndx(ColKey::Idx idx) const;
570
    ColKey::Idx spec_ndx2leaf_ndx(size_t idx) const;
571
    ColKey leaf_ndx2colkey(ColKey::Idx idx) const;
572
    ColKey spec_ndx2colkey(size_t ndx) const;
573

574
    // Queries
575
    // Using where(tv) is the new method to perform queries on TableView. The 'tv' can have any order; it does not
576
    // need to be sorted, and, resulting view retains its order.
577
    Query where(TableView* tv = nullptr)
578
    {
2,250,504✔
579
        return Query(m_own_ref, tv);
2,250,504✔
580
    }
2,250,504✔
581

582
    Query where(TableView* tv = nullptr) const
583
    {
28,146✔
584
        return Query(m_own_ref, tv);
28,146✔
585
    }
28,146✔
586

587
    // Perform queries on a Link Collection. The returned Query holds a reference to collection.
588
    Query where(const ObjList& list) const
589
    {
3,106✔
590
        return Query(m_own_ref, list);
3,106✔
591
    }
3,106✔
592
    Query where(const DictionaryLinkValues& dictionary_of_links) const;
593

594
    Query query(const std::string& query_string,
595
                const std::vector<mpark::variant<Mixed, std::vector<Mixed>>>& arguments = {}) const;
596
    Query query(const std::string& query_string, const std::vector<Mixed>& arguments) const;
597
    Query query(const std::string& query_string, const std::vector<Mixed>& arguments,
598
                const query_parser::KeyPathMapping& mapping) const;
599
    Query query(const std::string& query_string,
600
                const std::vector<mpark::variant<Mixed, std::vector<Mixed>>>& arguments,
601
                const query_parser::KeyPathMapping& mapping) const;
602
    Query query(const std::string& query_string, query_parser::Arguments& arguments,
603
                const query_parser::KeyPathMapping&) const;
604

605
    //@{
606
    /// WARNING: The link() and backlink() methods will alter a state on the Table object and return a reference
607
    /// to itself. Be aware if assigning the return value of link() to a variable; this might be an error!
608

609
    /// This is an error:
610

611
    /// Table& cats = owners->link(1);
612
    /// auto& dogs = owners->link(2);
613

614
    /// Query q = person_table->where()
615
    /// .and_query(cats.column<String>(5).equal("Fido"))
616
    /// .Or()
617
    /// .and_query(dogs.column<String>(6).equal("Meowth"));
618

619
    /// Instead, do this:
620

621
    /// Query q = owners->where()
622
    /// .and_query(person_table->link(1).column<String>(5).equal("Fido"))
623
    /// .Or()
624
    /// .and_query(person_table->link(2).column<String>(6).equal("Meowth"));
625

626
    /// The two calls to link() in the erroneous example will append the two values 0 and 1 to an internal vector in
627
    /// the owners table, and we end up with three references to that same table: owners, cats and dogs. They are all
628
    /// the same table, its vector has the values {0, 1}, so a query would not make any sense.
629
    LinkChain link(ColKey link_column) const;
630
    LinkChain backlink(const Table& origin, ColKey origin_col_key) const;
631

632
    // Conversion
633
    void schema_to_json(std::ostream& out, const std::map<std::string, std::string>& renames) const;
634
    void to_json(std::ostream& out, size_t link_depth, const std::map<std::string, std::string>& renames,
635
                 JSONOutputMode output_mode = output_mode_json) const;
636

637
    /// \brief Compare two tables for equality.
638
    ///
639
    /// Two tables are equal if they have equal descriptors
640
    /// (`Descriptor::operator==()`) and equal contents. Equal descriptors imply
641
    /// that the two tables have the same columns in the same order. Equal
642
    /// contents means that the two tables must have the same number of rows,
643
    /// and that for each row index, the two rows must have the same values in
644
    /// each column.
645
    ///
646
    /// In mixed columns, both the value types and the values are required to be
647
    /// equal.
648
    ///
649
    /// For a particular row and column, if the two values are themselves tables
650
    /// (subtable and mixed columns) value equality implies a recursive
651
    /// invocation of `Table::operator==()`.
652
    bool operator==(const Table&) const;
653

654
    /// \brief Compare two tables for inequality.
655
    ///
656
    /// See operator==().
657
    bool operator!=(const Table& t) const;
658

659
    // Debug
660
    void verify() const;
661

662
#ifdef REALM_DEBUG
663
    MemStats stats() const;
664
#endif
665
    TableRef get_opposite_table(ColKey col_key) const;
666
    TableKey get_opposite_table_key(ColKey col_key) const;
667
    bool links_to_self(ColKey col_key) const;
668
    ColKey get_opposite_column(ColKey col_key) const;
669
    ColKey find_opposite_column(ColKey col_key) const;
670

671
    class DisableReplication {
672
    public:
673
        DisableReplication(Table& table) noexcept
674
            : m_table(table)
675
            , m_repl(table.m_repl)
676
        {
9,261✔
677
            m_table.m_repl = &g_dummy_replication;
9,261✔
678
        }
9,261✔
679
        ~DisableReplication()
680
        {
9,261✔
681
            m_table.m_repl = m_repl;
9,261✔
682
        }
9,261✔
683

684
    private:
685
        Table& m_table;
686
        Replication* const* m_repl;
687
    };
688

689
private:
690
    enum LifeCycleCookie {
691
        cookie_created = 0x1234,
692
        cookie_transaction_ended = 0xcafe,
693
        cookie_initialized = 0xbeef,
694
        cookie_removed = 0xbabe,
695
        cookie_void = 0x5678,
696
        cookie_deleted = 0xdead,
697
    };
698

699
    // This is only used for debugging checks, so relaxed operations are fine.
700
    class AtomicLifeCycleCookie {
701
    public:
702
        void operator=(LifeCycleCookie cookie)
703
        {
10,920,279✔
704
            m_storage.store(cookie, std::memory_order_relaxed);
10,920,279✔
705
        }
10,920,279✔
706
        operator LifeCycleCookie() const
707
        {
313,059✔
708
            return m_storage.load(std::memory_order_relaxed);
313,059✔
709
        }
313,059✔
710

711
    private:
712
        std::atomic<LifeCycleCookie> m_storage = {};
713
    };
714

715
    mutable WrappedAllocator m_alloc;
716
    Array m_top;
717
    void update_allocator_wrapper(bool writable)
718
    {
6,879,495✔
719
        m_alloc.update_from_underlying_allocator(writable);
6,879,495✔
720
    }
6,879,495✔
721
    void refresh_allocator_wrapper() const noexcept
722
    {
×
723
        m_alloc.refresh_ref_translation();
×
724
    }
×
725
    Spec m_spec;                                    // 1st slot in m_top
726
    ClusterTree m_clusters;                         // 3rd slot in m_top
727
    std::unique_ptr<ClusterTree> m_tombstones;      // 13th slot in m_top
728
    TableKey m_key;                                 // 4th slot in m_top
729
    Array m_index_refs;                             // 5th slot in m_top
730
    Array m_opposite_table;                         // 7th slot in m_top
731
    Array m_opposite_column;                        // 8th slot in m_top
732
    std::vector<std::unique_ptr<SearchIndex>> m_index_accessors;
733
    ColKey m_primary_key_col;
734
    Replication* const* m_repl;
735
    static Replication* g_dummy_replication;
736
    bool m_is_frozen = false;
737
    util::Optional<bool> m_has_any_embedded_objects;
738
    TableRef m_own_ref;
739

740
    void batch_erase_rows(const KeyColumn& keys);
741
    size_t do_set_link(ColKey col_key, size_t row_ndx, size_t target_row_ndx);
742

743
    void populate_search_index(ColKey col_key);
744
    void erase_from_search_indexes(ObjKey key);
745
    void update_indexes(ObjKey key, const FieldValues& values);
746
    void clear_indexes();
747
    template <typename T>
748
    void do_populate_index(StringIndex* index, ColKey::Idx col_ndx);
749

750
    // Migration support
751
    void migrate_sets_and_dictionaries();
752
    void migrate_set_orderings();
753

754
    /// Disable copying assignment.
755
    ///
756
    /// It could easily be implemented by calling assign(), but the
757
    /// non-checking nature of the low-level dynamically typed API
758
    /// makes it too risky to offer this feature as an
759
    /// operator.
760
    Table& operator=(const Table&) = delete;
761

762
    /// Create an uninitialized accessor whose lifetime is managed by Group
763
    Table(Replication* const* repl, Allocator&);
764
    void revive(Replication* const* repl, Allocator& new_allocator, bool writable);
765

766
    void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool is_writable, bool is_frozen);
767
    void ensure_graveyard();
768

769
    void set_key(TableKey key);
770

771
    ColKey do_insert_column(ColKey col_key, DataType type, StringData name, Table* target_table,
772
                            DataType key_type = DataType(0));
773

774
    struct InsertSubtableColumns;
775
    struct EraseSubtableColumns;
776
    struct RenameSubtableColumns;
777

778
    void erase_root_column(ColKey col_key);
779
    ColKey do_insert_root_column(ColKey col_key, ColumnType, StringData name, DataType key_type = DataType(0));
780
    void do_erase_root_column(ColKey col_key);
781
    void do_add_search_index(ColKey col_key, IndexType type);
782

783
    bool has_any_embedded_objects();
784
    void set_opposite_column(ColKey col_key, TableKey opposite_table, ColKey opposite_column);
785
    ColKey find_backlink_column(ColKey origin_col_key, TableKey origin_table) const;
786
    ColKey find_or_add_backlink_column(ColKey origin_col_key, TableKey origin_table);
787
    void do_set_primary_key_column(ColKey col_key);
788
    void validate_column_is_unique(ColKey col_key) const;
789

790
    ObjKey get_next_valid_key();
791
    /// Some Object IDs are generated as a tuple of the client_file_ident and a
792
    /// local sequence number. This function takes the next number in the
793
    /// sequence for the given table and returns an appropriate globally unique
794
    /// GlobalKey.
795
    GlobalKey allocate_object_id_squeezed();
796

797
    /// Find the local 64-bit object ID for the provided global 128-bit ID.
798
    ObjKey global_to_local_object_id_hashed(GlobalKey global_id) const;
799

800
    /// After a local ObjKey collision has been detected, this function may be
801
    /// called to obtain a non-colliding local ObjKey in such a way that subsequent
802
    /// calls to global_to_local_object_id() will return the correct local ObjKey
803
    /// for both \a incoming_id and \a colliding_id.
804
    ObjKey allocate_local_id_after_hash_collision(GlobalKey incoming_id, GlobalKey colliding_id,
805
                                                  ObjKey colliding_local_id);
806
    /// Create a placeholder for a not yet existing object and return key to it
807
    Obj get_or_create_tombstone(ObjKey key, ColKey pk_col, Mixed pk_val);
808
    /// Should be called when an object is deleted
809
    void free_local_id_after_hash_collision(ObjKey key);
810
    /// Should be called when last entry is removed - or when table is cleared
811
    void free_collision_table();
812

813
    /// Called in the context of Group::commit() to ensure that
814
    /// attached table accessors stay valid across a commit. Please
815
    /// note that this works only for non-transactional commits. Table
816
    /// accessors obtained during a transaction are always detached
817
    /// when the transaction ends.
818
    void update_from_parent() noexcept;
819

820
    // Detach accessor. This recycles the Table accessor and all subordinate
821
    // accessors become invalid.
822
    void detach(LifeCycleCookie) noexcept;
823
    void fully_detach() noexcept;
824

825
    ColumnType get_real_column_type(ColKey col_key) const noexcept;
826

827
    uint64_t get_sync_file_id() const noexcept;
828

829
    /// Create an empty table with independent spec and return just
830
    /// the reference to the underlying memory.
831
    static ref_type create_empty_table(Allocator&, TableKey = TableKey());
832

833
    void nullify_links(CascadeState&);
834
    void remove_recursive(CascadeState&);
835

836
    util::Logger* get_logger() const noexcept;
837

838
    void set_ndx_in_parent(size_t ndx_in_parent) noexcept;
839

840
    /// Refresh the part of the accessor tree that is rooted at this
841
    /// table.
842
    void refresh_accessor_tree();
843
    void refresh_index_accessors();
844
    void refresh_content_version();
845
    void flush_for_commit();
846

847
    bool is_cross_table_link_target() const noexcept;
848

849
    template <typename T>
850
    void aggregate(QueryStateBase& st, ColKey col_key) const;
851

852
    std::vector<ColKey> m_leaf_ndx2colkey;
853
    std::vector<ColKey::Idx> m_spec_ndx2leaf_ndx;
854
    std::vector<size_t> m_leaf_ndx2spec_ndx;
855
    Type m_table_type = Type::TopLevel;
856
    uint64_t m_in_file_version_at_transaction_boundary = 0;
857
    AtomicLifeCycleCookie m_cookie;
858

859
    static constexpr int top_position_for_spec = 0;
860
    static constexpr int top_position_for_columns = 1;
861
    static constexpr int top_position_for_cluster_tree = 2;
862
    static constexpr int top_position_for_key = 3;
863
    static constexpr int top_position_for_search_indexes = 4;
864
    static constexpr int top_position_for_column_key = 5;
865
    static constexpr int top_position_for_version = 6;
866
    static constexpr int top_position_for_opposite_table = 7;
867
    static constexpr int top_position_for_opposite_column = 8;
868
    static constexpr int top_position_for_sequence_number = 9;
869
    static constexpr int top_position_for_collision_map = 10;
870
    static constexpr int top_position_for_pk_col = 11;
871
    static constexpr int top_position_for_flags = 12;
872
    // flags contents: bit 0-1 - table type
873
    static constexpr int top_position_for_tombstones = 13;
874
    static constexpr int top_array_size = 14;
875

876
    enum { s_collision_map_lo = 0, s_collision_map_hi = 1, s_collision_map_local_id = 2, s_collision_map_num_slots };
877

878
    friend class _impl::TableFriend;
879
    friend class Query;
880
    template <class>
881
    friend class SimpleQuerySupport;
882
    friend class TableView;
883
    template <class T>
884
    friend class Columns;
885
    friend class Columns<StringData>;
886
    friend class ParentNode;
887
    friend struct util::serializer::SerialisationState;
888
    friend class LinkMap;
889
    friend class LinkView;
890
    friend class Group;
891
    friend class Transaction;
892
    friend class Cluster;
893
    friend class ClusterTree;
894
    friend class ColKeyIterator;
895
    friend class Obj;
896
    friend class CollectionParent;
897
    friend class CollectionList;
898
    friend class LnkLst;
899
    friend class Dictionary;
900
    friend class IncludeDescriptor;
901
    template <class T>
902
    friend class AggregateHelper;
903
};
904

905
std::ostream& operator<<(std::ostream& o, Table::Type table_type);
906

907
class ColKeyIterator {
908
public:
909
    bool operator!=(const ColKeyIterator& other)
910
    {
6,428,817✔
911
        return m_pos != other.m_pos;
6,428,817✔
912
    }
6,428,817✔
913
    ColKeyIterator& operator++()
914
    {
4,687,923✔
915
        ++m_pos;
4,687,923✔
916
        return *this;
4,687,923✔
917
    }
4,687,923✔
918
    ColKeyIterator operator++(int)
919
    {
174✔
920
        ColKeyIterator tmp(m_table, m_pos);
174✔
921
        ++m_pos;
174✔
922
        return tmp;
174✔
923
    }
174✔
924
    ColKey operator*()
925
    {
25,169,049✔
926
        if (m_pos < m_table->get_column_count()) {
25,195,248✔
927
            REALM_ASSERT(m_table->m_spec.get_key(m_pos) == m_table->spec_ndx2colkey(m_pos));
25,194,825✔
928
            return m_table->m_spec.get_key(m_pos);
25,194,825✔
929
        }
25,194,825✔
930
        return {};
2,147,484,070✔
931
    }
2,147,484,070✔
932

933
private:
934
    friend class ColKeys;
935
    const Table* m_table;
936
    size_t m_pos;
937

938
    ColKeyIterator(const Table* t, size_t p)
939
        : m_table(t)
940
        , m_pos(p)
941
    {
23,977,767✔
942
    }
23,977,767✔
943
};
944

945
class ColKeys {
946
public:
947
    ColKeys(const Table* t)
948
        : m_table(t)
949
    {
7,696,932✔
950
    }
7,696,932✔
951

952
    ColKeys()
953
        : m_table(nullptr)
954
    {
×
955
    }
×
956

957
    size_t size() const
958
    {
1,743,396✔
959
        return m_table->get_column_count();
1,743,396✔
960
    }
1,743,396✔
961
    bool empty() const
962
    {
78✔
963
        return size() == 0;
78✔
964
    }
78✔
965
    ColKey operator[](size_t p) const
966
    {
20,487,051✔
967
        return ColKeyIterator(m_table, p).operator*();
20,487,051✔
968
    }
20,487,051✔
969
    ColKeyIterator begin() const
970
    {
1,740,906✔
971
        return ColKeyIterator(m_table, 0);
1,740,906✔
972
    }
1,740,906✔
973
    ColKeyIterator end() const
974
    {
1,740,894✔
975
        return ColKeyIterator(m_table, size());
1,740,894✔
976
    }
1,740,894✔
977

978
private:
979
    const Table* m_table;
980
};
981

982
// Class used to collect a chain of links when building up a Query following links.
983
// It has member functions corresponding to the ones defined on Table.
984
class LinkChain {
985
public:
986
    LinkChain(ConstTableRef t = {}, util::Optional<ExpressionComparisonType> type = util::none)
987
        : m_current_table(t)
988
        , m_base_table(t)
989
        , m_comparison_type(type)
990
    {
1,050,398✔
991
    }
1,050,398✔
992
    ConstTableRef get_base_table()
993
    {
×
994
        return m_base_table;
×
995
    }
×
996

997
    ConstTableRef get_current_table() const
998
    {
1,441,562✔
999
        return m_current_table;
1,441,562✔
1000
    }
1,441,562✔
1001

1002
    ColKey get_current_col() const
1003
    {
312✔
1004
        return m_link_cols.back();
312✔
1005
    }
312✔
1006

1007
    LinkChain& link(ColKey link_column)
1008
    {
1,656✔
1009
        add(link_column);
1,656✔
1010
        return *this;
1,656✔
1011
    }
1,656✔
1012

1013
    void pop_back()
1014
    {
4✔
1015
        m_link_cols.pop_back();
4✔
1016
        // Recalculate m_current_table
2✔
1017
        m_current_table = m_base_table;
4✔
1018
        for (auto col : m_link_cols) {
2✔
NEW
1019
            m_current_table = m_current_table->get_opposite_table(col);
×
UNCOV
1020
        }
×
1021
    }
4✔
1022

1023
    bool link(std::string col_name)
1024
    {
490,180✔
1025
        if (auto ck = m_current_table->get_column_key(col_name)) {
490,180✔
1026
            return add(ck);
490,000✔
1027
        }
490,000✔
1028
        return false;
180✔
1029
    }
180✔
1030

1031
    bool index(PathElement index)
1032
    {
260✔
1033
        if (!m_link_cols.empty() && !m_link_cols.back().has_index()) {
260✔
1034
            if (index.is_all())
148✔
1035
                return true;
4✔
1036
            ColKey last_col = m_link_cols.back();
144✔
1037
            if (index.is_ndx() && last_col.is_list()) {
144✔
1038
                m_link_cols.back().set_index(index);
72✔
1039
                return true;
72✔
1040
            }
72✔
1041
            if (index.is_key() && last_col.is_dictionary()) {
72✔
1042
                m_link_cols.back().set_index(index);
56✔
1043
                return true;
56✔
1044
            }
56✔
1045
        }
128✔
1046
        return false;
128✔
1047
    }
128✔
1048

1049
    LinkChain& backlink(const Table& origin, ColKey origin_col_key)
1050
    {
528✔
1051
        auto backlink_col_key = origin.get_opposite_column(origin_col_key);
528✔
1052
        return link(backlink_col_key);
528✔
1053
    }
528✔
1054

1055
    std::unique_ptr<Subexpr> column(const std::string&);
1056
    std::unique_ptr<Subexpr> subquery(Query subquery);
1057

1058
    template <class T>
1059
    inline Columns<T> column(ColKey col_key)
1060
    {
36,882✔
1061
        m_current_table->check_column(col_key);
36,882✔
1062

18,441✔
1063
        // Check if user-given template type equals Realm type.
18,441✔
1064
        auto ct = col_key.get_type();
36,882✔
1065
        if (ct == col_type_LinkList)
36,882✔
1066
            ct = col_type_Link;
2,906✔
1067
        if constexpr (std::is_same_v<T, Dictionary>) {
36,882✔
1068
            if (!col_key.is_dictionary())
22,269✔
1069
                throw LogicError(ErrorCodes::TypeMismatch, "Not a dictionary");
7,656✔
1070
        }
7,892✔
1071
        else {
36,646✔
1072
            if (ct != ColumnTypeTraits<T>::column_id)
36,646✔
1073
                throw LogicError(ErrorCodes::TypeMismatch,
4✔
1074
                                 util::format("Expected %1 to be a %2", m_current_table->get_column_name(col_key),
4✔
1075
                                              ColumnTypeTraits<T>::column_id));
4✔
1076
        }
36,642✔
1077

18,321✔
1078
        if (std::is_same<T, Link>::value || std::is_same<T, LnkLst>::value || std::is_same<T, BackLink>::value) {
36,878✔
1079
            m_link_cols.push_back(col_key);
3,868✔
1080
        }
3,868✔
1081

18,439✔
1082
        return Columns<T>(col_key, m_base_table, m_link_cols, m_comparison_type);
36,878✔
1083
    }
36,878✔
1084
    template <class T>
1085
    Columns<T> column(const Table& origin, ColKey origin_col_key)
1086
    {
42✔
1087
        static_assert(std::is_same<T, BackLink>::value, "");
42✔
1088

21✔
1089
        auto backlink_col_key = origin.get_opposite_column(origin_col_key);
42✔
1090
        m_link_cols.push_back(backlink_col_key);
42✔
1091

21✔
1092
        return Columns<T>(backlink_col_key, m_base_table, m_link_cols);
42✔
1093
    }
42✔
1094
    template <class T>
1095
    SubQuery<T> column(ColKey col_key, Query subquery)
1096
    {
62✔
1097
        static_assert(std::is_same<T, Link>::value, "A subquery must involve a link list or backlink column");
62✔
1098
        return SubQuery<T>(column<T>(col_key), std::move(subquery));
62✔
1099
    }
62✔
1100

1101
    template <class T>
1102
    SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery)
1103
    {
4✔
1104
        static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
4✔
1105
        return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
4✔
1106
    }
4✔
1107

1108
    template <class T>
1109
    BacklinkCount<T> get_backlink_count()
1110
    {
304✔
1111
        return BacklinkCount<T>(m_base_table, std::move(m_link_cols));
304✔
1112
    }
304✔
1113

1114
private:
1115
    friend class Table;
1116
    friend class query_parser::ParserDriver;
1117

1118
    std::vector<ExtendedColumnKey> m_link_cols;
1119
    ConstTableRef m_current_table;
1120
    ConstTableRef m_base_table;
1121
    util::Optional<ExpressionComparisonType> m_comparison_type;
1122

1123
    bool add(ColKey ck);
1124

1125
    template <class T>
1126
    std::unique_ptr<Subexpr> create_subexpr(ColKey col_key)
1127
    {
475,708✔
1128
        return std::make_unique<Columns<T>>(col_key, m_base_table, m_link_cols, m_comparison_type);
475,708✔
1129
    }
475,708✔
1130
};
1131

1132
// Implementation:
1133

1134
inline ColKeys Table::get_column_keys() const
1135
{
7,681,998✔
1136
    return ColKeys(this);
7,681,998✔
1137
}
7,681,998✔
1138

1139
inline uint_fast64_t Table::get_content_version() const noexcept
1140
{
2,114,682✔
1141
    return m_alloc.get_content_version();
2,114,682✔
1142
}
2,114,682✔
1143

1144
inline uint_fast64_t Table::get_instance_version() const noexcept
1145
{
873,542,622✔
1146
    return m_alloc.get_instance_version();
873,542,622✔
1147
}
873,542,622✔
1148

1149

1150
inline uint_fast64_t Table::get_storage_version(uint64_t instance_version) const
1151
{
×
1152
    return m_alloc.get_storage_version(instance_version);
×
1153
}
×
1154

1155
inline uint_fast64_t Table::get_storage_version() const
1156
{
1,456,308✔
1157
    return m_alloc.get_storage_version();
1,456,308✔
1158
}
1,456,308✔
1159

1160

1161
inline TableKey Table::get_key() const noexcept
1162
{
62,797,614✔
1163
    return m_key;
62,797,614✔
1164
}
62,797,614✔
1165

1166
inline void Table::bump_storage_version() const noexcept
1167
{
6,739,854✔
1168
    return m_alloc.bump_storage_version();
6,739,854✔
1169
}
6,739,854✔
1170

1171
inline void Table::bump_content_version() const noexcept
1172
{
5,622,969✔
1173
    m_alloc.bump_content_version();
5,622,969✔
1174
}
5,622,969✔
1175

1176

1177
inline size_t Table::get_column_count() const noexcept
1178
{
35,490,924✔
1179
    return m_spec.get_public_column_count();
35,490,924✔
1180
}
35,490,924✔
1181

1182
inline bool Table::is_embedded() const noexcept
1183
{
34,629,795✔
1184
    return m_table_type == Type::Embedded;
34,629,795✔
1185
}
34,629,795✔
1186

1187
inline bool Table::is_asymmetric() const noexcept
1188
{
3,221,580✔
1189
    return m_table_type == Type::TopLevelAsymmetric;
3,221,580✔
1190
}
3,221,580✔
1191

1192
inline Table::Type Table::get_table_type() const noexcept
1193
{
586,614✔
1194
    return m_table_type;
586,614✔
1195
}
586,614✔
1196

1197
inline StringData Table::get_column_name(ColKey column_key) const
1198
{
7,260,714✔
1199
    auto spec_ndx = colkey2spec_ndx(column_key);
7,260,714✔
1200
    REALM_ASSERT_3(spec_ndx, <, get_column_count());
7,260,714✔
1201
    return m_spec.get_column_name(spec_ndx);
7,260,714✔
1202
}
7,260,714✔
1203

1204
inline StringData Table::get_column_name(StableIndex index) const
1205
{
2,970✔
1206
    return m_spec.get_column_name(m_leaf_ndx2spec_ndx[index.get_index().val]);
2,970✔
1207
}
2,970✔
1208

1209
inline ColKey Table::get_column_key(StringData name) const noexcept
1210
{
8,464,215✔
1211
    size_t spec_ndx = m_spec.get_column_index(name);
8,464,215✔
1212
    if (spec_ndx == npos)
8,464,215✔
1213
        return ColKey();
45,657✔
1214
    return spec_ndx2colkey(spec_ndx);
8,418,558✔
1215
}
8,418,558✔
1216

1217
inline ColKey Table::get_column_key(StableIndex index) const noexcept
1218
{
470,883✔
1219
    return m_leaf_ndx2colkey[index.get_index().val];
470,883✔
1220
}
470,883✔
1221

1222
inline ColumnType Table::get_real_column_type(ColKey col_key) const noexcept
1223
{
40,512✔
1224
    return col_key.get_type();
40,512✔
1225
}
40,512✔
1226

1227
inline DataType Table::get_column_type(ColKey column_key) const
1228
{
1,933,791✔
1229
    return DataType(column_key.get_type());
1,933,791✔
1230
}
1,933,791✔
1231

1232
inline ColumnAttrMask Table::get_column_attr(ColKey column_key) const noexcept
1233
{
453,594✔
1234
    return column_key.get_attrs();
453,594✔
1235
}
453,594✔
1236

1237
inline DataType Table::get_dictionary_key_type(ColKey column_key) const noexcept
1238
{
173,730✔
1239
    if (column_key.is_dictionary()) {
173,730✔
1240
        auto spec_ndx = colkey2spec_ndx(column_key);
172,896✔
1241
        REALM_ASSERT_3(spec_ndx, <, get_column_count());
172,896✔
1242
        return m_spec.get_dictionary_key_type(spec_ndx);
172,896✔
1243
    }
172,896✔
1244
    return type_String;
834✔
1245
}
834✔
1246

1247

1248
inline void Table::revive(Replication* const* repl, Allocator& alloc, bool writable)
1249
{
5,427,081✔
1250
    m_alloc.switch_underlying_allocator(alloc);
5,427,081✔
1251
    m_alloc.update_from_underlying_allocator(writable);
5,427,081✔
1252
    m_repl = repl;
5,427,081✔
1253
    m_own_ref = TableRef(this, m_alloc.get_instance_version());
5,427,081✔
1254

3,153,819✔
1255
    // since we're rebinding to a new table, we'll bump version counters
3,153,819✔
1256
    // Possible optimization: save version counters along with the table data
3,153,819✔
1257
    // and restore them from there. Should decrease amount of non-necessary
3,153,819✔
1258
    // recomputations of any queries relying on this table.
3,153,819✔
1259
    bump_content_version();
5,427,081✔
1260
    bump_storage_version();
5,427,081✔
1261
    // we assume all other accessors are detached, so we're done.
3,153,819✔
1262
}
5,427,081✔
1263

1264
inline Allocator& Table::get_alloc() const
1265
{
19,764,147✔
1266
    return m_alloc;
19,764,147✔
1267
}
19,764,147✔
1268

1269
// For use by queries
1270
template <class T>
1271
inline Columns<T> Table::column(ColKey col_key, util::Optional<ExpressionComparisonType> cmp_type) const
1272
{
35,238✔
1273
    LinkChain lc(m_own_ref, cmp_type);
35,238✔
1274
    return lc.column<T>(col_key);
35,238✔
1275
}
35,238✔
1276

1277
template <class T>
1278
inline Columns<T> Table::column(const Table& origin, ColKey origin_col_key) const
1279
{
36✔
1280
    LinkChain lc(m_own_ref);
36✔
1281
    return lc.column<T>(origin, origin_col_key);
36✔
1282
}
36✔
1283

1284
template <class T>
1285
inline BacklinkCount<T> Table::get_backlink_count() const
1286
{
1287
    return BacklinkCount<T>(this, {});
1288
}
1289

1290
template <class T>
1291
SubQuery<T> Table::column(ColKey col_key, Query subquery) const
1292
{
62✔
1293
    LinkChain lc(m_own_ref);
62✔
1294
    return lc.column<T>(col_key, subquery);
62✔
1295
}
62✔
1296

1297
template <class T>
1298
SubQuery<T> Table::column(const Table& origin, ColKey origin_col_key, Query subquery) const
1299
{
4✔
1300
    LinkChain lc(m_own_ref);
4✔
1301
    return lc.column<T>(origin, origin_col_key, subquery);
4✔
1302
}
4✔
1303

1304
inline LinkChain Table::link(ColKey link_column) const
1305
{
20,550✔
1306
    LinkChain lc(m_own_ref);
20,550✔
1307
    lc.add(link_column);
20,550✔
1308

10,275✔
1309
    return lc;
20,550✔
1310
}
20,550✔
1311

1312
inline LinkChain Table::backlink(const Table& origin, ColKey origin_col_key) const
1313
{
2,984✔
1314
    auto backlink_col_key = origin.get_opposite_column(origin_col_key);
2,984✔
1315
    return link(backlink_col_key);
2,984✔
1316
}
2,984✔
1317

1318
inline bool Table::is_empty() const noexcept
1319
{
65,985✔
1320
    return size() == 0;
65,985✔
1321
}
65,985✔
1322

1323
inline ConstTableRef Table::get_link_target(ColKey col_key) const noexcept
1324
{
274,344✔
1325
    return const_cast<Table*>(this)->get_link_target(col_key);
274,344✔
1326
}
274,344✔
1327

1328
inline bool Table::is_group_level() const noexcept
1329
{
×
1330
    return bool(get_parent_group());
×
1331
}
×
1332

1333
inline bool Table::operator!=(const Table& t) const
1334
{
78✔
1335
    return !(*this == t); // Throws
78✔
1336
}
78✔
1337

1338
inline bool Table::is_link_type(ColumnType col_type) noexcept
1339
{
1,455,996✔
1340
    return col_type == col_type_Link || col_type == col_type_LinkList;
1,455,996✔
1341
}
1,455,996✔
1342

1343
inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept
UNCOV
1344
{
×
1345
    REALM_ASSERT(m_top.is_attached());
×
1346
    m_top.set_ndx_in_parent(ndx_in_parent);
×
1347
}
×
1348

1349
inline size_t Table::colkey2spec_ndx(ColKey key) const
1350
{
9,996,552✔
1351
    auto leaf_idx = key.get_index();
9,996,552✔
1352
    REALM_ASSERT(leaf_idx.val < m_leaf_ndx2spec_ndx.size());
9,996,552✔
1353
    return m_leaf_ndx2spec_ndx[leaf_idx.val];
9,996,552✔
1354
}
9,996,552✔
1355

1356
inline ColKey Table::spec_ndx2colkey(size_t spec_ndx) const
1357
{
42,110,451✔
1358
    REALM_ASSERT(spec_ndx < m_spec_ndx2leaf_ndx.size());
42,110,451✔
1359
    return m_leaf_ndx2colkey[m_spec_ndx2leaf_ndx[spec_ndx].val];
42,110,451✔
1360
}
42,110,451✔
1361

1362
inline size_t Table::leaf_ndx2spec_ndx(ColKey::Idx leaf_ndx) const
1363
{
11,139,750✔
1364
    REALM_ASSERT(leaf_ndx.val < m_leaf_ndx2colkey.size());
11,139,750✔
1365
    return m_leaf_ndx2spec_ndx[leaf_ndx.val];
11,139,750✔
1366
}
11,139,750✔
1367

1368
inline ColKey::Idx Table::spec_ndx2leaf_ndx(size_t spec_ndx) const
1369
{
×
1370
    REALM_ASSERT(spec_ndx < m_spec_ndx2leaf_ndx.size());
×
1371
    return m_spec_ndx2leaf_ndx[spec_ndx];
×
1372
}
×
1373

1374
inline ColKey Table::leaf_ndx2colkey(ColKey::Idx leaf_ndx) const
1375
{
10,425✔
1376
    // this may be called with leaf indicies outside of the table. This can happen
5,211✔
1377
    // when a column is removed from the mapping, but space for it is still reserved
5,211✔
1378
    // at leaf level. Operations on Cluster and ClusterTree which walks the columns
5,211✔
1379
    // based on leaf indicies may ask for colkeys which are no longer valid.
5,211✔
1380
    if (leaf_ndx.val < m_leaf_ndx2spec_ndx.size())
10,425✔
1381
        return m_leaf_ndx2colkey[leaf_ndx.val];
10,425✔
1382
    else
×
1383
        return ColKey();
×
1384
}
10,425✔
1385

1386
bool inline Table::valid_column(ColKey col_key) const noexcept
1387
{
276,597,567✔
1388
    if (col_key == ColKey())
276,597,567✔
1389
        return false;
6✔
1390
    ColKey::Idx leaf_idx = col_key.get_index();
276,597,561✔
1391
    if (leaf_idx.val >= m_leaf_ndx2colkey.size())
276,597,561✔
1392
        return false;
892,317✔
1393
    return col_key == m_leaf_ndx2colkey[leaf_idx.val];
275,705,244✔
1394
}
275,705,244✔
1395

1396
// The purpose of this class is to give internal access to some, but
1397
// not all of the non-public parts of the Table class.
1398
class _impl::TableFriend {
1399
public:
1400
    static Spec& get_spec(Table& table) noexcept
UNCOV
1401
    {
×
UNCOV
1402
        return table.m_spec;
×
UNCOV
1403
    }
×
1404

1405
    static const Spec& get_spec(const Table& table) noexcept
1406
    {
×
1407
        return table.m_spec;
×
1408
    }
×
1409

1410
    static TableRef get_opposite_link_table(const Table& table, ColKey col_key);
1411

1412
    static Group* get_parent_group(const Table& table) noexcept
1413
    {
4,795✔
1414
        return table.get_parent_group();
4,795✔
1415
    }
4,795✔
1416

1417
    static void remove_recursive(Table& table, CascadeState& rows)
1418
    {
3,609✔
1419
        table.remove_recursive(rows); // Throws
3,609✔
1420
    }
3,609✔
1421

1422
    static void batch_erase_objects(Table& table, std::vector<ObjKey>& keys)
1423
    {
4,767✔
1424
        table.batch_erase_objects(keys); // Throws
4,767✔
1425
    }
4,767✔
1426
    static void batch_erase_rows(Table& table, const KeyColumn& keys)
1427
    {
558✔
1428
        table.batch_erase_rows(keys); // Throws
558✔
1429
    }
558✔
1430
    static ObjKey global_to_local_object_id_hashed(const Table& table, GlobalKey global_id)
1431
    {
×
1432
        return table.global_to_local_object_id_hashed(global_id);
×
1433
    }
×
1434
};
1435

1436
} // namespace realm
1437

1438
#endif // REALM_TABLE_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

© 2026 Coveralls, Inc