added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / sync / partial_sync.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2017 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 "sync/partial_sync.hpp"
20
21 #include "impl/notification_wrapper.hpp"
22 #include "impl/object_accessor_impl.hpp"
23 #include "object_schema.hpp"
24 #include "results.hpp"
25 #include "shared_realm.hpp"
26 #include "sync/sync_config.hpp"
27
28 #include <realm/util/scope_exit.hpp>
29
30 namespace realm {
31 namespace partial_sync {
32
33 namespace {
34
35 constexpr const char* result_sets_type_name = "__ResultSets";
36
37 void update_schema(Group& group, Property matches_property)
38 {
39     Schema current_schema;
40     std::string table_name = ObjectStore::table_name_for_object_type(result_sets_type_name);
41     if (group.has_table(table_name))
42         current_schema = {ObjectSchema{group, result_sets_type_name}};
43
44     Schema desired_schema({
45         ObjectSchema(result_sets_type_name, {
46             {"matches_property", PropertyType::String},
47             {"query", PropertyType::String},
48             {"status", PropertyType::Int},
49             {"error_message", PropertyType::String},
50             {"query_parse_counter", PropertyType::Int},
51             std::move(matches_property)
52         })
53     });
54     auto required_changes = current_schema.compare(desired_schema);
55     if (!required_changes.empty())
56         ObjectStore::apply_additive_changes(group, required_changes, true);
57 }
58
59 } // unnamed namespace
60
61 void register_query(std::shared_ptr<Realm> realm, const std::string &object_class, const std::string &query,
62                     std::function<void (Results, std::exception_ptr)> callback)
63 {
64     auto sync_config = realm->config().sync_config;
65     if (!sync_config || !sync_config->is_partial)
66         throw std::logic_error("A partial sync query can only be registered in a partially synced Realm");
67
68     if (realm->schema().find(object_class) == realm->schema().end())
69         throw std::logic_error("A partial sync query can only be registered for a type that exists in the Realm's schema");
70
71     auto matches_property = object_class + "_matches";
72
73     // The object schema must outlive `object` below.
74     std::unique_ptr<ObjectSchema> result_sets_schema;
75     Object raw_object;
76     {
77         realm->begin_transaction();
78         auto cleanup = util::make_scope_exit([&]() noexcept {
79             if (realm->is_in_transaction())
80                 realm->cancel_transaction();
81         });
82
83         update_schema(realm->read_group(),
84                       Property(matches_property, PropertyType::Object|PropertyType::Array, object_class));
85
86         result_sets_schema = std::make_unique<ObjectSchema>(realm->read_group(), result_sets_type_name);
87
88         CppContext context;
89         raw_object = Object::create<util::Any>(context, realm, *result_sets_schema,
90                                                AnyDict{
91                                                    {"matches_property", matches_property},
92                                                    {"query", query},
93                                                    {"status", int64_t(0)},
94                                                    {"error_message", std::string()},
95                                                    {"query_parse_counter", int64_t(0)},
96                                                }, false);
97
98         realm->commit_transaction();
99     }
100
101     auto object = std::make_shared<_impl::NotificationWrapper<Object>>(std::move(raw_object));
102
103     // Observe the new object and notify listener when the results are complete (status != 0).
104     auto notification_callback = [object, matches_property,
105                                   result_sets_schema=std::move(result_sets_schema),
106                                   callback=std::move(callback)](CollectionChangeSet, std::exception_ptr error) mutable {
107         if (error) {
108             callback(Results(), error);
109             object.reset();
110             return;
111         }
112
113         CppContext context;
114         auto status = any_cast<int64_t>(object->get_property_value<util::Any>(context, "status"));
115         if (status == 0) {
116             // Still computing...
117             return;
118         } else if (status == 1) {
119             // Finished successfully.
120             auto list = any_cast<List>(object->get_property_value<util::Any>(context, matches_property));
121             callback(list.as_results(), nullptr);
122         } else {
123             // Finished with error.
124             auto message = any_cast<std::string>(object->get_property_value<util::Any>(context, "error_message"));
125             callback(Results(), std::make_exception_ptr(std::runtime_error(std::move(message))));
126         }
127         object.reset();
128     };
129     object->add_notification_callback(std::move(notification_callback));
130 }
131
132 } // namespace partial_sync
133 } // namespace realm