added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / shared_realm.hpp
diff --git a/iOS/Pods/Realm/include/shared_realm.hpp b/iOS/Pods/Realm/include/shared_realm.hpp
new file mode 100644 (file)
index 0000000..67a0d59
--- /dev/null
@@ -0,0 +1,479 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2015 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_REALM_HPP
+#define REALM_REALM_HPP
+
+#include "execution_context_id.hpp"
+#include "schema.hpp"
+
+#include <realm/util/optional.hpp>
+#include <realm/binary_data.hpp>
+
+#if REALM_ENABLE_SYNC
+#include <realm/sync/client.hpp>
+#endif
+
+#include <memory>
+
+namespace realm {
+class BindingContext;
+class Group;
+class Realm;
+class Replication;
+class SharedGroup;
+class StringData;
+class Table;
+struct SyncConfig;
+class ThreadSafeReferenceBase;
+template <typename T> class ThreadSafeReference;
+struct VersionID;
+template<typename Table> class BasicRow;
+typedef BasicRow<Table> Row;
+typedef std::shared_ptr<Realm> SharedRealm;
+typedef std::weak_ptr<Realm> WeakRealm;
+
+namespace _impl {
+    class AnyHandover;
+    class CollectionNotifier;
+    class RealmCoordinator;
+    class RealmFriend;
+}
+
+// How to handle update_schema() being called on a file which has
+// already been initialized with a different schema
+enum class SchemaMode : uint8_t {
+    // If the schema version has increased, automatically apply all
+    // changes, then call the migration function.
+    //
+    // If the schema version has not changed, verify that the only
+    // changes are to add new tables and add or remove indexes, and then
+    // apply them if so. Does not call the migration function.
+    //
+    // This mode does not automatically remove tables which are not
+    // present in the schema that must be manually done in the migration
+    // function, to support sharing a Realm file between processes using
+    // different class subsets.
+    //
+    // This mode allows using schemata with different subsets of tables
+    // on different threads, but the tables which are shared must be
+    // identical.
+    Automatic,
+
+    // Open the file in immutable mode. Schema version must match the
+    // version in the file, and all tables present in the file must
+    // exactly match the specified schema, except for indexes. Tables
+    // are allowed to be missing from the file.
+    // WARNING: This is the original ReadOnly mode.
+    Immutable,
+
+    // Open the Realm in read-only mode, transactions are not allowed to
+    // be performed on the Realm instance. The schema of the existing Realm
+    // file won't be changed through this Realm instance. Extra tables and
+    // extra properties are allowed in the existing Realm schema. The
+    // difference of indexes is allowed as well. Other schema differences
+    // than those will cause an exception. This is different from Immutable
+    // mode, sync Realm can be opened with ReadOnly mode. Changes
+    // can be made to the Realm file through another writable Realm instance.
+    // Thus, notifications are also allowed in this mode.
+    // FIXME: Rename this to ReadOnly
+    // WARNING: This is not the original ReadOnly mode. The original ReadOnly
+    // has been renamed to Immutable.
+    ReadOnlyAlternative,
+
+    // If the schema version matches and the only schema changes are new
+    // tables and indexes being added or removed, apply the changes to
+    // the existing file.
+    // Otherwise delete the file and recreate it from scratch.
+    // The migration function is not used.
+    //
+    // This mode allows using schemata with different subsets of tables
+    // on different threads, but the tables which are shared must be
+    // identical.
+    ResetFile,
+
+    // The only changes allowed are to add new tables, add columns to
+    // existing tables, and to add or remove indexes from existing
+    // columns. Extra tables not present in the schema are ignored.
+    // Indexes are only added to or removed from existing columns if the
+    // schema version is greater than the existing one (and unlike other
+    // modes, the schema version is allowed to be less than the existing
+    // one).
+    // The migration function is not used.
+    //
+    // This mode allows updating the schema with additive changes even
+    // if the Realm is already open on another thread.
+    Additive,
+
+    // Verify that the schema version has increased, call the migraiton
+    // function, and then verify that the schema now matches.
+    // The migration function is mandatory for this mode.
+    //
+    // This mode requires that all threads and processes which open a
+    // file use identical schemata.
+    Manual
+};
+
+class Realm : public std::enable_shared_from_this<Realm> {
+public:
+    // A callback function to be called during a migration for Automatic and
+    // Manual schema modes. It is passed a SharedRealm at the version before
+    // the migration, the SharedRealm in the migration, and a mutable reference
+    // to the realm's Schema. Updating the schema with changes made within the
+    // migration function is only required if you wish to use the ObjectStore
+    // functions which take a Schema from within the migration function.
+    using MigrationFunction = std::function<void (SharedRealm old_realm, SharedRealm realm, Schema&)>;
+
+    // A callback function to be called the first time when a schema is created.
+    // It is passed a SharedRealm which is in a write transaction with the schema
+    // initialized. So it is possible to create some initial objects inside the callback
+    // with the given SharedRealm. Those changes will be committed together with the
+    // schema creation in a single transaction.
+    using DataInitializationFunction = std::function<void (SharedRealm realm)>;
+
+    // A callback function called when opening a SharedRealm when no cached
+    // version of this Realm exists. It is passed the total bytes allocated for
+    // the file (file size) and the total bytes used by data in the file.
+    // Return `true` to indicate that an attempt to compact the file should be made
+    // if it is possible to do so.
+    // Won't compact the file if another process is accessing it.
+    //
+    // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
+    // because it's not crash safe! It may corrupt your database if something fails
+    using ShouldCompactOnLaunchFunction = std::function<bool (uint64_t total_bytes, uint64_t used_bytes)>;
+
+    struct Config {
+        // Path and binary data are mutually exclusive
+        std::string path;
+        BinaryData realm_data;
+        // User-supplied encryption key. Must be either empty or 64 bytes.
+        std::vector<char> encryption_key;
+
+        bool in_memory = false;
+        SchemaMode schema_mode = SchemaMode::Automatic;
+
+        // Optional schema for the file.
+        // If the schema and schema version are supplied, update_schema() is
+        // called with the supplied schema, version and migration function when
+        // the Realm is actually opened and not just retrieved from the cache
+        util::Optional<Schema> schema;
+        uint64_t schema_version = -1;
+        MigrationFunction migration_function;
+
+        DataInitializationFunction initialization_function;
+
+        // A callback function called when opening a SharedRealm when no cached
+        // version of this Realm exists. It is passed the total bytes allocated for
+        // the file (file size) and the total bytes used by data in the file.
+        // Return `true` to indicate that an attempt to compact the file should be made
+        // if it is possible to do so.
+        // Won't compact the file if another process is accessing it.
+        //
+        // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
+        // because it's not crash safe! It may corrupt your database if something fails
+        ShouldCompactOnLaunchFunction should_compact_on_launch_function;
+
+        // WARNING: The original read_only() has been renamed to immutable().
+        bool immutable() const { return schema_mode == SchemaMode::Immutable; }
+        // FIXME: Rename this to read_only().
+        bool read_only_alternative() const { return schema_mode == SchemaMode::ReadOnlyAlternative; }
+
+        // The following are intended for internal/testing purposes and
+        // should not be publicly exposed in binding APIs
+
+        // If false, always return a new Realm instance, and don't return
+        // that Realm instance for other requests for a cached Realm. Useful
+        // for dynamic Realms and for tests that need multiple instances on
+        // one thread
+        bool cache = true;
+        // Throw an exception rather than automatically upgrading the file
+        // format. Used by the browser to warn the user that it'll modify
+        // the file.
+        bool disable_format_upgrade = false;
+        // Disable the background worker thread for producing change
+        // notifications. Useful for tests for those notifications so that
+        // everything can be done deterministically on one thread, and
+        // speeds up tests that don't need notifications.
+        bool automatic_change_notifications = true;
+
+        // The identifier of the abstract execution context in which this Realm will be used.
+        // If unset, the current thread's identifier will be used to identify the execution context.
+        util::Optional<AbstractExecutionContextID> execution_context;
+
+        /// A data structure storing data used to configure the Realm for sync support.
+        std::shared_ptr<SyncConfig> sync_config;
+
+        // FIXME: Realm Java manages sync at the Java level, so it needs to create Realms using the sync history
+        //        format.
+        bool force_sync_history = false;
+    };
+
+    // Get a cached Realm or create a new one if no cached copies exists
+    // Caching is done by path - mismatches for in_memory, schema mode or
+    // encryption key will raise an exception.
+    static SharedRealm get_shared_realm(Config config);
+
+    // Updates a Realm to a given schema, using the Realm's pre-set schema mode.
+    void update_schema(Schema schema, uint64_t version=0,
+                       MigrationFunction migration_function=nullptr,
+                       DataInitializationFunction initialization_function=nullptr,
+                       bool in_transaction=false);
+
+    // Set the schema used for this Realm, but do not update the file's schema
+    // if it is not compatible (and instead throw an error).
+    // Cannot be called multiple times on a single Realm instance or an instance
+    // which has already had update_schema() called on it.
+    void set_schema_subset(Schema schema);
+
+    // Read the schema version from the file specified by the given config, or
+    // ObjectStore::NotVersioned if it does not exist
+    static uint64_t get_schema_version(Config const& config);
+
+    Config const& config() const { return m_config; }
+    Schema const& schema() const { return m_schema; }
+    uint64_t schema_version() const { return m_schema_version; }
+
+    void begin_transaction();
+    void commit_transaction();
+    void cancel_transaction();
+    bool is_in_transaction() const noexcept;
+    bool is_in_read_transaction() const { return !!m_group; }
+
+    bool is_in_migration() const noexcept { return m_in_migration; }
+
+    bool refresh();
+    void set_auto_refresh(bool auto_refresh) { m_auto_refresh = auto_refresh; }
+    bool auto_refresh() const { return m_auto_refresh; }
+    void notify();
+
+    void invalidate();
+
+    // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
+    // because it's not crash safe! It may corrupt your database if something fails
+    bool compact();
+    void write_copy(StringData path, BinaryData encryption_key);
+    OwnedBinaryData write_copy();
+
+    void verify_thread() const;
+    void verify_in_write() const;
+    void verify_open() const;
+
+    bool can_deliver_notifications() const noexcept;
+
+    // Close this Realm and remove it from the cache. Continuing to use a
+    // Realm after closing it will throw ClosedRealmException
+    void close();
+    bool is_closed() const { return !m_read_only_group && !m_shared_group; }
+
+    // returns the file format version upgraded from if an upgrade took place
+    util::Optional<int> file_format_upgraded_from_version() const;
+
+    Realm(const Realm&) = delete;
+    Realm& operator=(const Realm&) = delete;
+    Realm(Realm&&) = delete;
+    Realm& operator=(Realm&&) = delete;
+    ~Realm();
+
+    // Construct a thread safe reference, pinning the version in the process.
+    template <typename T>
+    ThreadSafeReference<T> obtain_thread_safe_reference(T const& value);
+
+    // Advances the read transaction to the latest version, resolving the thread safe reference and unpinning the
+    // version in the process.
+    template <typename T>
+    T resolve_thread_safe_reference(ThreadSafeReference<T> reference);
+
+    static SharedRealm make_shared_realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator = nullptr) {
+        struct make_shared_enabler : public Realm {
+            make_shared_enabler(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator)
+            : Realm(std::move(config), std::move(coordinator)) { }
+        };
+        return std::make_shared<make_shared_enabler>(std::move(config), std::move(coordinator));
+    }
+
+    // Expose some internal functionality to other parts of the ObjectStore
+    // without making it public to everyone
+    class Internal {
+        friend class _impl::CollectionNotifier;
+        friend class _impl::RealmCoordinator;
+        friend class ThreadSafeReferenceBase;
+        friend class GlobalNotifier;
+        friend class TestHelper;
+
+        // ResultsNotifier and ListNotifier need access to the SharedGroup
+        // to be able to call the handover functions, which are not very wrappable
+        static const std::unique_ptr<SharedGroup>& get_shared_group(Realm& realm) { return realm.m_shared_group; }
+
+        // CollectionNotifier needs to be able to access the owning
+        // coordinator to wake up the worker thread when a callback is
+        // added, and coordinators need to be able to get themselves from a Realm
+        static _impl::RealmCoordinator& get_coordinator(Realm& realm) { return *realm.m_coordinator; }
+
+        static void begin_read(Realm&, VersionID);
+    };
+
+    static void open_with_config(const Config& config,
+                                 std::unique_ptr<Replication>& history,
+                                 std::unique_ptr<SharedGroup>& shared_group,
+                                 std::unique_ptr<Group>& read_only_group,
+                                 Realm* realm);
+
+private:
+    // `enable_shared_from_this` is unsafe with public constructors; use `make_shared_realm` instead
+    Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator);
+
+    Config m_config;
+    AnyExecutionContextID m_execution_context;
+    bool m_auto_refresh = true;
+
+    std::unique_ptr<Replication> m_history;
+    std::unique_ptr<SharedGroup> m_shared_group;
+    std::unique_ptr<Group> m_read_only_group;
+
+    Group *m_group = nullptr;
+
+    uint64_t m_schema_version;
+    Schema m_schema;
+    util::Optional<Schema> m_new_schema;
+    uint64_t m_schema_transaction_version = -1;
+
+    // FIXME: this should be a Dynamic schema mode instead, but only once
+    // that's actually fully working
+    bool m_dynamic_schema = true;
+
+    std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
+
+    // File format versions populated when a file format upgrade takes place during realm opening
+    int upgrade_initial_version = 0, upgrade_final_version = 0;
+
+    // True while sending the notifications caused by advancing the read
+    // transaction version, to avoid recursive notifications where possible
+    bool m_is_sending_notifications = false;
+
+    // True while we're performing a schema migration via this Realm instance
+    // to allow for different behavior (such as allowing modifications to
+    // primary key values)
+    bool m_in_migration = false;
+
+    void begin_read(VersionID);
+
+    void set_schema(Schema const& reference, Schema schema);
+    bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
+    bool schema_change_needs_write_transaction(Schema& schema, std::vector<SchemaChange>& changes, uint64_t version);
+    Schema get_full_schema();
+
+    // Ensure that m_schema and m_schema_version match that of the current
+    // version of the file
+    void read_schema_from_group_if_needed();
+
+    void add_schema_change_handler();
+    void cache_new_schema();
+    void notify_schema_changed();
+
+public:
+    std::unique_ptr<BindingContext> m_binding_context;
+
+    // FIXME private
+    Group& read_group();
+
+    Replication *history() { return m_history.get(); }
+
+    friend class _impl::RealmFriend;
+};
+
+class RealmFileException : public std::runtime_error {
+public:
+    enum class Kind {
+        /** Thrown for any I/O related exception scenarios when a realm is opened. */
+        AccessError,
+        /** Thrown if the history type of the on-disk Realm is unexpected or incompatible. */
+        BadHistoryError,
+        /** Thrown if the user does not have permission to open or create
+         the specified file in the specified access mode when the realm is opened. */
+        PermissionDenied,
+        /** Thrown if create_Always was specified and the file did already exist when the realm is opened. */
+        Exists,
+        /** Thrown if no_create was specified and the file was not found when the realm is opened. */
+        NotFound,
+        /** Thrown if the database file is currently open in another
+         process which cannot share with the current process due to an
+         architecture mismatch. */
+        IncompatibleLockFile,
+        /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
+        FormatUpgradeRequired,
+        /** Thrown if the local copy of a synced Realm file was created using an incompatible version of Realm.
+         The specified path is where the local file was moved for recovery. */
+        IncompatibleSyncedRealm,
+    };
+    RealmFileException(Kind kind, std::string path, std::string message, std::string underlying)
+    : std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {}
+    Kind kind() const { return m_kind; }
+    const std::string& path() const { return m_path; }
+    const std::string& underlying() const { return m_underlying; }
+
+private:
+    Kind m_kind;
+    std::string m_path;
+    std::string m_underlying;
+};
+
+class MismatchedConfigException : public std::logic_error {
+public:
+    MismatchedConfigException(StringData message, StringData path);
+};
+
+class MismatchedRealmException : public std::logic_error {
+public:
+    MismatchedRealmException(StringData message);
+};
+
+class InvalidTransactionException : public std::logic_error {
+public:
+    InvalidTransactionException(std::string message) : std::logic_error(message) {}
+};
+
+class IncorrectThreadException : public std::logic_error {
+public:
+    IncorrectThreadException() : std::logic_error("Realm accessed from incorrect thread.") {}
+};
+
+class ClosedRealmException : public std::logic_error {
+public:
+    ClosedRealmException() : std::logic_error("Cannot access realm that has been closed.") {}
+};
+
+class UninitializedRealmException : public std::runtime_error {
+public:
+    UninitializedRealmException(std::string message) : std::runtime_error(message) {}
+};
+
+class InvalidEncryptionKeyException : public std::logic_error {
+public:
+    InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
+};
+
+// FIXME Those are exposed for Java async queries, mainly because of handover related methods.
+class _impl::RealmFriend {
+public:
+    static SharedGroup& get_shared_group(Realm& realm);
+    static Group& read_group_to(Realm& realm, VersionID version);
+};
+
+} // namespace realm
+
+#endif /* defined(REALM_REALM_HPP) */