Skip to content

Commit 7bbc7b7

Browse files
Schema migrations: Remove columns before removing the tables (#7607)
* Schema migrations: Remove backlink columns before removing the tables * Add links to schema in schema migration tests * Fix tests * Remove all columns before removing any tables --------- Co-authored-by: Thomas Goyne <[email protected]>
1 parent 94bbeef commit 7bbc7b7

File tree

6 files changed

+46
-28
lines changed

6 files changed

+46
-28
lines changed

src/realm/group.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -798,27 +798,27 @@ void Group::recycle_table_accessor(Table* to_be_recycled)
798798
g_table_recycler_1.push_back(to_be_recycled);
799799
}
800800

801-
void Group::remove_table(StringData name, bool ignore_backlinks)
801+
void Group::remove_table(StringData name)
802802
{
803803
check_attached();
804804
size_t table_ndx = m_table_names.find_first(name);
805805
if (table_ndx == not_found)
806806
throw NoSuchTable();
807807
auto key = ndx2key(table_ndx);
808-
remove_table(table_ndx, key, ignore_backlinks); // Throws
808+
remove_table(table_ndx, key); // Throws
809809
}
810810

811811

812-
void Group::remove_table(TableKey key, bool ignore_backlinks)
812+
void Group::remove_table(TableKey key)
813813
{
814814
check_attached();
815815

816816
size_t table_ndx = key2ndx_checked(key);
817-
remove_table(table_ndx, key, ignore_backlinks);
817+
remove_table(table_ndx, key);
818818
}
819819

820820

821-
void Group::remove_table(size_t table_ndx, TableKey key, bool ignore_backlinks)
821+
void Group::remove_table(size_t table_ndx, TableKey key)
822822
{
823823
if (!m_is_writable)
824824
throw LogicError(ErrorCodes::ReadOnlyDB, "Database not writable");
@@ -832,17 +832,14 @@ void Group::remove_table(size_t table_ndx, TableKey key, bool ignore_backlinks)
832832
// tables. Such a behaviour is deemed too obscure, and we shall therefore
833833
// require that a removed table does not contain foreign origin backlink
834834
// columns.
835-
if (!ignore_backlinks && table->is_cross_table_link_target())
835+
if (table->is_cross_table_link_target())
836836
throw CrossTableLinkTarget(table->get_name());
837837

838838
{
839839
// We don't want to replicate the individual column removals along the
840840
// way as they're covered by the table removal
841841
Table::DisableReplication dr(*table);
842-
for (size_t i = table->get_column_count(); i > 0; --i) {
843-
ColKey col_key = table->spec_ndx2colkey(i - 1);
844-
table->remove_column(col_key);
845-
}
842+
table->remove_columns();
846843
}
847844

848845
size_t prior_num_tables = m_tables.size();

src/realm/group.hpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,8 @@ class Group : public ArrayParent {
321321
TableRef get_or_add_table_with_primary_key(StringData name, DataType pk_type, StringData pk_name,
322322
bool nullable = false, Table::Type table_type = Table::Type::TopLevel);
323323

324-
// Use 'ignore_backlinks' with caution. ignore_backlinks=true will leave things in an invalid state
325-
// if the target table (or column) is not removed as well.
326-
void remove_table(TableKey key, bool ignore_backlinks = false);
327-
void remove_table(StringData name, bool ignore_backlinks = false);
324+
void remove_table(TableKey key);
325+
void remove_table(StringData name);
328326

329327
void rename_table(TableKey key, StringData new_name, bool require_unique_name = true);
330328
void rename_table(StringData name, StringData new_name, bool require_unique_name = true);
@@ -633,7 +631,7 @@ class Group : public ArrayParent {
633631
void attach_shared(ref_type new_top_ref, size_t new_file_size, bool writable, VersionID version);
634632

635633
void create_empty_group();
636-
void remove_table(size_t table_ndx, TableKey key, bool ignore_backlinks);
634+
void remove_table(size_t table_ndx, TableKey key);
637635

638636
void reset_free_space_tracking();
639637

src/realm/sync/noinst/sync_schema_migration.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,12 @@ void perform_schema_migration(DB& db)
116116
sync::TempShortCircuitReplication sync_history_guard(repl);
117117
repl.set_write_validator_factory(nullptr);
118118

119-
// Delete all tables (and their columns).
120-
const bool ignore_backlinks = true;
121-
for (const auto& tk : tr->get_table_keys()) {
122-
tr->remove_table(tk, ignore_backlinks);
119+
// Delete all columns before deleting tables to avoid complications with links
120+
for (auto tk : tr->get_table_keys()) {
121+
tr->get_table(tk)->remove_columns();
122+
}
123+
for (auto tk : tr->get_table_keys()) {
124+
tr->remove_table(tk);
123125
}
124126

125127
// Clear sync history, reset the file ident and the server version in the download and upload progress.
@@ -134,4 +136,4 @@ void perform_schema_migration(DB& db)
134136
tr->commit();
135137
}
136138

137-
} // namespace realm::_impl::sync_schema_migration
139+
} // namespace realm::_impl::sync_schema_migration

src/realm/table.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,14 @@ CollectionType Table::get_collection_type(ColKey col_key) const
511511
return CollectionType::Dictionary;
512512
}
513513

