added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / object_accessor.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_OS_OBJECT_ACCESSOR_HPP
20 #define REALM_OS_OBJECT_ACCESSOR_HPP
21
22 #include "object.hpp"
23
24 #include "feature_checks.hpp"
25 #include "list.hpp"
26 #include "object_schema.hpp"
27 #include "object_store.hpp"
28 #include "results.hpp"
29 #include "schema.hpp"
30 #include "shared_realm.hpp"
31
32 #include <realm/link_view.hpp>
33 #include <realm/util/assert.hpp>
34 #include <realm/table_view.hpp>
35
36 #if REALM_ENABLE_SYNC
37 #include <realm/sync/object.hpp>
38 #endif // REALM_ENABLE_SYNC
39
40 #include <string>
41
42 namespace realm {
43 template <typename ValueType, typename ContextType>
44 void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueType value, bool try_update)
45 {
46     verify_attached();
47     m_realm->verify_in_write();
48     auto& property = property_for_name(prop_name);
49
50     // Modifying primary keys is allowed in migrations to make it possible to
51     // add a new primary key to a type (or change the property type), but it
52     // is otherwise considered the immutable identity of the row
53     if (property.is_primary && !m_realm->is_in_migration())
54         throw std::logic_error("Cannot modify primary key after creation");
55
56     set_property_value_impl(ctx, property, value, try_update);
57 }
58
59 template <typename ValueType, typename ContextType>
60 ValueType Object::get_property_value(ContextType& ctx, StringData prop_name)
61 {
62     return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
63 }
64
65 template <typename ValueType, typename ContextType>
66 void Object::set_property_value_impl(ContextType& ctx, const Property &property,
67                                      ValueType value, bool try_update, bool is_default)
68 {
69     ctx.will_change(*this, property);
70
71     auto& table = *m_row.get_table();
72     size_t col = property.table_column;
73     size_t row = m_row.get_index();
74     if (is_nullable(property.type) && ctx.is_null(value)) {
75         if (property.type == PropertyType::Object) {
76             if (!is_default)
77                 table.nullify_link(col, row);
78         }
79         else {
80             table.set_null(col, row, is_default);
81         }
82
83         ctx.did_change();
84         return;
85     }
86
87     if (is_array(property.type)) {
88         if (property.type == PropertyType::LinkingObjects)
89             throw ReadOnlyPropertyException(m_object_schema->name, property.name);
90
91         ContextType child_ctx(ctx, property);
92         List list(m_realm, *m_row.get_table(), col, m_row.get_index());
93         list.assign(child_ctx, value, try_update);
94         ctx.did_change();
95         return;
96     }
97
98     switch (property.type & ~PropertyType::Nullable) {
99         case PropertyType::Object: {
100             ContextType child_ctx(ctx, property);
101             auto link = child_ctx.template unbox<RowExpr>(value, true, try_update);
102             table.set_link(col, row, link.get_index(), is_default);
103             break;
104         }
105         case PropertyType::Bool:
106             table.set(col, row, ctx.template unbox<bool>(value), is_default);
107             break;
108         case PropertyType::Int:
109             table.set(col, row, ctx.template unbox<int64_t>(value), is_default);
110             break;
111         case PropertyType::Float:
112             table.set(col, row, ctx.template unbox<float>(value), is_default);
113             break;
114         case PropertyType::Double:
115             table.set(col, row, ctx.template unbox<double>(value), is_default);
116             break;
117         case PropertyType::String:
118             table.set(col, row, ctx.template unbox<StringData>(value), is_default);
119             break;
120         case PropertyType::Data:
121             table.set(col, row, ctx.template unbox<BinaryData>(value), is_default);
122             break;
123         case PropertyType::Any:
124             throw std::logic_error("not supported");
125         case PropertyType::Date:
126             table.set(col, row, ctx.template unbox<Timestamp>(value), is_default);
127             break;
128         default:
129             REALM_COMPILER_HINT_UNREACHABLE();
130     }
131     ctx.did_change();
132 }
133
134 template <typename ValueType, typename ContextType>
135 ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property)
136 {
137     verify_attached();
138
139     size_t column = property.table_column;
140     if (is_nullable(property.type) && m_row.is_null(column))
141         return ctx.null_value();
142     if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
143         return ctx.box(List(m_realm, *m_row.get_table(), column, m_row.get_index()));
144
145     switch (property.type & ~PropertyType::Flags) {
146         case PropertyType::Bool:   return ctx.box(m_row.get_bool(column));
147         case PropertyType::Int:    return ctx.box(m_row.get_int(column));
148         case PropertyType::Float:  return ctx.box(m_row.get_float(column));
149         case PropertyType::Double: return ctx.box(m_row.get_double(column));
150         case PropertyType::String: return ctx.box(m_row.get_string(column));
151         case PropertyType::Data:   return ctx.box(m_row.get_binary(column));
152         case PropertyType::Date:   return ctx.box(m_row.get_timestamp(column));
153         case PropertyType::Any:    return ctx.box(m_row.get_mixed(column));
154         case PropertyType::Object: {
155             auto linkObjectSchema = m_realm->schema().find(property.object_type);
156             TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), property.object_type);
157             return ctx.box(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column))));
158         }
159         case PropertyType::LinkingObjects: {
160             auto target_object_schema = m_realm->schema().find(property.object_type);
161             auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
162             TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name);
163             auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column);
164             return ctx.box(Results(m_realm, std::move(tv)));
165         }
166         default: REALM_UNREACHABLE();
167     }
168 }
169
170 template<typename ValueType, typename ContextType>
171 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
172                       StringData object_type, ValueType value,
173                       bool try_update, Row* out_row)
174 {
175     auto object_schema = realm->schema().find(object_type);
176     REALM_ASSERT(object_schema != realm->schema().end());
177     return create(ctx, realm, *object_schema, value, try_update, out_row);
178 }
179
180 template<typename ValueType, typename ContextType>
181 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
182                       ObjectSchema const& object_schema, ValueType value,
183                       bool try_update, Row* out_row)
184 {
185     realm->verify_in_write();
186
187     // get or create our accessor
188     bool created = false;
189
190     // try to get existing row if updating
191     size_t row_index = realm::not_found;
192     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
193
194     bool skip_primary = true;
195     if (auto primary_prop = object_schema.primary_key_property()) {
196         // search for existing object based on primary key type
197         auto primary_value = ctx.value_for_property(value, primary_prop->name,
198                                                     primary_prop - &object_schema.persisted_properties[0]);
199         if (!primary_value)
200             primary_value = ctx.default_value_for_property(object_schema, primary_prop->name);
201         if (!primary_value) {
202             if (!is_nullable(primary_prop->type))
203                 throw MissingPropertyValueException(object_schema.name, primary_prop->name);
204             primary_value = ctx.null_value();
205         }
206         row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, *primary_value);
207
208         if (row_index == realm::not_found) {
209             created = true;
210             if (primary_prop->type == PropertyType::Int) {
211 #if REALM_ENABLE_SYNC
212                 row_index = sync::create_object_with_primary_key(realm->read_group(), *table, ctx.template unbox<util::Optional<int64_t>>(*primary_value));
213 #else
214                 row_index = table->add_empty_row();
215                 if (ctx.is_null(*primary_value))
216                     table->set_null_unique(primary_prop->table_column, row_index);
217                 else
218                     table->set_unique(primary_prop->table_column, row_index, ctx.template unbox<int64_t>(*primary_value));
219 #endif // REALM_ENABLE_SYNC
220             }
221             else if (primary_prop->type == PropertyType::String) {
222                 auto value = ctx.template unbox<StringData>(*primary_value);
223 #if REALM_ENABLE_SYNC
224                 row_index = sync::create_object_with_primary_key(realm->read_group(), *table, value);
225 #else
226                 row_index = table->add_empty_row();
227                 table->set_unique(primary_prop->table_column, row_index, value);
228 #endif // REALM_ENABLE_SYNC
229             }
230             else {
231                 REALM_TERMINATE("Unsupported primary key type.");
232             }
233         }
234         else if (!try_update) {
235             if (realm->is_in_migration()) {
236                 // Creating objects with duplicate primary keys is allowed in migrations
237                 // as long as there are no duplicates at the end, as adding an entirely
238                 // new column which is the PK will inherently result in duplicates at first
239                 row_index = table->add_empty_row();
240                 created = true;
241                 skip_primary = false;
242             }
243             else {
244                 throw std::logic_error(util::format("Attempting to create an object of type '%1' with an existing primary key value '%2'.",
245                                                     object_schema.name, ctx.print(*primary_value)));
246             }
247         }
248     }
249     else {
250 #if REALM_ENABLE_SYNC
251         row_index = sync::create_object(realm->read_group(), *table);
252 #else
253         row_index = table->add_empty_row();
254 #endif // REALM_ENABLE_SYNC
255         created = true;
256     }
257
258     // populate
259     Object object(realm, object_schema, table->get(row_index));
260     if (out_row)
261         *out_row = object.row();
262     for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
263         auto& prop = object_schema.persisted_properties[i];
264         if (skip_primary && prop.is_primary)
265             continue;
266
267         auto v = ctx.value_for_property(value, prop.name, i);
268         if (!created && !v)
269             continue;
270
271         bool is_default = false;
272         if (!v) {
273             v = ctx.default_value_for_property(object_schema, prop.name);
274             is_default = true;
275         }
276         if ((!v || ctx.is_null(*v)) && !is_nullable(prop.type) && !is_array(prop.type)) {
277             if (prop.is_primary || !ctx.allow_missing(value))
278                 throw MissingPropertyValueException(object_schema.name, prop.name);
279         }
280         if (v)
281             object.set_property_value_impl(ctx, prop, *v, try_update, is_default);
282     }
283     return object;
284 }
285
286 template<typename ValueType, typename ContextType>
287 Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
288                       StringData object_type, ValueType primary_value)
289 {
290     auto object_schema = realm->schema().find(object_type);
291     REALM_ASSERT(object_schema != realm->schema().end());
292     return get_for_primary_key(ctx, realm, *object_schema, primary_value);
293 }
294
295 template<typename ValueType, typename ContextType>
296 Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
297                                    const ObjectSchema &object_schema,
298                                    ValueType primary_value)
299 {
300     auto primary_prop = object_schema.primary_key_property();
301     if (!primary_prop) {
302         throw MissingPrimaryKeyException(object_schema.name);
303     }
304
305     auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
306     if (!table)
307         return Object(realm, object_schema, RowExpr());
308     auto row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, primary_value);
309
310     return Object(realm, object_schema, row_index == realm::not_found ? Row() : Row(table->get(row_index)));
311 }
312
313 template<typename ValueType, typename ContextType>
314 size_t Object::get_for_primary_key_impl(ContextType& ctx, Table const& table,
315                                         const Property &primary_prop,
316                                         ValueType primary_value) {
317     bool is_null = ctx.is_null(primary_value);
318     if (is_null && !is_nullable(primary_prop.type))
319         throw std::logic_error("Invalid null value for non-nullable primary key.");
320     if (primary_prop.type == PropertyType::String) {
321         return table.find_first(primary_prop.table_column,
322                                 ctx.template unbox<StringData>(primary_value));
323     }
324     if (is_nullable(primary_prop.type))
325         return table.find_first(primary_prop.table_column,
326                                 ctx.template unbox<util::Optional<int64_t>>(primary_value));
327     return table.find_first(primary_prop.table_column,
328                             ctx.template unbox<int64_t>(primary_value));
329 }
330
331 } // namespace realm
332
333 #endif // REALM_OS_OBJECT_ACCESSOR_HPP