added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / shared_realm.hpp
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 #ifndef REALM_REALM_HPP
20 #define REALM_REALM_HPP
21
22 #include "execution_context_id.hpp"
23 #include "schema.hpp"
24
25 #include <realm/util/optional.hpp>
26 #include <realm/binary_data.hpp>
27
28 #if REALM_ENABLE_SYNC
29 #include <realm/sync/client.hpp>
30 #endif
31
32 #include <memory>
33
34 namespace realm {
35 class BindingContext;
36 class Group;
37 class Realm;
38 class Replication;
39 class SharedGroup;
40 class StringData;
41 class Table;
42 struct SyncConfig;
43 class ThreadSafeReferenceBase;
44 template <typename T> class ThreadSafeReference;
45 struct VersionID;
46 template<typename Table> class BasicRow;
47 typedef BasicRow<Table> Row;
48 typedef std::shared_ptr<Realm> SharedRealm;
49 typedef std::weak_ptr<Realm> WeakRealm;
50
51 namespace _impl {
52     class AnyHandover;
53     class CollectionNotifier;
54     class RealmCoordinator;
55     class RealmFriend;
56 }
57
58 // How to handle update_schema() being called on a file which has
59 // already been initialized with a different schema
60 enum class SchemaMode : uint8_t {
61     // If the schema version has increased, automatically apply all
62     // changes, then call the migration function.
63     //
64     // If the schema version has not changed, verify that the only
65     // changes are to add new tables and add or remove indexes, and then
66     // apply them if so. Does not call the migration function.
67     //
68     // This mode does not automatically remove tables which are not
69     // present in the schema that must be manually done in the migration
70     // function, to support sharing a Realm file between processes using
71     // different class subsets.
72     //
73     // This mode allows using schemata with different subsets of tables
74     // on different threads, but the tables which are shared must be
75     // identical.
76     Automatic,
77
78     // Open the file in immutable mode. Schema version must match the
79     // version in the file, and all tables present in the file must
80     // exactly match the specified schema, except for indexes. Tables
81     // are allowed to be missing from the file.
82     // WARNING: This is the original ReadOnly mode.
83     Immutable,
84
85     // Open the Realm in read-only mode, transactions are not allowed to
86     // be performed on the Realm instance. The schema of the existing Realm
87     // file won't be changed through this Realm instance. Extra tables and
88     // extra properties are allowed in the existing Realm schema. The
89     // difference of indexes is allowed as well. Other schema differences
90     // than those will cause an exception. This is different from Immutable
91     // mode, sync Realm can be opened with ReadOnly mode. Changes
92     // can be made to the Realm file through another writable Realm instance.
93     // Thus, notifications are also allowed in this mode.
94     // FIXME: Rename this to ReadOnly
95     // WARNING: This is not the original ReadOnly mode. The original ReadOnly
96     // has been renamed to Immutable.
97     ReadOnlyAlternative,
98
99     // If the schema version matches and the only schema changes are new
100     // tables and indexes being added or removed, apply the changes to
101     // the existing file.
102     // Otherwise delete the file and recreate it from scratch.
103     // The migration function is not used.
104     //
105     // This mode allows using schemata with different subsets of tables
106     // on different threads, but the tables which are shared must be
107     // identical.
108     ResetFile,
109
110     // The only changes allowed are to add new tables, add columns to
111     // existing tables, and to add or remove indexes from existing
112     // columns. Extra tables not present in the schema are ignored.
113     // Indexes are only added to or removed from existing columns if the
114     // schema version is greater than the existing one (and unlike other
115     // modes, the schema version is allowed to be less than the existing
116     // one).
117     // The migration function is not used.
118     //
119     // This mode allows updating the schema with additive changes even
120     // if the Realm is already open on another thread.
121     Additive,
122
123     // Verify that the schema version has increased, call the migraiton
124     // function, and then verify that the schema now matches.
125     // The migration function is mandatory for this mode.
126     //
127     // This mode requires that all threads and processes which open a
128     // file use identical schemata.
129     Manual
130 };
131
132 class Realm : public std::enable_shared_from_this<Realm> {
133 public:
134     // A callback function to be called during a migration for Automatic and
135     // Manual schema modes. It is passed a SharedRealm at the version before
136     // the migration, the SharedRealm in the migration, and a mutable reference
137     // to the realm's Schema. Updating the schema with changes made within the
138     // migration function is only required if you wish to use the ObjectStore
139     // functions which take a Schema from within the migration function.
140     using MigrationFunction = std::function<void (SharedRealm old_realm, SharedRealm realm, Schema&)>;
141
142     // A callback function to be called the first time when a schema is created.
143     // It is passed a SharedRealm which is in a write transaction with the schema
144     // initialized. So it is possible to create some initial objects inside the callback
145     // with the given SharedRealm. Those changes will be committed together with the
146     // schema creation in a single transaction.
147     using DataInitializationFunction = std::function<void (SharedRealm realm)>;
148
149     // A callback function called when opening a SharedRealm when no cached
150     // version of this Realm exists. It is passed the total bytes allocated for
151     // the file (file size) and the total bytes used by data in the file.
152     // Return `true` to indicate that an attempt to compact the file should be made
153     // if it is possible to do so.
154     // Won't compact the file if another process is accessing it.
155     //
156     // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
157     // because it's not crash safe! It may corrupt your database if something fails
158     using ShouldCompactOnLaunchFunction = std::function<bool (uint64_t total_bytes, uint64_t used_bytes)>;
159
160     struct Config {
161         // Path and binary data are mutually exclusive
162         std::string path;
163         BinaryData realm_data;
164         // User-supplied encryption key. Must be either empty or 64 bytes.
165         std::vector<char> encryption_key;
166
167         bool in_memory = false;
168         SchemaMode schema_mode = SchemaMode::Automatic;
169
170         // Optional schema for the file.
171         // If the schema and schema version are supplied, update_schema() is
172         // called with the supplied schema, version and migration function when
173         // the Realm is actually opened and not just retrieved from the cache
174         util::Optional<Schema> schema;
175         uint64_t schema_version = -1;
176         MigrationFunction migration_function;
177
178         DataInitializationFunction initialization_function;
179
180         // A callback function called when opening a SharedRealm when no cached
181         // version of this Realm exists. It is passed the total bytes allocated for
182         // the file (file size) and the total bytes used by data in the file.
183         // Return `true` to indicate that an attempt to compact the file should be made
184         // if it is possible to do so.
185         // Won't compact the file if another process is accessing it.
186         //
187         // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
188         // because it's not crash safe! It may corrupt your database if something fails
189         ShouldCompactOnLaunchFunction should_compact_on_launch_function;
190
191         // WARNING: The original read_only() has been renamed to immutable().
192         bool immutable() const { return schema_mode == SchemaMode::Immutable; }
193         // FIXME: Rename this to read_only().
194         bool read_only_alternative() const { return schema_mode == SchemaMode::ReadOnlyAlternative; }
195
196         // The following are intended for internal/testing purposes and
197         // should not be publicly exposed in binding APIs
198
199         // If false, always return a new Realm instance, and don't return
200         // that Realm instance for other requests for a cached Realm. Useful
201         // for dynamic Realms and for tests that need multiple instances on
202         // one thread
203         bool cache = true;
204         // Throw an exception rather than automatically upgrading the file
205         // format. Used by the browser to warn the user that it'll modify
206         // the file.
207         bool disable_format_upgrade = false;
208         // Disable the background worker thread for producing change
209         // notifications. Useful for tests for those notifications so that
210         // everything can be done deterministically on one thread, and
211         // speeds up tests that don't need notifications.
212         bool automatic_change_notifications = true;
213
214         // The identifier of the abstract execution context in which this Realm will be used.
215         // If unset, the current thread's identifier will be used to identify the execution context.
216         util::Optional<AbstractExecutionContextID> execution_context;
217
218         /// A data structure storing data used to configure the Realm for sync support.
219         std::shared_ptr<SyncConfig> sync_config;
220
221         // FIXME: Realm Java manages sync at the Java level, so it needs to create Realms using the sync history
222         //        format.
223         bool force_sync_history = false;
224     };
225
226     // Get a cached Realm or create a new one if no cached copies exists
227     // Caching is done by path - mismatches for in_memory, schema mode or
228     // encryption key will raise an exception.
229     static SharedRealm get_shared_realm(Config config);
230
231     // Updates a Realm to a given schema, using the Realm's pre-set schema mode.
232     void update_schema(Schema schema, uint64_t version=0,
233                        MigrationFunction migration_function=nullptr,
234                        DataInitializationFunction initialization_function=nullptr,
235                        bool in_transaction=false);
236
237     // Set the schema used for this Realm, but do not update the file's schema
238     // if it is not compatible (and instead throw an error).
239     // Cannot be called multiple times on a single Realm instance or an instance
240     // which has already had update_schema() called on it.
241     void set_schema_subset(Schema schema);
242
243     // Read the schema version from the file specified by the given config, or
244     // ObjectStore::NotVersioned if it does not exist
245     static uint64_t get_schema_version(Config const& config);
246
247     Config const& config() const { return m_config; }
248     Schema const& schema() const { return m_schema; }
249     uint64_t schema_version() const { return m_schema_version; }
250
251     void begin_transaction();
252     void commit_transaction();
253     void cancel_transaction();
254     bool is_in_transaction() const noexcept;
255     bool is_in_read_transaction() const { return !!m_group; }
256
257     bool is_in_migration() const noexcept { return m_in_migration; }
258
259     bool refresh();
260     void set_auto_refresh(bool auto_refresh) { m_auto_refresh = auto_refresh; }
261     bool auto_refresh() const { return m_auto_refresh; }
262     void notify();
263
264     void invalidate();
265
266     // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
267     // because it's not crash safe! It may corrupt your database if something fails
268     bool compact();
269     void write_copy(StringData path, BinaryData encryption_key);
270     OwnedBinaryData write_copy();
271
272     void verify_thread() const;
273     void verify_in_write() const;
274     void verify_open() const;
275
276     bool can_deliver_notifications() const noexcept;
277
278     // Close this Realm and remove it from the cache. Continuing to use a
279     // Realm after closing it will throw ClosedRealmException
280     void close();
281     bool is_closed() const { return !m_read_only_group && !m_shared_group; }
282
283     // returns the file format version upgraded from if an upgrade took place
284     util::Optional<int> file_format_upgraded_from_version() const;
285
286     Realm(const Realm&) = delete;
287     Realm& operator=(const Realm&) = delete;
288     Realm(Realm&&) = delete;
289     Realm& operator=(Realm&&) = delete;
290     ~Realm();
291
292     // Construct a thread safe reference, pinning the version in the process.
293     template <typename T>
294     ThreadSafeReference<T> obtain_thread_safe_reference(T const& value);
295
296     // Advances the read transaction to the latest version, resolving the thread safe reference and unpinning the
297     // version in the process.
298     template <typename T>
299     T resolve_thread_safe_reference(ThreadSafeReference<T> reference);
300
301     static SharedRealm make_shared_realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator = nullptr) {
302         struct make_shared_enabler : public Realm {
303             make_shared_enabler(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator)
304             : Realm(std::move(config), std::move(coordinator)) { }
305         };
306         return std::make_shared<make_shared_enabler>(std::move(config), std::move(coordinator));
307     }
308
309     // Expose some internal functionality to other parts of the ObjectStore
310     // without making it public to everyone
311     class Internal {
312         friend class _impl::CollectionNotifier;
313         friend class _impl::RealmCoordinator;
314         friend class ThreadSafeReferenceBase;
315         friend class GlobalNotifier;
316         friend class TestHelper;
317
318         // ResultsNotifier and ListNotifier need access to the SharedGroup
319         // to be able to call the handover functions, which are not very wrappable
320         static const std::unique_ptr<SharedGroup>& get_shared_group(Realm& realm) { return realm.m_shared_group; }
321
322         // CollectionNotifier needs to be able to access the owning
323         // coordinator to wake up the worker thread when a callback is
324         // added, and coordinators need to be able to get themselves from a Realm
325         static _impl::RealmCoordinator& get_coordinator(Realm& realm) { return *realm.m_coordinator; }
326
327         static void begin_read(Realm&, VersionID);
328     };
329
330     static void open_with_config(const Config& config,
331                                  std::unique_ptr<Replication>& history,
332                                  std::unique_ptr<SharedGroup>& shared_group,
333                                  std::unique_ptr<Group>& read_only_group,
334                                  Realm* realm);
335
336 private:
337     // `enable_shared_from_this` is unsafe with public constructors; use `make_shared_realm` instead
338     Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator);
339
340     Config m_config;
341     AnyExecutionContextID m_execution_context;
342     bool m_auto_refresh = true;
343
344     std::unique_ptr<Replication> m_history;
345     std::unique_ptr<SharedGroup> m_shared_group;
346     std::unique_ptr<Group> m_read_only_group;
347
348     Group *m_group = nullptr;
349
350     uint64_t m_schema_version;
351     Schema m_schema;
352     util::Optional<Schema> m_new_schema;
353     uint64_t m_schema_transaction_version = -1;
354
355     // FIXME: this should be a Dynamic schema mode instead, but only once
356     // that's actually fully working
357     bool m_dynamic_schema = true;
358
359     std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
360
361     // File format versions populated when a file format upgrade takes place during realm opening
362     int upgrade_initial_version = 0, upgrade_final_version = 0;
363
364     // True while sending the notifications caused by advancing the read
365     // transaction version, to avoid recursive notifications where possible
366     bool m_is_sending_notifications = false;
367
368     // True while we're performing a schema migration via this Realm instance
369     // to allow for different behavior (such as allowing modifications to
370     // primary key values)
371     bool m_in_migration = false;
372
373     void begin_read(VersionID);
374
375     void set_schema(Schema const& reference, Schema schema);
376     bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
377     bool schema_change_needs_write_transaction(Schema& schema, std::vector<SchemaChange>& changes, uint64_t version);
378     Schema get_full_schema();
379
380     // Ensure that m_schema and m_schema_version match that of the current
381     // version of the file
382     void read_schema_from_group_if_needed();
383
384     void add_schema_change_handler();
385     void cache_new_schema();
386     void notify_schema_changed();
387
388 public:
389     std::unique_ptr<BindingContext> m_binding_context;
390
391     // FIXME private
392     Group& read_group();
393
394     Replication *history() { return m_history.get(); }
395
396     friend class _impl::RealmFriend;
397 };
398
399 class RealmFileException : public std::runtime_error {
400 public:
401     enum class Kind {
402         /** Thrown for any I/O related exception scenarios when a realm is opened. */
403         AccessError,
404         /** Thrown if the history type of the on-disk Realm is unexpected or incompatible. */
405         BadHistoryError,
406         /** Thrown if the user does not have permission to open or create
407          the specified file in the specified access mode when the realm is opened. */
408         PermissionDenied,
409         /** Thrown if create_Always was specified and the file did already exist when the realm is opened. */
410         Exists,
411         /** Thrown if no_create was specified and the file was not found when the realm is opened. */
412         NotFound,
413         /** Thrown if the database file is currently open in another
414          process which cannot share with the current process due to an
415          architecture mismatch. */
416         IncompatibleLockFile,
417         /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
418         FormatUpgradeRequired,
419         /** Thrown if the local copy of a synced Realm file was created using an incompatible version of Realm.
420          The specified path is where the local file was moved for recovery. */
421         IncompatibleSyncedRealm,
422     };
423     RealmFileException(Kind kind, std::string path, std::string message, std::string underlying)
424     : std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {}
425     Kind kind() const { return m_kind; }
426     const std::string& path() const { return m_path; }
427     const std::string& underlying() const { return m_underlying; }
428
429 private:
430     Kind m_kind;
431     std::string m_path;
432     std::string m_underlying;
433 };
434
435 class MismatchedConfigException : public std::logic_error {
436 public:
437     MismatchedConfigException(StringData message, StringData path);
438 };
439
440 class MismatchedRealmException : public std::logic_error {
441 public:
442     MismatchedRealmException(StringData message);
443 };
444
445 class InvalidTransactionException : public std::logic_error {
446 public:
447     InvalidTransactionException(std::string message) : std::logic_error(message) {}
448 };
449
450 class IncorrectThreadException : public std::logic_error {
451 public:
452     IncorrectThreadException() : std::logic_error("Realm accessed from incorrect thread.") {}
453 };
454
455 class ClosedRealmException : public std::logic_error {
456 public:
457     ClosedRealmException() : std::logic_error("Cannot access realm that has been closed.") {}
458 };
459
460 class UninitializedRealmException : public std::runtime_error {
461 public:
462     UninitializedRealmException(std::string message) : std::runtime_error(message) {}
463 };
464
465 class InvalidEncryptionKeyException : public std::logic_error {
466 public:
467     InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
468 };
469
470 // FIXME Those are exposed for Java async queries, mainly because of handover related methods.
471 class _impl::RealmFriend {
472 public:
473     static SharedGroup& get_shared_group(Realm& realm);
474     static Group& read_group_to(Realm& realm, VersionID version);
475 };
476
477 } // namespace realm
478
479 #endif /* defined(REALM_REALM_HPP) */