added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / schema.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 "schema.hpp"
20
21 #include "object_schema.hpp"
22 #include "object_store.hpp"
23 #include "object_schema.hpp"
24 #include "property.hpp"
25
26 #include <algorithm>
27
28 using namespace realm;
29
30 namespace realm {
31 bool operator==(Schema const& a, Schema const& b)
32 {
33     return static_cast<Schema::base const&>(a) == static_cast<Schema::base const&>(b);
34 }
35 }
36
37 Schema::Schema() = default;
38 Schema::~Schema() = default;
39 Schema::Schema(Schema const&) = default;
40 Schema::Schema(Schema &&) = default;
41 Schema& Schema::operator=(Schema const&) = default;
42 Schema& Schema::operator=(Schema&&) = default;
43
44 Schema::Schema(std::initializer_list<ObjectSchema> types) : Schema(base(types)) { }
45
46 Schema::Schema(base types) : base(std::move(types))
47 {
48     std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) {
49         return lft.name < rgt.name;
50     });
51 }
52
53 Schema::iterator Schema::find(StringData name)
54 {
55     auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) {
56         return lft.name < rgt;
57     });
58     if (it != end() && it->name != name) {
59         it = end();
60     }
61     return it;
62 }
63
64 Schema::const_iterator Schema::find(StringData name) const
65 {
66     return const_cast<Schema *>(this)->find(name);
67 }
68
69 Schema::iterator Schema::find(ObjectSchema const& object) noexcept
70 {
71     return find(object.name);
72 }
73
74 Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept
75 {
76     return const_cast<Schema *>(this)->find(object);
77 }
78
79 void Schema::validate() const
80 {
81     std::vector<ObjectSchemaValidationException> exceptions;
82     for (auto const& object : *this) {
83         object.validate(*this, exceptions);
84     }
85
86     if (exceptions.size()) {
87         throw SchemaValidationException(exceptions);
88     }
89 }
90
91 namespace {
92 struct IsNotRemoveProperty {
93     bool operator()(SchemaChange sc) const { return sc.visit(*this); }
94     bool operator()(schema_change::RemoveProperty) const { return false; }
95     template<typename T> bool operator()(T) const { return true; }
96 };
97 struct GetRemovedColumn {
98     size_t operator()(SchemaChange sc) const { return sc.visit(*this); }
99     size_t operator()(schema_change::RemoveProperty p) const { return p.property->table_column; }
100     template<typename T> size_t operator()(T) const { REALM_COMPILER_HINT_UNREACHABLE(); }
101 };
102 }
103
104 static void compare(ObjectSchema const& existing_schema,
105                     ObjectSchema const& target_schema,
106                     std::vector<SchemaChange>& changes)
107 {
108     for (auto& current_prop : existing_schema.persisted_properties) {
109         auto target_prop = target_schema.property_for_name(current_prop.name);
110
111         if (!target_prop) {
112             changes.emplace_back(schema_change::RemoveProperty{&existing_schema, &current_prop});
113             continue;
114         }
115         if (target_schema.property_is_computed(*target_prop)) {
116             changes.emplace_back(schema_change::RemoveProperty{&existing_schema, &current_prop});
117             continue;
118         }
119         if (current_prop.type != target_prop->type ||
120             current_prop.object_type != target_prop->object_type ||
121             is_array(current_prop.type) != is_array(target_prop->type)) {
122
123             changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, &current_prop, target_prop});
124             continue;
125         }
126         if (is_nullable(current_prop.type) != is_nullable(target_prop->type)) {
127             if (is_nullable(current_prop.type))
128                 changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, &current_prop});
129             else
130                 changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, &current_prop});
131         }
132         if (target_prop->requires_index()) {
133             if (!current_prop.is_indexed)
134                 changes.emplace_back(schema_change::AddIndex{&existing_schema, &current_prop});
135         }
136         else if (current_prop.requires_index()) {
137             changes.emplace_back(schema_change::RemoveIndex{&existing_schema, &current_prop});
138         }
139     }
140
141     if (existing_schema.primary_key != target_schema.primary_key) {
142         changes.emplace_back(schema_change::ChangePrimaryKey{&existing_schema, target_schema.primary_key_property()});
143     }
144
145     for (auto& target_prop : target_schema.persisted_properties) {
146         if (!existing_schema.property_for_name(target_prop.name)) {
147             changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop});
148         }
149     }
150
151     // Move all RemovePropertys to the end and sort in descending order of
152     // column index, as removing a column will shift all columns after that one
153     auto it = std::partition(begin(changes), end(changes), IsNotRemoveProperty{});
154     std::sort(it, end(changes),
155               [](auto a, auto b) { return GetRemovedColumn()(a) > GetRemovedColumn()(b); });
156 }
157
158 template<typename T, typename U, typename Func>
159 void Schema::zip_matching(T&& a, U&& b, Func&& func)
160 {
161     size_t i = 0, j = 0;
162     while (i < a.size() && j < b.size()) {
163         auto& object_schema = a[i];
164         auto& matching_schema = b[j];
165         int cmp = object_schema.name.compare(matching_schema.name);
166         if (cmp == 0) {
167             func(&object_schema, &matching_schema);
168             ++i;
169             ++j;
170         }
171         else if (cmp < 0) {
172             func(&object_schema, nullptr);
173             ++i;
174         }
175         else {
176             func(nullptr, &matching_schema);
177             ++j;
178         }
179     }
180     for (; i < a.size(); ++i)
181         func(&a[i], nullptr);
182     for (; j < b.size(); ++j)
183         func(nullptr, &b[j]);
184
185 }
186
187 std::vector<SchemaChange> Schema::compare(Schema const& target_schema) const
188 {
189     std::vector<SchemaChange> changes;
190
191     // Add missing tables
192     zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
193         if (target && !existing) {
194             changes.emplace_back(schema_change::AddTable{target});
195         }
196     });
197
198     // Modify columns
199     zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
200         if (target && existing)
201             ::compare(*existing, *target, changes);
202         else if (target) {
203             // Target is a new table -- add all properties
204             changes.emplace_back(schema_change::AddInitialProperties{target});
205         }
206         // nothing for tables in existing but not target
207     });
208     return changes;
209 }
210
211 void Schema::copy_table_columns_from(realm::Schema const& other)
212 {
213     zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) {
214         if (!existing || !other)
215             return;
216
217         for (auto& current_prop : other->persisted_properties) {
218             auto target_prop = existing->property_for_name(current_prop.name);
219             if (target_prop) {
220                 target_prop->table_column = current_prop.table_column;
221             }
222         }
223     });
224 }
225
226 namespace realm {
227 bool operator==(SchemaChange const& lft, SchemaChange const& rgt)
228 {
229     if (lft.m_kind != rgt.m_kind)
230         return false;
231
232     using namespace schema_change;
233     struct Visitor {
234         SchemaChange const& value;
235
236         #define REALM_SC_COMPARE(type, ...) \
237             bool operator()(type rgt) const \
238             { \
239                 auto cmp = [](auto&& v) { return std::tie(__VA_ARGS__); }; \
240                 return cmp(value.type) == cmp(rgt); \
241             }
242
243         REALM_SC_COMPARE(AddIndex, v.object, v.property)
244         REALM_SC_COMPARE(AddProperty, v.object, v.property)
245         REALM_SC_COMPARE(AddInitialProperties, v.object)
246         REALM_SC_COMPARE(AddTable, v.object)
247         REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property)
248         REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property)
249         REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property)
250         REALM_SC_COMPARE(MakePropertyRequired, v.object, v.property)
251         REALM_SC_COMPARE(RemoveIndex, v.object, v.property)
252         REALM_SC_COMPARE(RemoveProperty, v.object, v.property)
253
254         #undef REALM_SC_COMPARE
255     } visitor{lft};
256     return rgt.visit(visitor);
257 }
258 } // namespace realm