1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2017 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
19 #include "sync/partial_sync.hpp"
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"
28 #include <realm/util/scope_exit.hpp>
31 namespace partial_sync {
35 constexpr const char* result_sets_type_name = "__ResultSets";
37 void update_schema(Group& group, Property matches_property)
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}};
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)
54 auto required_changes = current_schema.compare(desired_schema);
55 if (!required_changes.empty())
56 ObjectStore::apply_additive_changes(group, required_changes, true);
59 } // unnamed namespace
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)
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");
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");
71 auto matches_property = object_class + "_matches";
73 // The object schema must outlive `object` below.
74 std::unique_ptr<ObjectSchema> result_sets_schema;
77 realm->begin_transaction();
78 auto cleanup = util::make_scope_exit([&]() noexcept {
79 if (realm->is_in_transaction())
80 realm->cancel_transaction();
83 update_schema(realm->read_group(),
84 Property(matches_property, PropertyType::Object|PropertyType::Array, object_class));
86 result_sets_schema = std::make_unique<ObjectSchema>(realm->read_group(), result_sets_type_name);
89 raw_object = Object::create<util::Any>(context, realm, *result_sets_schema,
91 {"matches_property", matches_property},
93 {"status", int64_t(0)},
94 {"error_message", std::string()},
95 {"query_parse_counter", int64_t(0)},
98 realm->commit_transaction();
101 auto object = std::make_shared<_impl::NotificationWrapper<Object>>(std::move(raw_object));
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 {
108 callback(Results(), error);
114 auto status = any_cast<int64_t>(object->get_property_value<util::Any>(context, "status"));
116 // Still computing...
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);
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))));
129 object->add_notification_callback(std::move(notification_callback));
132 } // namespace partial_sync