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

realm / realm-core / 1873

27 Nov 2023 09:40PM UTC coverage: 91.661% (-0.03%) from 91.695%
1873

push

Evergreen

web-flow
Fix a bunch of throw statements to use Realm exceptions (#7141)

* Fix a bunch of throw statements to use Realm exceptions
* check correct exception in test
* clang-format and replaced a couple of std::exceptions in SyncManager
* Updated changelog

---------

Co-authored-by: Jonathan Reams <jbreams@mongodb.com>
Co-authored-by: Jørgen Edelbo <jorgen.edelbo@mongodb.com>

92402 of 169340 branches covered (0.0%)

2 of 77 new or added lines in 7 files covered. (2.6%)

113 existing lines in 15 files now uncovered.

231821 of 252910 relevant lines covered (91.66%)

6407488.93 hits per line

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

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

19
#include <realm/sync/noinst/sync_metadata_schema.hpp>
20

21
#include <realm/data_type.hpp>
22
#include <realm/transaction.hpp>
23
#include <realm/util/flat_map.hpp>
24
#include <stdexcept>
25

26
namespace realm::sync {
27
namespace {
28

29
constexpr static std::string_view c_flx_metadata_table("flx_metadata");
30
constexpr static std::string_view c_sync_internal_schemas_table("sync_internal_schemas");
31
constexpr static std::string_view c_meta_schema_version_field("schema_version");
32
constexpr static std::string_view c_meta_schema_schema_group_field("schema_group_name");
33

34
} // namespace
35

36
void create_sync_metadata_schema(const TransactionRef& tr, std::vector<SyncMetadataTable>* tables)
37
{
2,776✔
38
    util::FlatMap<std::string_view, TableRef> found_tables;
2,776✔
39
    for (auto& table : *tables) {
5,088✔
40
        if (tr->has_table(table.name)) {
5,088✔
NEW
41
            throw RuntimeError(
×
NEW
42
                ErrorCodes::RuntimeError,
×
43
                util::format("table %1 already existed when creating internal tables for sync", table.name));
×
44
        }
×
45
        TableRef table_ref;
5,088✔
46
        if (table.is_embedded) {
5,088✔
47
            table_ref = tr->add_table(table.name, Table::Type::Embedded);
2,312✔
48
        }
2,312✔
49
        else if (table.pk_info) {
2,776✔
50
            table_ref = tr->add_table_with_primary_key(table.name, table.pk_info->data_type, table.pk_info->name,
2,532✔
51
                                                       table.pk_info->is_optional);
2,532✔
52
            *table.pk_info->key_out = table_ref->get_primary_key_column();
2,532✔
53
        }
2,532✔
54
        else {
244✔
55
            table_ref = tr->add_table(table.name);
244✔
56
        }
244✔
57

2,532✔
58
        found_tables.insert({table.name, table_ref});
5,088✔
59
        *table.key_out = table_ref->get_key();
5,088✔
60
    }
5,088✔
61

1,382✔
62
    for (auto& table : *tables) {
5,088✔
63
        auto& table_ref = found_tables.at(table.name);
5,088✔
64
        for (auto& column : table.columns) {
21,004✔
65
            if (column.data_type == type_LinkList) {
21,004✔
66
                auto target_table_it = found_tables.find(column.target_table);
1,560✔
67
                if (target_table_it == found_tables.end()) {
1,560✔
NEW
68
                    throw LogicError(ErrorCodes::InvalidArgument,
×
NEW
69
                                     util::format("cannot link to non-existant table %1 from internal sync table %2",
×
NEW
70
                                                  column.target_table, table.name));
×
UNCOV
71
                }
×
72
                *column.key_out = table_ref->add_column_list(*target_table_it->second, column.name);
1,560✔
73
            }
1,560✔
74
            else if (column.data_type == type_Link) {
19,444✔
75
                auto target_table_it = found_tables.find(column.target_table);
752✔
76
                if (target_table_it == found_tables.end()) {
752✔
NEW
77
                    throw LogicError(ErrorCodes::InvalidArgument,
×
NEW
78
                                     util::format("cannot link to non-existant table %1 from internal sync table %2",
×
NEW
79
                                                  column.target_table, table.name));
×
UNCOV
80
                }
×
81
                *column.key_out = table_ref->add_column(*target_table_it->second, column.name);
752✔
82
            }
752✔
83
            else {
18,692✔
84
                *column.key_out = table_ref->add_column(column.data_type, column.name, column.is_optional);
18,692✔
85
            }
18,692✔
86
        }
21,004✔
87
    }
5,088✔
88
}
2,776✔
89

90
void load_sync_metadata_schema(const TransactionRef& tr, std::vector<SyncMetadataTable>* tables)
91
{
1,932✔
92
    for (auto& table : *tables) {
2,700✔
93
        auto table_ref = tr->get_table(table.name);
2,700✔
94
        if (!table_ref) {
2,700✔
NEW
95
            throw RuntimeError(ErrorCodes::RuntimeError,
×
NEW
96
                               util::format("could not find internal sync table %1", table.name));
×
UNCOV
97
        }
×
98

1,368✔
99
        *table.key_out = table_ref->get_key();
2,700✔
100
        if (table.pk_info) {
2,700✔
101
            auto pk_col = table_ref->get_primary_key_column();
1,896✔
102
            if (auto pk_name = table_ref->get_column_name(pk_col); pk_name != table.pk_info->name) {
1,896✔
NEW
103
                throw RuntimeError(
×
NEW
104
                    ErrorCodes::RuntimeError,
×
NEW
105
                    util::format(
×
NEW
106
                        "primary key name of sync internal table %1 does not match (stored: %2, defined: %3)",
×
NEW
107
                        table.name, pk_name, table.pk_info->name));
×
UNCOV
108
            }
×
109
            if (auto pk_type = table_ref->get_column_type(pk_col); pk_type != table.pk_info->data_type) {
1,896✔
NEW
110
                throw RuntimeError(
×
NEW
111
                    ErrorCodes::RuntimeError,
×
NEW
112
                    util::format(
×
NEW
113
                        "primary key type of sync internal table %1 does not match (stored: %2, defined: %3)",
×
NEW
114
                        table.name, pk_type, table.pk_info->data_type));
×
UNCOV
115
            }
×
116
            if (auto is_nullable = table_ref->is_nullable(pk_col); is_nullable != table.pk_info->is_optional) {
1,896✔
NEW
117
                throw RuntimeError(
×
NEW
118
                    ErrorCodes::RuntimeError,
×
NEW
119
                    util::format(
×
NEW
120
                        "primary key nullabilty of sync internal table %1 does not match (stored: %2, defined: %3)",
×
NEW
121
                        table.name, is_nullable, table.pk_info->is_optional));
×
UNCOV
122
            }
×
123
            *table.pk_info->key_out = pk_col;
1,896✔
124
        }
1,896✔
125
        else if (table.is_embedded && !table_ref->is_embedded()) {
804✔
NEW
126
            throw RuntimeError(ErrorCodes::RuntimeError,
×
NEW
127
                               util::format("internal sync table %1 should be embedded, but is not", table.name));
×
UNCOV
128
        }
×
129

1,368✔
130
        if (table.columns.size() + size_t(table.pk_info ? 1 : 0) != table_ref->get_column_count()) {
2,700✔
NEW
131
            throw RuntimeError(
×
NEW
132
                ErrorCodes::RuntimeError,
×
133
                util::format("sync internal table %1 has a different number of columns than its schema", table.name));
×
134
        }
×
135

1,368✔
136
        for (auto& col : table.columns) {
7,414✔
137
            auto col_key = table_ref->get_column_key(col.name);
7,414✔
138
            if (!col_key) {
7,414✔
NEW
139
                throw RuntimeError(
×
NEW
140
                    ErrorCodes::RuntimeError,
×
141
                    util::format("column %1 is missing in sync internal table %2", col.name, table.name));
×
142
            }
×
143

3,774✔
144
            auto found_col_type = table_ref->get_column_type(col_key);
7,414✔
145
            if (found_col_type != col.data_type) {
7,414✔
NEW
146
                throw RuntimeError(
×
NEW
147
                    ErrorCodes::RuntimeError,
×
148
                    util::format("column %1 in sync internal table %2 is the wrong type", col.name, table.name));
×
149
            }
×
150

3,774✔
151
            if (col.is_optional != table_ref->is_nullable(col_key)) {
7,414✔
NEW
152
                throw RuntimeError(
×
NEW
153
                    ErrorCodes::RuntimeError,
×
154
                    util::format("column %1 in sync internal table %2 has different nullabilty than in its schema",
×
155
                                 col.name, table.name));
×
156
            }
×
157

3,774✔
158
            if (col.data_type == type_LinkList &&
7,414✔
159
                table_ref->get_link_target(col_key)->get_name() != col.target_table) {
3,998✔
NEW
160
                throw RuntimeError(ErrorCodes::RuntimeError,
×
NEW
161
                                   util::format("column %1 in sync internal table %2 links to the wrong table %3",
×
NEW
162
                                                col.name, table.name,
×
NEW
163
                                                table_ref->get_link_target(col_key)->get_name()));
×
UNCOV
164
            }
×
165
            *col.key_out = col_key;
7,414✔
166
        }
7,414✔
167
    }
2,700✔
168
}
1,932✔
169

170
SyncMetadataSchemaVersionsReader::SyncMetadataSchemaVersionsReader(const TransactionRef& tr)
171
{
5,076✔
172
    TableKey legacy_table_key;
5,076✔
173
    ColKey legacy_version_key;
5,076✔
174
    std::vector<SyncMetadataTable> legacy_table_def{
5,076✔
175
        {&legacy_table_key, c_flx_metadata_table, {{&legacy_version_key, c_meta_schema_version_field, type_Int}}}};
5,076✔
176
    std::vector<SyncMetadataTable> unified_schema_version_table_def{
5,076✔
177
        {&m_table,
5,076✔
178
         c_sync_internal_schemas_table,
5,076✔
179
         {&m_schema_group_field, c_meta_schema_schema_group_field, type_String},
5,076✔
180
         {{&m_version_field, c_meta_schema_version_field, type_Int}}}};
5,076✔
181

2,366✔
182
    REALM_ASSERT_3(tr->get_transact_stage(), ==, DB::transact_Reading);
5,076✔
183
    // If the legacy_meta_table exists, then this table hasn't been converted and
2,366✔
184
    // the metadata schema versions information has not been upgraded/not accurate
2,366✔
185
    if (tr->has_table(c_flx_metadata_table)) {
5,076✔
186
        return;
16✔
187
    }
16✔
188

2,358✔
189
    if (tr->has_table(c_sync_internal_schemas_table)) {
5,060✔
190
        // Load m_table with the table/schema information
722✔
191
        load_sync_metadata_schema(tr, &unified_schema_version_table_def);
1,434✔
192
    }
1,434✔
193
}
5,060✔
194

195
std::optional<int64_t> SyncMetadataSchemaVersionsReader::get_version_for(const TransactionRef& tr,
196
                                                                         std::string_view schema_group_name)
197
{
5,112✔
198
    if (!m_table) {
5,112✔
199
        return util::none;
2,666✔
200
    }
2,666✔
201

1,226✔
202
    auto schema_versions = tr->get_table(m_table);
2,446✔
203
    auto obj_key = schema_versions->find_primary_key(Mixed{StringData(schema_group_name)});
2,446✔
204
    if (!obj_key) {
2,446✔
205
        return util::none;
1,896✔
206
    }
1,896✔
207
    auto metadata_obj = schema_versions->get_object(obj_key);
550✔
208
    if (!metadata_obj) {
550✔
209
        return util::none;
×
210
    }
×
211

280✔
212
    return metadata_obj.get<int64_t>(m_version_field);
550✔
213
}
550✔
214

215
SyncMetadataSchemaVersions::SyncMetadataSchemaVersions(const TransactionRef& tr)
216
    : SyncMetadataSchemaVersionsReader(tr)
217
{
2,282✔
218
    TableKey legacy_table_key;
2,282✔
219
    ColKey legacy_version_key;
2,282✔
220
    std::vector<SyncMetadataTable> legacy_table_def{
2,282✔
221
        {&legacy_table_key, c_flx_metadata_table, {{&legacy_version_key, c_meta_schema_version_field, type_Int}}}};
2,282✔
222
    std::vector<SyncMetadataTable> unified_schema_version_table_def{
2,282✔
223
        {&m_table,
2,282✔
224
         c_sync_internal_schemas_table,
2,282✔
225
         {&m_schema_group_field, c_meta_schema_schema_group_field, type_String},
2,282✔
226
         {{&m_version_field, c_meta_schema_version_field, type_Int}}}};
2,282✔
227

1,142✔
228
    REALM_ASSERT_3(tr->get_transact_stage(), ==, DB::transact_Reading);
2,282✔
229
    // If the versions table exists, then m_table would have been initialized by the reader constructor
1,142✔
230
    // If the versions table doesn't exist, then initialize it now
1,142✔
231
    if (!m_table) {
2,282✔
232
        // table should have already been initialized or needs to be created,
486✔
233
        // but re-initialize in case it isn't (e.g. both unified and legacy tables exist in DB)
486✔
234
        if (REALM_UNLIKELY(tr->has_table(c_sync_internal_schemas_table))) {
976✔
235
            load_sync_metadata_schema(tr, &unified_schema_version_table_def);
4✔
236
        }
4✔
237
        else {
972✔
238
            tr->promote_to_write();
972✔
239
            create_sync_metadata_schema(tr, &unified_schema_version_table_def);
972✔
240
            tr->commit_and_continue_as_read();
972✔
241
        }
972✔
242
    }
976✔
243

1,142✔
244
    if (!tr->has_table(c_flx_metadata_table)) {
2,282✔
245
        return;
2,270✔
246
    }
2,270✔
247

6✔
248
    // Convert the legacy table to the regular schema versions table if it exists
6✔
249
    load_sync_metadata_schema(tr, &legacy_table_def);
12✔
250
    // Migrate from just having a subscription store metadata table to having multiple table groups with multiple
6✔
251
    // versions.
6✔
252
    tr->promote_to_write();
12✔
253
    auto legacy_meta_table = tr->get_table(legacy_table_key);
12✔
254
    auto legacy_obj = legacy_meta_table->get_object(0);
12✔
255
    // Only the flx subscription store can potentially have the legacy metadata table
6✔
256
    set_version_for(tr, internal_schema_groups::c_flx_subscription_store,
12✔
257
                    legacy_obj.get<int64_t>(legacy_version_key));
12✔
258
    tr->remove_table(legacy_table_key);
12✔
259
    tr->commit_and_continue_as_read();
12✔
260
}
12✔
261

262
void SyncMetadataSchemaVersions::set_version_for(const TransactionRef& tr, std::string_view schema_group_name,
263
                                                 int64_t version)
264
{
1,836✔
265
    REALM_ASSERT(m_table);
1,836✔
266

914✔
267
    auto schema_versions = tr->get_table(m_table);
1,836✔
268
    auto metadata_obj = schema_versions->create_object_with_primary_key(Mixed{StringData(schema_group_name)});
1,836✔
269
    metadata_obj.set(m_version_field, version);
1,836✔
270
}
1,836✔
271

272
} // namespace realm::sync
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