514+
void Table::remove_columns()
515+
{
516+
for (size_t i = get_column_count(); i > 0; --i) {
517+
ColKey col_key = spec_ndx2colkey(i - 1);
518+
remove_column(col_key);
519+
}
520+
}
521+
514522
void Table::remove_column(ColKey col_key)
515523
{
516524
check_column(col_key);

src/realm/table.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class Table {
204204

205205
CollectionType get_collection_type(ColKey col_key) const;
206206

207+
void remove_columns();
207208
void remove_column(ColKey col_key);
208209
void rename_column(ColKey col_key, StringData new_name);
209210
bool valid_column(ColKey col_key) const noexcept;

test/object-store/sync/flx_schema_migration.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ std::pair<SharedRealm, std::exception_ptr> async_open_realm(const Realm::Config&
8989
std::vector<ObjectSchema> get_schema_v0()
9090
{
9191
return {
92+
{"Embedded", ObjectSchema::ObjectType::Embedded, {{"str_field", PropertyType::String}}},
9293
{"TopLevel",
9394
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
9495
{"queryable_str_field", PropertyType::String | PropertyType::Nullable},
@@ -101,7 +102,10 @@ std::vector<ObjectSchema> get_schema_v0()
101102
{"queryable_int_field", PropertyType::Int | PropertyType::Nullable},
102103
{"non_queryable_field", PropertyType::String | PropertyType::Nullable}}},
103104
{"TopLevel3",
104-
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}}, {"queryable_int_field", PropertyType::Int}}},
105+
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
106+
{"queryable_int_field", PropertyType::Int},
107+
{"link", PropertyType::Object | PropertyType::Nullable, "TopLevel"},
108+
{"embedded_link", PropertyType::Object | PropertyType::Nullable, "Embedded"}}},
105109
};
106110
}
107111

@@ -135,12 +139,16 @@ auto get_subscription_initializer_callback_for_schema_v0()
135139
std::vector<ObjectSchema> get_schema_v1()
136140
{
137141
return {
142+
{"Embedded", ObjectSchema::ObjectType::Embedded, {{"str_field", PropertyType::String}}},
138143
{"TopLevel",
139144
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
140145
{"queryable_int_field", PropertyType::Int | PropertyType::Nullable},
141146
{"non_queryable_field", PropertyType::String},
142147
{"non_queryable_field2", PropertyType::String | PropertyType::Nullable}}},
143-
{"TopLevel3", {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}}}},
148+
{"TopLevel3",
149+
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
150+
{"link", PropertyType::Object | PropertyType::Nullable, "TopLevel"},
151+
{"embedded_link", PropertyType::Object | PropertyType::Nullable, "Embedded"}}},
144152
};
145153
}
146154

@@ -165,12 +173,16 @@ auto get_subscription_initializer_callback_for_schema_v1()
165173
std::vector<ObjectSchema> get_schema_v2()
166174
{
167175
return {
176+
{"Embedded", ObjectSchema::ObjectType::Embedded, {{"str_field", PropertyType::String}}},
168177
{"TopLevel",
169178
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
170179
{"queryable_int_field", PropertyType::Int},
171180
{"non_queryable_field", PropertyType::String},
172181
{"non_queryable_field2", PropertyType::String | PropertyType::Nullable}}},
173-
{"TopLevel3", {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}}}},
182+
{"TopLevel3",
183+
{{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
184+
{"link", PropertyType::Object | PropertyType::Nullable, "TopLevel"},
185+
{"embedded_link", PropertyType::Object | PropertyType::Nullable, "Embedded"}}},
174186
};
175187
}
176188

@@ -262,7 +274,7 @@ TEST_CASE("Sync schema migrations don't work with sync open", "[sync][flx][flx s
262274

263275
SECTION("Breaking change detected by client") {
264276
// Make field 'non_queryable_field2' of table 'TopLevel' optional.
265-
schema_v1[0].persisted_properties.back() = {"non_queryable_field2",
277+
schema_v1[1].persisted_properties.back() = {"non_queryable_field2",
266278
PropertyType::String | PropertyType::Nullable};
267279
config.schema = schema_v1;
268280
create_schema(app_session, *config.schema, config.schema_version);
@@ -272,8 +284,8 @@ TEST_CASE("Sync schema migrations don't work with sync open", "[sync][flx][flx s
272284
}
273285

274286
SECTION("Breaking change detected by server") {
275-
// Remove table 'TopLevel3'.
276-
schema_v1.pop_back();
287+
// Remove table 'TopLevel2'.
288+
schema_v1.erase(schema_v1.begin() + 2);
277289
config.schema = schema_v1;
278290
create_schema(app_session, *config.schema, config.schema_version);
279291

@@ -294,8 +306,8 @@ TEST_CASE("Sync schema migrations don't work with sync open", "[sync][flx][flx s
294306
wait_for_download(*realm);
295307
wait_for_upload(*realm);
296308

297-
auto table = realm->read_group().get_table("class_TopLevel3");
298-
// Migration did not succeed because table 'TopLevel3' still exists (but there is no error).
309+
auto table = realm->read_group().get_table("class_TopLevel2");
310+
// Migration did not succeed because table 'TopLevel2' still exists (but there is no error).
299311
CHECK(table);
300312
check_realm_schema(config.path, schema_v0, 1);
301313
}

0 commit comments

Comments
 (0)