added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / object_store.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2015 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 "object_store.hpp"
20
21 #include "feature_checks.hpp"
22 #include "object_schema.hpp"
23 #include "schema.hpp"
24 #include "shared_realm.hpp"
25
26 #include <realm/descriptor.hpp>
27 #include <realm/group.hpp>
28 #include <realm/table.hpp>
29 #include <realm/table_view.hpp>
30 #include <realm/util/assert.hpp>
31
32 #if REALM_ENABLE_SYNC
33 #include <realm/sync/object.hpp>
34 #endif // REALM_ENABLE_SYNC
35
36 #include <string.h>
37
38 using namespace realm;
39
40 constexpr uint64_t ObjectStore::NotVersioned;
41
42 namespace {
43 const char * const c_metadataTableName = "metadata";
44 const char * const c_versionColumnName = "version";
45 const size_t c_versionColumnIndex = 0;
46
47 const char * const c_primaryKeyTableName = "pk";
48 const char * const c_primaryKeyObjectClassColumnName = "pk_table";
49 const size_t c_primaryKeyObjectClassColumnIndex =  0;
50 const char * const c_primaryKeyPropertyNameColumnName = "pk_property";
51 const size_t c_primaryKeyPropertyNameColumnIndex =  1;
52
53 const size_t c_zeroRowIndex = 0;
54
55 const char c_object_table_prefix[] = "class_";
56
57 void create_metadata_tables(Group& group) {
58     // The tables 'pk' and 'metadata' are treated specially by Sync. The 'pk' table
59     // is populated by `sync::create_table` and friends, while the 'metadata' table
60     // is simply ignored.
61     TableRef pk_table = group.get_or_add_table(c_primaryKeyTableName);
62     TableRef metadata_table = group.get_or_add_table(c_metadataTableName);
63     const size_t empty_table_size = 0;
64
65     if (metadata_table->get_column_count() == empty_table_size) {
66         metadata_table->insert_column(c_versionColumnIndex, type_Int, c_versionColumnName);
67         metadata_table->add_empty_row();
68         // set initial version
69         metadata_table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned);
70     }
71
72     if (pk_table->get_column_count() == 0) {
73         pk_table->insert_column(c_primaryKeyObjectClassColumnIndex, type_String, c_primaryKeyObjectClassColumnName);
74         pk_table->insert_column(c_primaryKeyPropertyNameColumnIndex, type_String, c_primaryKeyPropertyNameColumnName);
75     }
76     pk_table->add_search_index(c_primaryKeyObjectClassColumnIndex);
77 }
78
79 void set_schema_version(Group& group, uint64_t version) {
80     TableRef table = group.get_table(c_metadataTableName);
81     table->set_int(c_versionColumnIndex, c_zeroRowIndex, version);
82 }
83
84 template<typename Group>
85 auto table_for_object_schema(Group& group, ObjectSchema const& object_schema)
86 {
87     return ObjectStore::table_for_object_type(group, object_schema.name);
88 }
89
90 DataType to_core_type(PropertyType type)
91 {
92     REALM_ASSERT(type != PropertyType::Object); // Link columns have to be handled differently
93     REALM_ASSERT(type != PropertyType::Any); // Mixed columns can't be created
94     switch (type & ~PropertyType::Flags) {
95         case PropertyType::Int:    return type_Int;
96         case PropertyType::Bool:   return type_Bool;
97         case PropertyType::Float:  return type_Float;
98         case PropertyType::Double: return type_Double;
99         case PropertyType::String: return type_String;
100         case PropertyType::Date:   return type_Timestamp;
101         case PropertyType::Data:   return type_Binary;
102         default: REALM_COMPILER_HINT_UNREACHABLE();
103     }
104 }
105
106 void insert_column(Group& group, Table& table, Property const& property, size_t col_ndx)
107 {
108     // Cannot directly insert a LinkingObjects column (a computed property).
109     // LinkingObjects must be an artifact of an existing link column.
110     REALM_ASSERT(property.type != PropertyType::LinkingObjects);
111
112     if (property.type == PropertyType::Object) {
113         auto target_name = ObjectStore::table_name_for_object_type(property.object_type);
114         TableRef link_table = group.get_table(target_name);
115         REALM_ASSERT(link_table);
116         table.insert_column_link(col_ndx, is_array(property.type) ? type_LinkList : type_Link,
117                                  property.name, *link_table);
118     }
119     else if (is_array(property.type)) {
120         DescriptorRef desc;
121         table.insert_column(col_ndx, type_Table, property.name, &desc);
122         desc->add_column(to_core_type(property.type & ~PropertyType::Flags), ObjectStore::ArrayColumnName,
123                          nullptr, is_nullable(property.type));
124     }
125     else {
126         table.insert_column(col_ndx, to_core_type(property.type), property.name, is_nullable(property.type));
127         if (property.requires_index())
128             table.add_search_index(col_ndx);
129     }
130 }
131
132 void add_column(Group& group, Table& table, Property const& property)
133 {
134     insert_column(group, table, property, table.get_column_count());
135 }
136
137 void replace_column(Group& group, Table& table, Property const& old_property, Property const& new_property)
138 {
139     insert_column(group, table, new_property, old_property.table_column);
140     table.remove_column(old_property.table_column + 1);
141 }
142
143 TableRef create_table(Group& group, ObjectSchema const& object_schema)
144 {
145     auto name = ObjectStore::table_name_for_object_type(object_schema.name);
146
147     TableRef table;
148 #if REALM_ENABLE_SYNC
149     if (auto* pk_property = object_schema.primary_key_property()) {
150         table = sync::create_table_with_primary_key(group, name, to_core_type(pk_property->type),
151                                                     pk_property->name, is_nullable(pk_property->type));
152     }
153     else {
154         table = sync::create_table(group, name);
155     }
156 #else
157     table = group.get_or_add_table(name);
158 #endif // REALM_ENABLE_SYNC
159
160     ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key);
161
162     return table;
163 }
164
165 void add_initial_columns(Group& group, ObjectSchema const& object_schema)
166 {
167     auto name = ObjectStore::table_name_for_object_type(object_schema.name);
168     TableRef table = group.get_table(name);
169
170     for (auto const& prop : object_schema.persisted_properties) {
171 #if REALM_ENABLE_SYNC
172         // The sync::create_table* functions create the PK column for us.
173         if (prop.is_primary)
174             continue;
175 #endif // REALM_ENABLE_SYNC
176         add_column(group, *table, prop);
177     }
178 }
179
180 void copy_property_values(Property const& prop, Table& table)
181 {
182     auto copy_property_values = [&](auto getter, auto setter) {
183         for (size_t i = 0, count = table.size(); i < count; i++) {
184             bool is_default = false;
185             (table.*setter)(prop.table_column, i, (table.*getter)(prop.table_column + 1, i),
186                             is_default);
187         }
188     };
189
190     switch (prop.type & ~PropertyType::Flags) {
191         case PropertyType::Int:
192             copy_property_values(&Table::get_int, &Table::set_int);
193             break;
194         case PropertyType::Bool:
195             copy_property_values(&Table::get_bool, &Table::set_bool);
196             break;
197         case PropertyType::Float:
198             copy_property_values(&Table::get_float, &Table::set_float);
199             break;
200         case PropertyType::Double:
201             copy_property_values(&Table::get_double, &Table::set_double);
202             break;
203         case PropertyType::String:
204             copy_property_values(&Table::get_string, &Table::set_string);
205             break;
206         case PropertyType::Data:
207             copy_property_values(&Table::get_binary, &Table::set_binary);
208             break;
209         case PropertyType::Date:
210             copy_property_values(&Table::get_timestamp, &Table::set_timestamp);
211             break;
212         default:
213             break;
214     }
215 }
216
217 void make_property_optional(Group& group, Table& table, Property property)
218 {
219     property.type |= PropertyType::Nullable;
220     insert_column(group, table, property, property.table_column);
221     copy_property_values(property, table);
222     table.remove_column(property.table_column + 1);
223 }
224
225 void make_property_required(Group& group, Table& table, Property property)
226 {
227     property.type &= ~PropertyType::Nullable;
228     insert_column(group, table, property, property.table_column);
229     table.remove_column(property.table_column + 1);
230 }
231
232 void validate_primary_column_uniqueness(Group const& group, StringData object_type, StringData primary_property)
233 {
234     auto table = ObjectStore::table_for_object_type(group, object_type);
235     if (table->get_distinct_view(table->get_column_index(primary_property)).size() != table->size()) {
236         throw DuplicatePrimaryKeyValueException(object_type, primary_property);
237     }
238 }
239
240 void validate_primary_column_uniqueness(Group const& group)
241 {
242     auto pk_table = group.get_table(c_primaryKeyTableName);
243     for (size_t i = 0, count = pk_table->size(); i < count; ++i) {
244         validate_primary_column_uniqueness(group,
245                                            pk_table->get_string(c_primaryKeyObjectClassColumnIndex, i),
246                                            pk_table->get_string(c_primaryKeyPropertyNameColumnIndex, i));
247     }
248 }
249 } // anonymous namespace
250
251 void ObjectStore::set_schema_version(Group& group, uint64_t version) {
252     ::create_metadata_tables(group);
253     ::set_schema_version(group, version);
254 }
255
256 uint64_t ObjectStore::get_schema_version(Group const& group) {
257     ConstTableRef table = group.get_table(c_metadataTableName);
258     if (!table || table->get_column_count() == 0) {
259         return ObjectStore::NotVersioned;
260     }
261     return table->get_int(c_versionColumnIndex, c_zeroRowIndex);
262 }
263
264 StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) {
265     ConstTableRef table = group.get_table(c_primaryKeyTableName);
266     if (!table) {
267         return "";
268     }
269     size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
270     if (row == not_found) {
271         return "";
272     }
273     return table->get_string(c_primaryKeyPropertyNameColumnIndex, row);
274 }
275
276 void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) {
277     TableRef table = group.get_table(c_primaryKeyTableName);
278
279     size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
280
281 #if REALM_ENABLE_SYNC
282     // sync::create_table* functions should have already updated the pk table.
283     if (sync::has_object_ids(group)) {
284         if (primary_key.size() == 0)
285             REALM_ASSERT(row == not_found);
286         else {
287              REALM_ASSERT(row != not_found);
288              REALM_ASSERT(table->get_string(c_primaryKeyPropertyNameColumnIndex, row) == primary_key);
289         }
290         return;
291     }
292 #endif // REALM_ENABLE_SYNC
293
294     if (row == not_found && primary_key.size()) {
295         row = table->add_empty_row();
296         table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type);
297         table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key);
298         return;
299     }
300     // set if changing, or remove if setting to nil
301     if (primary_key.size() == 0) {
302         if (row != not_found) {
303             table->move_last_over(row);
304         }
305     }
306     else {
307         table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key);
308     }
309 }
310
311 StringData ObjectStore::object_type_for_table_name(StringData table_name) {
312     if (table_name.begins_with(c_object_table_prefix)) {
313         return table_name.substr(sizeof(c_object_table_prefix) - 1);
314     }
315     return StringData();
316 }
317
318 std::string ObjectStore::table_name_for_object_type(StringData object_type) {
319     return std::string(c_object_table_prefix) + std::string(object_type);
320 }
321
322 TableRef ObjectStore::table_for_object_type(Group& group, StringData object_type) {
323     auto name = table_name_for_object_type(object_type);
324     return group.get_table(name);
325 }
326
327 ConstTableRef ObjectStore::table_for_object_type(Group const& group, StringData object_type) {
328     auto name = table_name_for_object_type(object_type);
329     return group.get_table(name);
330 }
331
332 namespace {
333 struct SchemaDifferenceExplainer {
334     std::vector<ObjectSchemaValidationException> errors;
335
336     void operator()(schema_change::AddTable op)
337     {
338         errors.emplace_back("Class '%1' has been added.", op.object->name);
339     }
340
341     void operator()(schema_change::AddInitialProperties)
342     {
343         // Nothing. Always preceded by AddTable.
344     }
345
346     void operator()(schema_change::AddProperty op)
347     {
348         errors.emplace_back("Property '%1.%2' has been added.", op.object->name, op.property->name);
349     }
350
351     void operator()(schema_change::RemoveProperty op)
352     {
353         errors.emplace_back("Property '%1.%2' has been removed.", op.object->name, op.property->name);
354     }
355
356     void operator()(schema_change::ChangePropertyType op)
357     {
358         errors.emplace_back("Property '%1.%2' has been changed from '%3' to '%4'.",
359                             op.object->name, op.new_property->name,
360                             op.old_property->type_string(),
361                             op.new_property->type_string());
362     }
363
364     void operator()(schema_change::MakePropertyNullable op)
365     {
366         errors.emplace_back("Property '%1.%2' has been made optional.", op.object->name, op.property->name);
367     }
368
369     void operator()(schema_change::MakePropertyRequired op)
370     {
371         errors.emplace_back("Property '%1.%2' has been made required.", op.object->name, op.property->name);
372     }
373
374     void operator()(schema_change::ChangePrimaryKey op)
375     {
376         if (op.property && !op.object->primary_key.empty()) {
377             errors.emplace_back("Primary Key for class '%1' has changed from '%2' to '%3'.",
378                                 op.object->name, op.object->primary_key, op.property->name);
379         }
380         else if (op.property) {
381             errors.emplace_back("Primary Key for class '%1' has been added.", op.object->name);
382         }
383         else {
384             errors.emplace_back("Primary Key for class '%1' has been removed.", op.object->name);
385         }
386     }
387
388     void operator()(schema_change::AddIndex op)
389     {
390         errors.emplace_back("Property '%1.%2' has been made indexed.", op.object->name, op.property->name);
391     }
392
393     void operator()(schema_change::RemoveIndex op)
394     {
395         errors.emplace_back("Property '%1.%2' has been made unindexed.", op.object->name, op.property->name);
396     }
397 };
398
399 class TableHelper {
400 public:
401     TableHelper(Group& g) : m_group(g) { }
402
403     Table& operator()(const ObjectSchema* object_schema)
404     {
405         if (object_schema != m_current_object_schema) {
406             m_current_table = table_for_object_schema(m_group, *object_schema);
407             m_current_object_schema = object_schema;
408         }
409         REALM_ASSERT(m_current_table);
410         return *m_current_table;
411     }
412
413 private:
414     Group& m_group;
415     const ObjectSchema* m_current_object_schema = nullptr;
416     TableRef m_current_table;
417 };
418
419 template<typename ErrorType, typename Verifier>
420 void verify_no_errors(Verifier&& verifier, std::vector<SchemaChange> const& changes)
421 {
422     for (auto& change : changes) {
423         change.visit(verifier);
424     }
425
426     if (!verifier.errors.empty()) {
427         throw ErrorType(verifier.errors);
428     }
429 }
430 } // anonymous namespace
431
432 bool ObjectStore::needs_migration(std::vector<SchemaChange> const& changes)
433 {
434     using namespace schema_change;
435     struct Visitor {
436         bool operator()(AddIndex) { return false; }
437         bool operator()(AddInitialProperties) { return false; }
438         bool operator()(AddProperty) { return true; }
439         bool operator()(AddTable) { return false; }
440         bool operator()(ChangePrimaryKey) { return true; }
441         bool operator()(ChangePropertyType) { return true; }
442         bool operator()(MakePropertyNullable) { return true; }
443         bool operator()(MakePropertyRequired) { return true; }
444         bool operator()(RemoveIndex) { return false; }
445         bool operator()(RemoveProperty) { return true; }
446     };
447
448     return std::any_of(begin(changes), end(changes),
449                        [](auto&& change) { return change.visit(Visitor()); });
450 }
451
452 void ObjectStore::verify_no_changes_required(std::vector<SchemaChange> const& changes)
453 {
454     verify_no_errors<SchemaMismatchException>(SchemaDifferenceExplainer(), changes);
455 }
456
457 void ObjectStore::verify_no_migration_required(std::vector<SchemaChange> const& changes)
458 {
459     using namespace schema_change;
460     struct Verifier : SchemaDifferenceExplainer {
461         using SchemaDifferenceExplainer::operator();
462
463         // Adding a table or adding/removing indexes can be done automatically.
464         // All other changes require migrations.
465         void operator()(AddTable) { }
466         void operator()(AddInitialProperties) { }
467         void operator()(AddIndex) { }
468         void operator()(RemoveIndex) { }
469     } verifier;
470     verify_no_errors<SchemaMismatchException>(verifier, changes);
471 }
472
473 bool ObjectStore::verify_valid_additive_changes(std::vector<SchemaChange> const& changes, bool update_indexes)
474 {
475     using namespace schema_change;
476     struct Verifier : SchemaDifferenceExplainer {
477         using SchemaDifferenceExplainer::operator();
478
479         bool index_changes = false;
480         bool other_changes = false;
481
482         // Additive mode allows adding things, extra columns, and adding/removing indexes
483         void operator()(AddTable) { other_changes = true; }
484         void operator()(AddInitialProperties) { other_changes = true; }
485         void operator()(AddProperty) { other_changes = true; }
486         void operator()(RemoveProperty) { }
487         void operator()(AddIndex) { index_changes = true; }
488         void operator()(RemoveIndex) { index_changes = true; }
489     } verifier;
490     verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
491     return verifier.other_changes || (verifier.index_changes && update_indexes);
492 }
493
494 void ObjectStore::verify_valid_external_changes(std::vector<SchemaChange> const& changes)
495 {
496     using namespace schema_change;
497     struct Verifier : SchemaDifferenceExplainer {
498         using SchemaDifferenceExplainer::operator();
499
500         // Adding new things is fine
501         void operator()(AddTable) { }
502         void operator()(AddInitialProperties) { }
503         void operator()(AddProperty) { }
504         void operator()(AddIndex) { }
505         void operator()(RemoveIndex) { }
506     } verifier;
507     verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
508 }
509
510 void ObjectStore::verify_compatible_for_immutable_and_readonly(std::vector<SchemaChange> const& changes)
511 {
512     using namespace schema_change;
513     struct Verifier : SchemaDifferenceExplainer {
514         using SchemaDifferenceExplainer::operator();
515
516         void operator()(AddTable) { }
517         void operator()(AddInitialProperties) { }
518         void operator()(RemoveProperty) { }
519         void operator()(AddIndex) { }
520         void operator()(RemoveIndex) { }
521     } verifier;
522     verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
523 }
524
525 static void apply_non_migration_changes(Group& group, std::vector<SchemaChange> const& changes)
526 {
527     using namespace schema_change;
528     struct Applier : SchemaDifferenceExplainer {
529         Applier(Group& group) : group{group}, table{group} { }
530         Group& group;
531         TableHelper table;
532
533         // Produce an exception listing the unsupported schema changes for
534         // everything but the explicitly supported ones
535         using SchemaDifferenceExplainer::operator();
536
537         void operator()(AddTable op) { create_table(group, *op.object); }
538         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
539         void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
540         void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
541     } applier{group};
542     verify_no_errors<SchemaMismatchException>(applier, changes);
543 }
544
545 static void create_initial_tables(Group& group, std::vector<SchemaChange> const& changes)
546 {
547     using namespace schema_change;
548     struct Applier {
549         Applier(Group& group) : group{group}, table{group} { }
550         Group& group;
551         TableHelper table;
552
553         void operator()(AddTable op) { create_table(group, *op.object); }
554         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
555
556         // Note that in normal operation none of these will be hit, as if we're
557         // creating the initial tables there shouldn't be anything to update.
558         // Implementing these makes us better able to handle weird
559         // not-quite-correct files produced by other things and has no obvious
560         // downside.
561         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
562         void operator()(RemoveProperty op) { table(op.object).remove_column(op.property->table_column); }
563         void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); }
564         void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); }
565         void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); }
566         void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
567         void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
568
569         void operator()(ChangePropertyType op)
570         {
571             replace_column(group, table(op.object), *op.old_property, *op.new_property);
572         }
573     } applier{group};
574
575     for (auto& change : changes) {
576         change.visit(applier);
577     }
578 }
579
580 void ObjectStore::apply_additive_changes(Group& group, std::vector<SchemaChange> const& changes, bool update_indexes)
581 {
582     using namespace schema_change;
583     struct Applier {
584         Applier(Group& group, bool update_indexes)
585         : group{group}, table{group}, update_indexes{update_indexes} { }
586         Group& group;
587         TableHelper table;
588         bool update_indexes;
589
590         void operator()(AddTable op) { create_table(group, *op.object); }
591         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
592         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
593         void operator()(AddIndex op) { if (update_indexes) table(op.object).add_search_index(op.property->table_column); }
594         void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->table_column); }
595         void operator()(RemoveProperty) { }
596
597         // No need for errors for these, as we've already verified that they aren't present
598         void operator()(ChangePrimaryKey) { }
599         void operator()(ChangePropertyType) { }
600         void operator()(MakePropertyNullable) { }
601         void operator()(MakePropertyRequired) { }
602     } applier{group, update_indexes};
603
604     for (auto& change : changes) {
605         change.visit(applier);
606     }
607 }
608
609 static void apply_pre_migration_changes(Group& group, std::vector<SchemaChange> const& changes)
610 {
611     using namespace schema_change;
612     struct Applier {
613         Applier(Group& group) : group{group}, table{group} { }
614         Group& group;
615         TableHelper table;
616
617         void operator()(AddTable op) { create_table(group, *op.object); }
618         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
619         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
620         void operator()(RemoveProperty) { /* delayed until after the migration */ }
621         void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); }
622         void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); }
623         void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); }
624         void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name.c_str(), op.property ? op.property->name.c_str() : ""); }
625         void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
626         void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
627     } applier{group};
628
629     for (auto& change : changes) {
630         change.visit(applier);
631     }
632 }
633
634 enum class DidRereadSchema { Yes, No };
635
636 static void apply_post_migration_changes(Group& group, std::vector<SchemaChange> const& changes, Schema const& initial_schema,
637                                          DidRereadSchema did_reread_schema)
638 {
639     using namespace schema_change;
640     struct Applier {
641         Applier(Group& group, Schema const& initial_schema, DidRereadSchema did_reread_schema)
642         : group{group}, initial_schema(initial_schema), table(group)
643         , did_reread_schema(did_reread_schema == DidRereadSchema::Yes)
644         { }
645         Group& group;
646         Schema const& initial_schema;
647         TableHelper table;
648         bool did_reread_schema;
649
650         void operator()(RemoveProperty op)
651         {
652             if (!initial_schema.empty() && !initial_schema.find(op.object->name)->property_for_name(op.property->name))
653                 throw std::logic_error(util::format("Renamed property '%1.%2' does not exist.", op.object->name, op.property->name));
654             auto table = table_for_object_schema(group, *op.object);
655             table->remove_column(op.property->table_column);
656         }
657
658         void operator()(ChangePrimaryKey op)
659         {
660             if (op.property) {
661                 validate_primary_column_uniqueness(group, op.object->name, op.property->name);
662             }
663         }
664
665         void operator()(AddTable op) { create_table(group, *op.object); }
666
667         void operator()(AddInitialProperties op) {
668             if (did_reread_schema)
669                 add_initial_columns(group, *op.object);
670             else {
671                 // If we didn't re-read the schema then AddInitialProperties was already taken care of
672                 // during apply_pre_migration_changes.
673             }
674         }
675
676         void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
677         void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
678
679         void operator()(ChangePropertyType) { }
680         void operator()(MakePropertyNullable) { }
681         void operator()(MakePropertyRequired) { }
682         void operator()(AddProperty) { }
683     } applier{group, initial_schema, did_reread_schema};
684
685     for (auto& change : changes) {
686         change.visit(applier);
687     }
688 }
689
690 void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version,
691                                        Schema& target_schema, uint64_t target_schema_version,
692                                        SchemaMode mode, std::vector<SchemaChange> const& changes,
693                                        std::function<void()> migration_function)
694 {
695     create_metadata_tables(group);
696
697     if (mode == SchemaMode::Additive) {
698         bool target_schema_is_newer = (schema_version < target_schema_version
699             || schema_version == ObjectStore::NotVersioned);
700
701         // With sync v2.x, indexes are no longer synced, so there's no reason to avoid creating them.
702         bool update_indexes = true;
703         apply_additive_changes(group, changes, update_indexes);
704
705         if (target_schema_is_newer)
706             set_schema_version(group, target_schema_version);
707
708         set_schema_columns(group, target_schema);
709         return;
710     }
711
712     if (schema_version == ObjectStore::NotVersioned) {
713         create_initial_tables(group, changes);
714         set_schema_version(group, target_schema_version);
715         set_schema_columns(group, target_schema);
716         return;
717     }
718
719     if (mode == SchemaMode::Manual) {
720         set_schema_columns(group, target_schema);
721         if (migration_function) {
722             migration_function();
723         }
724
725         verify_no_changes_required(schema_from_group(group).compare(target_schema));
726         validate_primary_column_uniqueness(group);
727         set_schema_columns(group, target_schema);
728         set_schema_version(group, target_schema_version);
729         return;
730     }
731
732     if (schema_version == target_schema_version) {
733         apply_non_migration_changes(group, changes);
734         set_schema_columns(group, target_schema);
735         return;
736     }
737
738     auto old_schema = schema_from_group(group);
739     apply_pre_migration_changes(group, changes);
740     if (migration_function) {
741         set_schema_columns(group, target_schema);
742         migration_function();
743
744         // Migration function may have changed the schema, so we need to re-read it
745         auto schema = schema_from_group(group);
746         apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes);
747         validate_primary_column_uniqueness(group);
748     }
749     else {
750         apply_post_migration_changes(group, changes, {}, DidRereadSchema::No);
751     }
752
753     set_schema_version(group, target_schema_version);
754     set_schema_columns(group, target_schema);
755 }
756
757 Schema ObjectStore::schema_from_group(Group const& group) {
758     std::vector<ObjectSchema> schema;
759     schema.reserve(group.size());
760     for (size_t i = 0; i < group.size(); i++) {
761         auto object_type = object_type_for_table_name(group.get_table_name(i));
762         if (object_type.size()) {
763             schema.emplace_back(group, object_type, i);
764         }
765     }
766     return schema;
767 }
768
769 util::Optional<Property> ObjectStore::property_for_column_index(ConstTableRef& table, size_t column_index)
770 {
771     StringData column_name = table->get_column_name(column_index);
772
773 #if REALM_ENABLE_SYNC
774     // The object ID column is an implementation detail, and is omitted from the schema.
775     // FIXME: Consider filtering out all column names starting with `!`.
776     if (column_name == sync::object_id_column_name)
777         return util::none;
778 #endif
779
780     if (table->get_column_type(column_index) == type_Table) {
781         auto subdesc = table->get_subdescriptor(column_index);
782         if (subdesc->get_column_count() != 1 || subdesc->get_column_name(0) != ObjectStore::ArrayColumnName)
783             return util::none;
784     }
785
786     Property property;
787     property.name = column_name;
788     property.type = ObjectSchema::from_core_type(*table->get_descriptor(), column_index);
789     property.is_indexed = table->has_search_index(column_index);
790     property.table_column = column_index;
791
792     if (property.type == PropertyType::Object) {
793         // set link type for objects and arrays
794         ConstTableRef linkTable = table->get_link_target(column_index);
795         property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
796     }
797     return property;
798 }
799
800 void ObjectStore::set_schema_columns(Group const& group, Schema& schema)
801 {
802     for (auto& object_schema : schema) {
803         auto table = table_for_object_schema(group, object_schema);
804         if (!table) {
805             continue;
806         }
807         for (auto& property : object_schema.persisted_properties) {
808             property.table_column = table->get_column_index(property.name);
809         }
810     }
811 }
812
813 void ObjectStore::delete_data_for_object(Group& group, StringData object_type) {
814     if (TableRef table = table_for_object_type(group, object_type)) {
815         group.remove_table(table->get_index_in_group());
816         ObjectStore::set_primary_key_for_object(group, object_type, "");
817     }
818 }
819
820 bool ObjectStore::is_empty(Group const& group) {
821     for (size_t i = 0; i < group.size(); i++) {
822         ConstTableRef table = group.get_table(i);
823         std::string object_type = object_type_for_table_name(table->get_name());
824         if (!object_type.length()) {
825             continue;
826         }
827         if (!table->is_empty()) {
828             return false;
829         }
830     }
831     return true;
832 }
833
834 void ObjectStore::rename_property(Group& group, Schema& target_schema, StringData object_type, StringData old_name, StringData new_name)
835 {
836     TableRef table = table_for_object_type(group, object_type);
837     if (!table) {
838         throw std::logic_error(util::format("Cannot rename properties for type '%1' because it does not exist.", object_type));
839     }
840
841     auto target_object_schema = target_schema.find(object_type);
842     if (target_object_schema == target_schema.end()) {
843         throw std::logic_error(util::format("Cannot rename properties for type '%1' because it has been removed from the Realm.", object_type));
844     }
845
846     if (target_object_schema->property_for_name(old_name)) {
847         throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because the source property still exists.",
848                                             object_type, old_name, new_name));
849     }
850
851     ObjectSchema table_object_schema(group, object_type);
852     Property *old_property = table_object_schema.property_for_name(old_name);
853     if (!old_property) {
854         throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name));
855     }
856
857     Property *new_property = table_object_schema.property_for_name(new_name);
858     if (!new_property) {
859         // New property doesn't exist in the table, which means we're probably
860         // renaming to an intermediate property in a multi-version migration.
861         // This is safe because the migration will fail schema validation unless
862         // this property is renamed again to a valid name before the end.
863         table->rename_column(old_property->table_column, new_name);
864         return;
865     }
866
867     if (old_property->type != new_property->type || old_property->object_type != new_property->object_type) {
868         throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from type '%4' to '%5'.",
869                                             object_type, old_name, new_name, old_property->type_string(), new_property->type_string()));
870     }
871
872     if (is_nullable(old_property->type) && !is_nullable(new_property->type)) {
873         throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from optional to required.",
874                                             object_type, old_name, new_name));
875     }
876
877     size_t column_to_remove = new_property->table_column;
878     table->rename_column(old_property->table_column, new_name);
879     table->remove_column(column_to_remove);
880
881     // update table_column for each property since it may have shifted
882     for (auto& current_prop : target_object_schema->persisted_properties) {
883         if (current_prop.table_column == column_to_remove)
884             current_prop.table_column = old_property->table_column;
885         else if (current_prop.table_column > column_to_remove)
886             --current_prop.table_column;
887     }
888
889     // update nullability for column
890     if (is_nullable(new_property->type) && !is_nullable(old_property->type)) {
891         auto prop = *new_property;
892         prop.table_column = old_property->table_column;
893         make_property_optional(group, *table, prop);
894     }
895 }
896
897 InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version)
898 : logic_error(util::format("Provided schema version %1 is less than last set version %2.", new_version, old_version))
899 , m_old_version(old_version), m_new_version(new_version)
900 {
901 }
902
903 DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, std::string property)
904 : logic_error(util::format("Primary key property '%1.%2' has duplicate values after migration.", object_type, property))
905 , m_object_type(object_type), m_property(property)
906 {
907 }
908
909 SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
910 : std::logic_error([&] {
911     std::string message = "Schema validation failed due to the following errors:";
912     for (auto const& error : errors) {
913         message += std::string("\n- ") + error.what();
914     }
915     return message;
916 }())
917 {
918 }
919
920 SchemaMismatchException::SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors)
921 : std::logic_error([&] {
922     std::string message = "Migration is required due to the following errors:";
923     for (auto const& error : errors) {
924         message += std::string("\n- ") + error.what();
925     }
926     return message;
927 }())
928 {
929 }
930
931 InvalidSchemaChangeException::InvalidSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors)
932 : std::logic_error([&] {
933     std::string message = "The following changes cannot be made in additive-only schema mode:";
934     for (auto const& error : errors) {
935         message += std::string("\n- ") + error.what();
936     }
937     return message;
938 }())
939 {
940 }