mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-11 14:54:33 +00:00
Merge pull request #535 from realm/tg/update-object-store
Update to the latest objectstore code
This commit is contained in:
commit
47c1c2d400
@ -4,7 +4,7 @@ set -e
|
||||
set -o pipefail
|
||||
|
||||
# Set to "latest" for the latest build.
|
||||
: ${REALM_CORE_VERSION:=1.1.2}
|
||||
: ${REALM_CORE_VERSION:=1.4.0}
|
||||
|
||||
if [ "$1" = '--version' ]; then
|
||||
echo "$REALM_CORE_VERSION"
|
||||
@ -22,7 +22,7 @@ if [ "$1" = 'node' ]; then
|
||||
fi
|
||||
else
|
||||
CORE_DIR='core'
|
||||
CORE_DOWNLOAD_FILE="realm-core-$REALM_CORE_VERSION.tar.bz2"
|
||||
CORE_DOWNLOAD_FILE="realm-core-$REALM_CORE_VERSION.tar.xz"
|
||||
fi
|
||||
|
||||
# Start current working directory at the root of the project.
|
||||
|
@ -207,7 +207,7 @@ void ListClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, *list, false));
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, list->snapshot()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -147,7 +147,7 @@ struct NativeAccessor {
|
||||
return ListClass<T>::create_instance(ctx, std::move(list));
|
||||
}
|
||||
static ValueType from_results(ContextType ctx, realm::Results results) {
|
||||
return ResultsClass<T>::create_instance(ctx, results);
|
||||
return ResultsClass<T>::create_instance(ctx, std::move(results));
|
||||
}
|
||||
static Mixed to_mixed(ContextType ctx, ValueType &val) {
|
||||
throw std::runtime_error("'Any' type is unsupported");
|
||||
|
@ -38,10 +38,8 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
|
||||
static ObjectType create_instance(ContextType, const realm::Results &, bool live = true);
|
||||
static ObjectType create_instance(ContextType, const realm::List &, bool live = true);
|
||||
static ObjectType create_instance(ContextType, SharedRealm, const ObjectSchema &, bool live = true);
|
||||
static ObjectType create_instance(ContextType, SharedRealm, const ObjectSchema &, Query, bool live = true);
|
||||
static ObjectType create_instance(ContextType, realm::Results);
|
||||
static ObjectType create_instance(ContextType, SharedRealm, const ObjectSchema &);
|
||||
|
||||
template<typename U>
|
||||
static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]);
|
||||
@ -74,33 +72,14 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, const realm::Results &results, bool live) {
|
||||
auto new_results = new realm::Results(results);
|
||||
new_results->set_live(live);
|
||||
|
||||
return create_object<T, ResultsClass<T>>(ctx, new_results);
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, realm::Results results) {
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(std::move(results)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, const realm::List &list, bool live) {
|
||||
return create_instance(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, bool live) {
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema) {
|
||||
auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
|
||||
auto results = new realm::Results(realm, object_schema, *table);
|
||||
results->set_live(live);
|
||||
|
||||
return create_object<T, ResultsClass<T>>(ctx, results);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) {
|
||||
auto results = new realm::Results(realm, object_schema, std::move(query));
|
||||
results->set_live(live);
|
||||
|
||||
return create_object<T, ResultsClass<T>>(ctx, results);
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(realm, *table));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -111,18 +90,13 @@ typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &co
|
||||
auto const &realm = collection.get_realm();
|
||||
auto const &object_schema = collection.get_object_schema();
|
||||
|
||||
std::vector<ValueType> args;
|
||||
args.reserve(argc - 1);
|
||||
|
||||
for (size_t i = 1; i < argc; i++) {
|
||||
args.push_back(arguments[i]);
|
||||
}
|
||||
std::vector<ValueType> args(&arguments[1], &arguments[argc]);
|
||||
|
||||
parser::Predicate predicate = parser::parse(query_string);
|
||||
query_builder::ArgumentConverter<ValueType, ContextType> converter(ctx, realm, args);
|
||||
query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name);
|
||||
|
||||
return create_instance(ctx, realm, object_schema, std::move(query));
|
||||
return create_instance(ctx, realm::Results(realm, std::move(query)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -179,7 +153,7 @@ typename T::Object ResultsClass<T>::create_sorted(ContextType ctx, const U &coll
|
||||
columns.push_back(prop->table_column);
|
||||
}
|
||||
|
||||
auto results = new realm::Results(realm, object_schema, collection.get_query(), {std::move(columns), std::move(ascending)});
|
||||
auto results = new realm::Results(realm, collection.get_query(), {std::move(columns), std::move(ascending)});
|
||||
return create_object<T, ResultsClass<T>>(ctx, results);
|
||||
}
|
||||
|
||||
@ -209,7 +183,7 @@ void ResultsClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t a
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, *results, false));
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, results->snapshot()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -1,12 +1,36 @@
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
set(CMAKE_CXX_EXTENSIONS off)
|
||||
add_compile_options(-Wall -DREALM_HAVE_CONFIG)
|
||||
add_compile_options("$<$<CONFIG:DEBUG>:-DREALM_DEBUG>")
|
||||
add_compile_options("$<$<CONFIG:COVERAGE>:-DREALM_DEBUG>")
|
||||
add_compile_options(
|
||||
-DREALM_HAVE_CONFIG
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-missing-field-initializers
|
||||
-Wempty-body
|
||||
-Wparentheses
|
||||
-Wunknown-pragmas
|
||||
-Wunreachable-code
|
||||
)
|
||||
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
add_compile_options(
|
||||
-Wassign-enum
|
||||
-Wbool-conversion
|
||||
-Wconditional-uninitialized
|
||||
-Wconstant-conversion
|
||||
-Wenum-conversion
|
||||
-Wint-conversion
|
||||
-Wmissing-prototypes
|
||||
-Wnewline-eof
|
||||
-Wshorten-64-to-32
|
||||
-Wimplicit-fallthrough
|
||||
)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_GENERATOR} STREQUAL "Ninja")
|
||||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
||||
|
@ -28,8 +28,8 @@ function(use_realm_core version_or_path_to_source)
|
||||
endfunction()
|
||||
|
||||
function(download_realm_core core_version)
|
||||
set(core_url "https://static.realm.io/downloads/core/realm-core-${core_version}.tar.bz2")
|
||||
set(core_tarball_name "realm-core-${core_version}.tar.bz2")
|
||||
set(core_url "https://static.realm.io/downloads/core/realm-core-${core_version}.tar.xz")
|
||||
set(core_tarball_name "realm-core-${core_version}.tar.xz")
|
||||
set(core_temp_tarball "/tmp/${core_tarball_name}")
|
||||
set(core_directory_parent "${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_FILES_DIRECTORY}")
|
||||
set(core_directory "${core_directory_parent}/realm-core-${core_version}")
|
||||
|
@ -10,7 +10,7 @@ include(CompilerFlags)
|
||||
include(Sanitizers)
|
||||
|
||||
include(RealmCore)
|
||||
set(REALM_CORE_VERSION "1.1.0" CACHE STRING "")
|
||||
set(REALM_CORE_VERSION "1.2.0" CACHE STRING "")
|
||||
use_realm_core(${REALM_CORE_VERSION})
|
||||
|
||||
set(PEGTL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/pegtl)
|
||||
|
@ -40,6 +40,7 @@ set(HEADERS
|
||||
parser/parser.hpp
|
||||
parser/query_builder.hpp
|
||||
util/atomic_shared_ptr.hpp
|
||||
util/compiler.hpp
|
||||
util/format.hpp
|
||||
util/thread_id.hpp
|
||||
util/thread_local.hpp)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "impl/external_commit_helper.hpp"
|
||||
#include "impl/transact_log_handler.hpp"
|
||||
#include "impl/weak_realm_notifier.hpp"
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
|
@ -64,7 +64,7 @@ class TransactLogValidationMixin {
|
||||
REALM_NOINLINE
|
||||
void schema_error()
|
||||
{
|
||||
throw std::runtime_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way");
|
||||
throw std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way");
|
||||
}
|
||||
|
||||
// Throw an exception if the currently modified table already existed before
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
#include "impl/list_notifier.hpp"
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "results.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "shared_realm.hpp"
|
||||
#include "util/format.hpp"
|
||||
|
||||
@ -37,13 +39,25 @@ List& List::operator=(const List&) = default;
|
||||
List::List(List&&) = default;
|
||||
List& List::operator=(List&&) = default;
|
||||
|
||||
List::List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept
|
||||
List::List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept
|
||||
: m_realm(std::move(r))
|
||||
, m_object_schema(&s)
|
||||
, m_link_view(std::move(l))
|
||||
{
|
||||
}
|
||||
|
||||
const ObjectSchema& List::get_object_schema() const
|
||||
{
|
||||
verify_attached();
|
||||
|
||||
if (!m_object_schema) {
|
||||
auto object_type = ObjectStore::object_type_for_table_name(m_link_view->get_target_table().get_name());
|
||||
auto it = m_realm->config().schema->find(object_type);
|
||||
REALM_ASSERT(it != m_realm->config().schema->end());
|
||||
m_object_schema = &*it;
|
||||
}
|
||||
return *m_object_schema;
|
||||
}
|
||||
|
||||
Query List::get_query() const
|
||||
{
|
||||
verify_attached();
|
||||
@ -73,7 +87,7 @@ bool List::is_valid() const
|
||||
void List::verify_attached() const
|
||||
{
|
||||
if (!is_valid()) {
|
||||
throw InvalidatedException{};
|
||||
throw InvalidatedException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,13 +186,19 @@ void List::delete_all()
|
||||
Results List::sort(SortOrder order)
|
||||
{
|
||||
verify_attached();
|
||||
return Results(m_realm, *m_object_schema, m_link_view, util::none, std::move(order));
|
||||
return Results(m_realm, m_link_view, util::none, std::move(order));
|
||||
}
|
||||
|
||||
Results List::filter(Query q)
|
||||
{
|
||||
verify_attached();
|
||||
return Results(m_realm, *m_object_schema, m_link_view, get_query().and_query(std::move(q)));
|
||||
return Results(m_realm, m_link_view, get_query().and_query(std::move(q)));
|
||||
}
|
||||
|
||||
Results List::snapshot() const
|
||||
{
|
||||
verify_attached();
|
||||
return Results(m_realm, m_link_view).snapshot();
|
||||
}
|
||||
|
||||
// These definitions rely on that LinkViews are interned by core
|
||||
|
@ -40,7 +40,7 @@ struct SortOrder;
|
||||
class List {
|
||||
public:
|
||||
List() noexcept;
|
||||
List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept;
|
||||
List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept;
|
||||
~List();
|
||||
|
||||
List(const List&);
|
||||
@ -50,7 +50,7 @@ public:
|
||||
|
||||
const std::shared_ptr<Realm>& get_realm() const { return m_realm; }
|
||||
Query get_query() const;
|
||||
const ObjectSchema& get_object_schema() const { return *m_object_schema; }
|
||||
const ObjectSchema& get_object_schema() const;
|
||||
size_t get_origin_row_index() const;
|
||||
|
||||
bool is_valid() const;
|
||||
@ -75,6 +75,9 @@ public:
|
||||
Results sort(SortOrder order);
|
||||
Results filter(Query q);
|
||||
|
||||
// Return a Results representing a snapshot of this List.
|
||||
Results snapshot() const;
|
||||
|
||||
bool operator==(List const& rgt) const noexcept;
|
||||
|
||||
NotificationToken add_notification_callback(CollectionChangeCallback cb);
|
||||
@ -92,8 +95,8 @@ public:
|
||||
// The List object has been invalidated (due to the Realm being invalidated,
|
||||
// or the containing object being deleted)
|
||||
// All non-noexcept functions can throw this
|
||||
struct InvalidatedException : public std::runtime_error {
|
||||
InvalidatedException() : std::runtime_error("Access to invalidated List object") {}
|
||||
struct InvalidatedException : public std::logic_error {
|
||||
InvalidatedException() : std::logic_error("Access to invalidated List object") {}
|
||||
};
|
||||
|
||||
// The input index parameter was out of bounds
|
||||
@ -105,7 +108,7 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Realm> m_realm;
|
||||
const ObjectSchema* m_object_schema;
|
||||
mutable const ObjectSchema* m_object_schema = nullptr;
|
||||
LinkViewRef m_link_view;
|
||||
_impl::CollectionNotifier::Handle<_impl::CollectionNotifier> m_notifier;
|
||||
|
||||
|
@ -123,50 +123,43 @@ namespace realm {
|
||||
//
|
||||
// Deprecated
|
||||
//
|
||||
static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); }
|
||||
static Mixed to_mixed(ContextType, ValueType&) { throw std::logic_error("'Any' type is unsupported"); }
|
||||
};
|
||||
|
||||
class InvalidatedObjectException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidatedObjectException(const std::string object_type, const std::string message) : std::runtime_error(message), object_type(object_type) {}
|
||||
struct InvalidatedObjectException : public std::logic_error {
|
||||
InvalidatedObjectException(const std::string& object_type, const std::string& message) :
|
||||
std::logic_error(message), object_type(object_type) {}
|
||||
const std::string object_type;
|
||||
};
|
||||
|
||||
class InvalidPropertyException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidPropertyException(const std::string object_type, const std::string property_name, const std::string message) : std::runtime_error(message), object_type(object_type), property_name(property_name) {}
|
||||
struct InvalidPropertyException : public std::logic_error {
|
||||
InvalidPropertyException(const std::string& object_type, const std::string& property_name, const std::string& message) :
|
||||
std::logic_error(message), object_type(object_type), property_name(property_name) {}
|
||||
const std::string object_type;
|
||||
const std::string property_name;
|
||||
};
|
||||
|
||||
class MissingPropertyValueException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingPropertyValueException(const std::string object_type, const std::string property_name, const std::string message) : std::runtime_error(message), object_type(object_type), property_name(property_name) {}
|
||||
struct MissingPropertyValueException : public std::logic_error {
|
||||
MissingPropertyValueException(const std::string& object_type, const std::string& property_name, const std::string& message) :
|
||||
std::logic_error(message), object_type(object_type), property_name(property_name) {}
|
||||
const std::string object_type;
|
||||
const std::string property_name;
|
||||
};
|
||||
|
||||
class MissingPrimaryKeyException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingPrimaryKeyException(const std::string object_type, const std::string message) : std::runtime_error(message), object_type(object_type) {}
|
||||
struct MissingPrimaryKeyException : public std::logic_error {
|
||||
MissingPrimaryKeyException(const std::string& object_type, const std::string& message) : std::logic_error(message), object_type(object_type) {}
|
||||
const std::string object_type;
|
||||
};
|
||||
|
||||
class ReadOnlyPropertyValueException : public std::runtime_error {
|
||||
public:
|
||||
ReadOnlyPropertyValueException(const std::string& object_type, const std::string& property_name, const std::string& message)
|
||||
: std::runtime_error(message), object_type(object_type), property_name(property_name) {}
|
||||
struct ReadOnlyPropertyException : public std::logic_error {
|
||||
ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name, const std::string& message) :
|
||||
std::logic_error(message), object_type(object_type), property_name(property_name) {}
|
||||
const std::string object_type;
|
||||
const std::string property_name;
|
||||
};
|
||||
|
||||
class MutationOutsideTransactionException : public std::runtime_error {
|
||||
public:
|
||||
MutationOutsideTransactionException(std::string message) : std::runtime_error(message) {}
|
||||
struct MutationOutsideTransactionException : public std::logic_error {
|
||||
MutationOutsideTransactionException(const std::string& message) : std::logic_error(message) {}
|
||||
};
|
||||
|
||||
//
|
||||
@ -265,9 +258,9 @@ namespace realm {
|
||||
break;
|
||||
}
|
||||
case PropertyType::LinkingObjects:
|
||||
throw ReadOnlyPropertyValueException(m_object_schema->name, property.name,
|
||||
util::format("Cannot modify read-only property '%1.%2'",
|
||||
m_object_schema->name, property.name));
|
||||
throw ReadOnlyPropertyException(m_object_schema->name, property.name,
|
||||
util::format("Cannot modify read-only property '%1.%2'",
|
||||
m_object_schema->name, property.name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,15 +302,14 @@ namespace realm {
|
||||
return Accessor::from_object(ctx, std::move(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column)))));
|
||||
}
|
||||
case PropertyType::Array: {
|
||||
auto arrayObjectSchema = m_realm->config().schema->find(property.object_type);
|
||||
return Accessor::from_list(ctx, std::move(List(m_realm, *arrayObjectSchema, static_cast<LinkViewRef>(m_row.get_linklist(column)))));
|
||||
return Accessor::from_list(ctx, std::move(List(m_realm, static_cast<LinkViewRef>(m_row.get_linklist(column)))));
|
||||
}
|
||||
case PropertyType::LinkingObjects: {
|
||||
auto target_object_schema = m_realm->config().schema->find(property.object_type);
|
||||
auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
|
||||
TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name);
|
||||
auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column);
|
||||
Results results(m_realm, *m_object_schema, std::move(tv), {});
|
||||
Results results(m_realm, std::move(tv), {});
|
||||
return Accessor::from_results(ctx, std::move(results));
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "object_store.hpp"
|
||||
|
||||
#include "object_schema.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "util/format.hpp"
|
||||
|
||||
@ -123,7 +124,7 @@ StringData ObjectStore::object_type_for_table_name(StringData table_name) {
|
||||
}
|
||||
|
||||
std::string ObjectStore::table_name_for_object_type(StringData object_type) {
|
||||
return std::string(c_object_table_prefix) + object_type.data();
|
||||
return std::string(c_object_table_prefix) + static_cast<std::string>(object_type);
|
||||
}
|
||||
|
||||
TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) {
|
||||
@ -507,7 +508,7 @@ bool ObjectStore::is_empty(const Group *group) {
|
||||
InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) :
|
||||
m_old_version(old_version), m_new_version(new_version)
|
||||
{
|
||||
m_what = util::format("Provided schema version %1 is less than last set version %2.", old_version, new_version);
|
||||
m_what = util::format("Provided schema version %1 is less than last set version %2.", new_version, old_version);
|
||||
}
|
||||
|
||||
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property) :
|
||||
@ -527,16 +528,16 @@ DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string
|
||||
SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
|
||||
: m_validation_errors(errors)
|
||||
{
|
||||
m_what = "Schema validation failed due to the following errors: ";
|
||||
m_what = "Schema validation failed due to the following errors:";
|
||||
for (auto const& error : errors) {
|
||||
m_what += std::string("\n- ") + error.what();
|
||||
}
|
||||
}
|
||||
|
||||
SchemaMismatchException::SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors) :
|
||||
m_validation_errors(errors)
|
||||
m_validation_errors(errors)
|
||||
{
|
||||
m_what = "Migration is required due to the following errors: ";
|
||||
m_what = "Migration is required due to the following errors:";
|
||||
for (auto const& error : errors) {
|
||||
m_what += std::string("\n- ") + error.what();
|
||||
}
|
||||
@ -546,7 +547,7 @@ PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string
|
||||
Property const& property)
|
||||
: ObjectSchemaPropertyException(object_type, property)
|
||||
{
|
||||
m_what = util::format("Can't index property %1.%2: indexing a property of type '%3' is currently not supported",
|
||||
m_what = util::format("Can't index property %1.%2: indexing a property of type '%3' is currently not supported.",
|
||||
object_type, property.name, string_for_property_type(property.type));
|
||||
}
|
||||
|
||||
@ -572,7 +573,7 @@ InvalidNullabilityException::InvalidNullabilityException(std::string const& obje
|
||||
case PropertyType::Any:
|
||||
case PropertyType::Array:
|
||||
case PropertyType::LinkingObjects:
|
||||
m_what = util::format("Property '%1' of type '%2' cannoy be nullable",
|
||||
m_what = util::format("Property '%1' of type '%2' cannot be nullable.",
|
||||
property.name, string_for_property_type(property.type));
|
||||
break;
|
||||
case PropertyType::Int:
|
||||
@ -599,17 +600,17 @@ MismatchedPropertiesException::MismatchedPropertiesException(std::string const&
|
||||
ObjectSchemaValidationException(object_type), m_old_property(old_property), m_new_property(new_property)
|
||||
{
|
||||
if (new_property.type != old_property.type) {
|
||||
m_what = util::format("Property types for '%1' property doe not match. Old type '%2', new type '%3'",
|
||||
m_what = util::format("Property types for '%1' property do not match. Old type '%2', new type '%3'.",
|
||||
old_property.name,
|
||||
string_for_property_type(old_property.type),
|
||||
string_for_property_type(new_property.type));
|
||||
}
|
||||
else if (new_property.object_type != old_property.object_type) {
|
||||
m_what = util::format("Target object type for property '%1' do not match. Old type '%2', new type '%3'",
|
||||
m_what = util::format("Target object type for property '%1' do not match. Old type '%2', new type '%3'.",
|
||||
old_property.name, old_property.object_type, new_property.object_type);
|
||||
}
|
||||
else if (new_property.is_nullable != old_property.is_nullable) {
|
||||
m_what = util::format("Nullability for property '%1' has been changed from %2 to %3",
|
||||
m_what = util::format("Nullability for property '%1' has been changed from %2 to %3.",
|
||||
old_property.name,
|
||||
old_property.is_nullable, new_property.is_nullable);
|
||||
}
|
||||
@ -645,17 +646,17 @@ InvalidLinkingObjectsPropertyException::InvalidLinkingObjectsPropertyException(T
|
||||
{
|
||||
switch (error_type) {
|
||||
case Type::OriginPropertyDoesNotExist:
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist",
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist.",
|
||||
property.object_type, property.link_origin_property_name,
|
||||
object_type, property.name);
|
||||
break;
|
||||
case Type::OriginPropertyIsNotALink:
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link",
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link.",
|
||||
property.object_type, property.link_origin_property_name,
|
||||
object_type, property.name);
|
||||
break;
|
||||
case Type::OriginPropertyInvalidLinkTarget:
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to a different class",
|
||||
m_what = util::format("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to a different class.",
|
||||
property.object_type, property.link_origin_property_name,
|
||||
object_type, property.name);
|
||||
break;
|
||||
|
@ -19,17 +19,20 @@
|
||||
#ifndef REALM_OBJECT_STORE_HPP
|
||||
#define REALM_OBJECT_STORE_HPP
|
||||
|
||||
#include "schema.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
#include <realm/table_ref.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace realm {
|
||||
class Group;
|
||||
class ObjectSchema;
|
||||
class ObjectSchemaValidationException;
|
||||
class Schema;
|
||||
class StringData;
|
||||
|
||||
class ObjectStore {
|
||||
public:
|
||||
|
@ -201,14 +201,14 @@ template< typename Rule >
|
||||
struct action : nothing< Rule > {};
|
||||
|
||||
#ifdef REALM_PARSER_PRINT_TOKENS
|
||||
#define DEBUG_PRINT_TOKEN(string) std::cout << string << std::endl
|
||||
#define DEBUG_PRINT_TOKEN(string) do { std::cout << string << std::endl; while (0)
|
||||
#else
|
||||
#define DEBUG_PRINT_TOKEN(string)
|
||||
#define DEBUG_PRINT_TOKEN(string) do { static_cast<void>(string); } while (0)
|
||||
#endif
|
||||
|
||||
template<> struct action< and_op >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input&, ParserState& state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<and>");
|
||||
state.next_type = Predicate::Type::And;
|
||||
@ -217,7 +217,7 @@ template<> struct action< and_op >
|
||||
|
||||
template<> struct action< or_op >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input&, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<or>");
|
||||
state.next_type = Predicate::Type::Or;
|
||||
@ -227,7 +227,7 @@ template<> struct action< or_op >
|
||||
|
||||
#define EXPRESSION_ACTION(rule, type) \
|
||||
template<> struct action< rule > { \
|
||||
static void apply( const input & in, ParserState & state ) { \
|
||||
static void apply(const input& in, ParserState& state) { \
|
||||
DEBUG_PRINT_TOKEN(in.string()); \
|
||||
state.add_expression(Expression(type, in.string())); }};
|
||||
|
||||
@ -239,10 +239,10 @@ EXPRESSION_ACTION(true_value, Expression::Type::True)
|
||||
EXPRESSION_ACTION(false_value, Expression::Type::False)
|
||||
EXPRESSION_ACTION(null_value, Expression::Type::Null)
|
||||
EXPRESSION_ACTION(argument_index, Expression::Type::Argument)
|
||||
|
||||
|
||||
template<> struct action< true_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input& in, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(in.string());
|
||||
state.current_group()->cpnd.sub_predicates.emplace_back(Predicate::Type::True);
|
||||
@ -251,7 +251,7 @@ template<> struct action< true_pred >
|
||||
|
||||
template<> struct action< false_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input& in, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(in.string());
|
||||
state.current_group()->cpnd.sub_predicates.emplace_back(Predicate::Type::False);
|
||||
@ -260,7 +260,7 @@ template<> struct action< false_pred >
|
||||
|
||||
#define OPERATOR_ACTION(rule, oper) \
|
||||
template<> struct action< rule > { \
|
||||
static void apply( const input & in, ParserState & state ) { \
|
||||
static void apply(const input& in, ParserState& state) { \
|
||||
DEBUG_PRINT_TOKEN(in.string()); \
|
||||
state.last_predicate()->cmpr.op = oper; }};
|
||||
|
||||
@ -273,19 +273,19 @@ OPERATOR_ACTION(lt, Predicate::Operator::LessThan)
|
||||
OPERATOR_ACTION(begins, Predicate::Operator::BeginsWith)
|
||||
OPERATOR_ACTION(ends, Predicate::Operator::EndsWith)
|
||||
OPERATOR_ACTION(contains, Predicate::Operator::Contains)
|
||||
|
||||
|
||||
template<> struct action< case_insensitive >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input& in, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(in.string());
|
||||
state.last_predicate()->cmpr.option = Predicate::OperatorOption::CaseInsensitive;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> struct action< one< '(' > >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input&, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<begin_group>");
|
||||
state.add_predicate_to_current_group(Predicate::Type::And);
|
||||
@ -295,7 +295,7 @@ template<> struct action< one< '(' > >
|
||||
|
||||
template<> struct action< group_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input&, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<end_group>");
|
||||
state.group_stack.pop_back();
|
||||
@ -304,7 +304,7 @@ template<> struct action< group_pred >
|
||||
|
||||
template<> struct action< not_pre >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
static void apply(const input&, ParserState & state)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<not>");
|
||||
state.negate_next = true;
|
||||
@ -317,9 +317,9 @@ struct error_message_control : pegtl::normal< Rule >
|
||||
static const std::string error_message;
|
||||
|
||||
template< typename Input, typename ... States >
|
||||
static void raise( const Input & in, States && ... )
|
||||
static void raise(const Input& in, States&&...)
|
||||
{
|
||||
throw pegtl::parse_error( error_message, in );
|
||||
throw pegtl::parse_error(error_message, in);
|
||||
}
|
||||
};
|
||||
|
||||
@ -351,5 +351,3 @@ void analyze_grammar()
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
|
@ -21,16 +21,18 @@
|
||||
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "util/compiler.hpp"
|
||||
#include "util/format.hpp"
|
||||
|
||||
#include <realm.hpp>
|
||||
#include <assert.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace realm {
|
||||
namespace query_builder {
|
||||
using namespace realm;
|
||||
using namespace parser;
|
||||
using namespace query_builder;
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
T stot(std::string const& s) {
|
||||
std::istringstream iss(s);
|
||||
@ -45,7 +47,7 @@ T stot(std::string const& s) {
|
||||
// check a precondition and throw an exception if it is not met
|
||||
// this should be used iff the condition being false indicates a bug in the caller
|
||||
// of the function checking its preconditions
|
||||
#define precondition(condition, message) if (!__builtin_expect(condition, 1)) { throw std::runtime_error(message); }
|
||||
#define precondition(condition, message) if (!__builtin_expect(condition, 1)) { throw std::logic_error(message); }
|
||||
|
||||
// FIXME: TrueExpression and FalseExpression should be supported by core in some way
|
||||
struct TrueExpression : realm::Expression {
|
||||
@ -148,7 +150,7 @@ void add_numeric_constraint_to_query(Query& query,
|
||||
query.and_query(lhs != rhs);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for numeric queries.");
|
||||
throw std::logic_error("Unsupported operator for numeric queries.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +164,7 @@ void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType
|
||||
query.and_query(lhs != rhs);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for numeric queries.");
|
||||
throw std::logic_error("Unsupported operator for numeric queries.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +190,7 @@ void add_string_constraint_to_query(Query &query,
|
||||
query.and_query(column.not_equal(value, case_sensitive));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for string queries.");
|
||||
throw std::logic_error("Unsupported operator for string queries.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +207,7 @@ void add_string_constraint_to_query(realm::Query &query,
|
||||
query.and_query(column.not_equal(value, case_sensitive));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Substring comparison not supported for keypath substrings.");
|
||||
throw std::logic_error("Substring comparison not supported for keypath substrings.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +232,7 @@ void add_binary_constraint_to_query(Query &query,
|
||||
query.not_equal(column.m_column, BinaryData(value));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for binary queries.");
|
||||
throw std::logic_error("Unsupported operator for binary queries.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,7 +248,7 @@ void add_binary_constraint_to_query(realm::Query &query,
|
||||
query.not_equal(column.m_column, BinaryData(value));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Substring comparison not supported for keypath substrings.");
|
||||
throw std::logic_error("Substring comparison not supported for keypath substrings.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,22 +260,23 @@ void add_link_constraint_to_query(realm::Query &query,
|
||||
switch (op) {
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.Not();
|
||||
REALM_FALLTHROUGH;
|
||||
case Predicate::Operator::Equal: {
|
||||
size_t col = prop_expr.prop->table_column;
|
||||
query.links_to(col, query.get_table()->get_link_target(col)->get(row_index));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison.");
|
||||
throw std::logic_error("Only 'equal' and 'not equal' operators supported for object comparison.");
|
||||
}
|
||||
}
|
||||
|
||||
auto link_argument(const PropertyExpression &propExpr, const parser::Expression &argExpr, Arguments &args)
|
||||
auto link_argument(const PropertyExpression&, const parser::Expression &argExpr, Arguments &args)
|
||||
{
|
||||
return args.object_index_for_argument(stot<int>(argExpr.s));
|
||||
}
|
||||
|
||||
auto link_argument(const parser::Expression &argExpr, const PropertyExpression &propExpr, Arguments &args)
|
||||
auto link_argument(const parser::Expression &argExpr, const PropertyExpression&, Arguments &args)
|
||||
{
|
||||
return args.object_index_for_argument(stot<int>(argExpr.s));
|
||||
}
|
||||
@ -281,7 +284,7 @@ auto link_argument(const parser::Expression &argExpr, const PropertyExpression &
|
||||
|
||||
template <typename RetType, typename TableGetter>
|
||||
struct ColumnGetter {
|
||||
static Columns<RetType> convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args)
|
||||
static Columns<RetType> convert(TableGetter&& table, const PropertyExpression& expr, Arguments&)
|
||||
{
|
||||
return table()->template column<RetType>(expr.prop->table_column);
|
||||
}
|
||||
@ -295,7 +298,7 @@ struct ValueGetter<Timestamp, TableGetter> {
|
||||
static Timestamp convert(TableGetter&&, const parser::Expression & value, Arguments &args)
|
||||
{
|
||||
if (value.type != parser::Expression::Type::Argument) {
|
||||
throw std::runtime_error("You must pass in a date argument to compare");
|
||||
throw std::logic_error("You must pass in a date argument to compare");
|
||||
}
|
||||
return args.timestamp_for_argument(stot<int>(value.s));
|
||||
}
|
||||
@ -309,7 +312,7 @@ struct ValueGetter<bool, TableGetter> {
|
||||
return args.bool_for_argument(stot<int>(value.s));
|
||||
}
|
||||
if (value.type != parser::Expression::Type::True && value.type != parser::Expression::Type::False) {
|
||||
throw std::runtime_error("Attempting to compare bool property to a non-bool value");
|
||||
throw std::logic_error("Attempting to compare bool property to a non-bool value");
|
||||
}
|
||||
return value.type == parser::Expression::Type::True;
|
||||
}
|
||||
@ -356,7 +359,7 @@ struct ValueGetter<String, TableGetter> {
|
||||
return args.string_for_argument(stot<int>(value.s));
|
||||
}
|
||||
if (value.type != parser::Expression::Type::String) {
|
||||
throw std::runtime_error("Attempting to compare String property to a non-String value");
|
||||
throw std::logic_error("Attempting to compare String property to a non-String value");
|
||||
}
|
||||
return value.s;
|
||||
}
|
||||
@ -369,7 +372,7 @@ struct ValueGetter<Binary, TableGetter> {
|
||||
if (value.type == parser::Expression::Type::Argument) {
|
||||
return args.binary_for_argument(stot<int>(value.s));
|
||||
}
|
||||
throw std::runtime_error("Binary properties must be compared against a binary argument.");
|
||||
throw std::logic_error("Binary properties must be compared against a binary argument.");
|
||||
}
|
||||
};
|
||||
|
||||
@ -382,7 +385,7 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp,
|
||||
void do_add_comparison_to_query(Query &query, Predicate::Comparison cmp,
|
||||
const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args)
|
||||
{
|
||||
auto type = expr.prop->type;
|
||||
@ -420,12 +423,12 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object
|
||||
add_link_constraint_to_query(query, cmp.op, expr, link_argument(lhs, rhs, args));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(util::format("Object type '%1' not supported", string_for_property_type(type)));
|
||||
throw std::logic_error(util::format("Object type '%1' not supported", string_for_property_type(type)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args)
|
||||
void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr)
|
||||
{
|
||||
Columns<T> column = expr.table_getter()->template column<T>(expr.prop->table_column);
|
||||
switch (op) {
|
||||
@ -436,12 +439,12 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const
|
||||
query.and_query(column == realm::null());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
|
||||
throw std::logic_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void do_add_null_comparison_to_query<Binary>(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args)
|
||||
void do_add_null_comparison_to_query<Binary>(Query &query, Predicate::Operator op, const PropertyExpression &expr)
|
||||
{
|
||||
precondition(expr.indexes.empty(), "KeyPath queries not supported for data comparisons.");
|
||||
Columns<Binary> column = expr.table_getter()->template column<Binary>(expr.prop->table_column);
|
||||
@ -453,60 +456,58 @@ void do_add_null_comparison_to_query<Binary>(Query &query, Predicate::Operator o
|
||||
query.equal(expr.prop->table_column, realm::null());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
|
||||
throw std::logic_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void do_add_null_comparison_to_query<Link>(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args)
|
||||
void do_add_null_comparison_to_query<Link>(Query &query, Predicate::Operator op, const PropertyExpression &expr)
|
||||
{
|
||||
precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons.");
|
||||
switch (op) {
|
||||
case Predicate::Operator::NotEqual:
|
||||
// for not equal we negate the query and then fallthrough
|
||||
query.Not();
|
||||
REALM_FALLTHROUGH;
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(query.get_table()->column<Link>(expr.prop->table_column).is_null());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison.");
|
||||
throw std::logic_error("Only 'equal' and 'not equal' operators supported for object comparison.");
|
||||
}
|
||||
}
|
||||
|
||||
void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp,
|
||||
const PropertyExpression &expr, Arguments &args)
|
||||
void do_add_null_comparison_to_query(Query &query, Predicate::Comparison cmp, const PropertyExpression &expr)
|
||||
{
|
||||
auto type = expr.prop->type;
|
||||
switch (type) {
|
||||
case realm::PropertyType::Bool:
|
||||
do_add_null_comparison_to_query<bool>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<bool>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Date:
|
||||
do_add_null_comparison_to_query<Timestamp>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Timestamp>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Double:
|
||||
do_add_null_comparison_to_query<Double>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Double>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Float:
|
||||
do_add_null_comparison_to_query<Float>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Float>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Int:
|
||||
do_add_null_comparison_to_query<Int>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Int>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::String:
|
||||
do_add_null_comparison_to_query<String>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<String>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Data:
|
||||
do_add_null_comparison_to_query<Binary>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Binary>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Object:
|
||||
do_add_null_comparison_to_query<Link>(query, cmp.op, expr, args);
|
||||
do_add_null_comparison_to_query<Link>(query, cmp.op, expr);
|
||||
break;
|
||||
case realm::PropertyType::Array:
|
||||
throw std::runtime_error("Comparing Lists to 'null' is not supported");
|
||||
default: {
|
||||
throw std::runtime_error(std::string("Object type ") + string_for_property_type(type) + " not supported");
|
||||
}
|
||||
throw std::logic_error("Comparing Lists to 'null' is not supported");
|
||||
default:
|
||||
throw std::logic_error(util::format("Object type '%1' not supported", string_for_property_type(type)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,23 +529,23 @@ void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &arg
|
||||
if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) {
|
||||
PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s);
|
||||
if (expression_is_null(cmpr.expr[1], args)) {
|
||||
do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args);
|
||||
do_add_null_comparison_to_query(query, cmpr, expr);
|
||||
}
|
||||
else {
|
||||
do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args);
|
||||
do_add_comparison_to_query(query, cmpr, expr, expr, cmpr.expr[1], args);
|
||||
}
|
||||
}
|
||||
else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) {
|
||||
PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s);
|
||||
if (expression_is_null(cmpr.expr[0], args)) {
|
||||
do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args);
|
||||
do_add_null_comparison_to_query(query, cmpr, expr);
|
||||
}
|
||||
else {
|
||||
do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args);
|
||||
do_add_comparison_to_query(query, cmpr, expr, cmpr.expr[0], expr, args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value");
|
||||
throw std::logic_error("Predicate expressions must compare a keypath and another keypath or a constant value");
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,9 +592,13 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Invalid predicate type");
|
||||
throw std::logic_error("Invalid predicate type");
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace realm {
|
||||
namespace query_builder {
|
||||
|
||||
void apply_predicate(Query &query, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType)
|
||||
{
|
||||
@ -604,4 +609,5 @@ void apply_predicate(Query &query, const Predicate &predicate, Arguments &argume
|
||||
precondition(validateMessage.empty(), validateMessage.c_str());
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
|
||||
namespace realm {
|
||||
enum class PropertyType {
|
||||
enum class PropertyType : unsigned char {
|
||||
Int = 0,
|
||||
Bool = 1,
|
||||
Float = 9,
|
||||
@ -94,6 +94,8 @@ namespace realm {
|
||||
return "object";
|
||||
case PropertyType::Array:
|
||||
return "array";
|
||||
case PropertyType::LinkingObjects:
|
||||
return "linking objects";
|
||||
#if __GNUC__
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
|
@ -20,31 +20,21 @@
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "impl/results_notifier.hpp"
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "util/format.hpp"
|
||||
#include "util/compiler.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace realm;
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
|
||||
#else
|
||||
#define REALM_HAS_CCP_ATTRIBUTE(attr) 0
|
||||
#endif
|
||||
|
||||
#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough)
|
||||
#define REALM_FALLTHROUGH [[clang::fallthrough]]
|
||||
#else
|
||||
#define REALM_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
Results::Results() = default;
|
||||
Results::~Results() = default;
|
||||
|
||||
Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s)
|
||||
Results::Results(SharedRealm r, Query q, SortOrder s)
|
||||
: m_realm(std::move(r))
|
||||
, m_object_schema(&o)
|
||||
, m_query(std::move(q))
|
||||
, m_table(m_query.get_table().get())
|
||||
, m_sort(std::move(s))
|
||||
@ -53,17 +43,15 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s)
|
||||
REALM_ASSERT(m_sort.column_indices.size() == m_sort.ascending.size());
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, const ObjectSchema &o, Table& table)
|
||||
Results::Results(SharedRealm r, Table& table)
|
||||
: m_realm(std::move(r))
|
||||
, m_object_schema(&o)
|
||||
, m_table(&table)
|
||||
, m_mode(Mode::Table)
|
||||
{
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, const ObjectSchema& o, LinkViewRef lv, util::Optional<Query> q, SortOrder s)
|
||||
Results::Results(SharedRealm r, LinkViewRef lv, util::Optional<Query> q, SortOrder s)
|
||||
: m_realm(std::move(r))
|
||||
, m_object_schema(&o)
|
||||
, m_link_view(lv)
|
||||
, m_table(&lv->get_target_table())
|
||||
, m_sort(std::move(s))
|
||||
@ -76,9 +64,8 @@ Results::Results(SharedRealm r, const ObjectSchema& o, LinkViewRef lv, util::Opt
|
||||
}
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, const ObjectSchema& o, TableView tv, SortOrder s)
|
||||
Results::Results(SharedRealm r, TableView tv, SortOrder s)
|
||||
: m_realm(std::move(r))
|
||||
, m_object_schema(&o)
|
||||
, m_table_view(std::move(tv))
|
||||
, m_table(&m_table_view.get_parent())
|
||||
, m_sort(std::move(s))
|
||||
@ -108,9 +95,9 @@ Results::Results(Results&& other)
|
||||
, m_link_view(std::move(other.m_link_view))
|
||||
, m_table(other.m_table)
|
||||
, m_sort(std::move(other.m_sort))
|
||||
, m_live(other.m_live)
|
||||
, m_notifier(std::move(other.m_notifier))
|
||||
, m_mode(other.m_mode)
|
||||
, m_update_policy(other.m_update_policy)
|
||||
, m_has_used_table_view(other.m_has_used_table_view)
|
||||
, m_wants_background_updates(other.m_wants_background_updates)
|
||||
{
|
||||
@ -130,18 +117,16 @@ bool Results::is_valid() const
|
||||
{
|
||||
if (m_realm)
|
||||
m_realm->verify_thread();
|
||||
|
||||
if (m_table && !m_table->is_attached())
|
||||
return false;
|
||||
if (m_mode == Mode::TableView && (!m_table_view.is_attached() || (m_live && m_table_view.depends_on_deleted_object())))
|
||||
return false;
|
||||
if (m_mode == Mode::LinkView && !m_link_view->is_attached())
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Results::validate_read() const
|
||||
{
|
||||
// is_valid ensures that we're on the correct thread.
|
||||
if (!is_valid())
|
||||
throw InvalidatedException();
|
||||
}
|
||||
@ -153,19 +138,6 @@ void Results::validate_write() const
|
||||
throw InvalidTransactionException("Must be in a write transaction");
|
||||
}
|
||||
|
||||
void Results::set_live(bool live)
|
||||
{
|
||||
validate_read();
|
||||
|
||||
if (!live && (m_mode == Mode::Table || m_mode == Mode::LinkView)) {
|
||||
m_query = get_query();
|
||||
m_mode = Mode::Query;
|
||||
}
|
||||
|
||||
update_tableview();
|
||||
m_live = live;
|
||||
}
|
||||
|
||||
size_t Results::size()
|
||||
{
|
||||
validate_read();
|
||||
@ -183,9 +155,28 @@ size_t Results::size()
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
const ObjectSchema& Results::get_object_schema() const
|
||||
{
|
||||
validate_read();
|
||||
|
||||
if (!m_object_schema) {
|
||||
REALM_ASSERT(m_realm);
|
||||
auto it = m_realm->config().schema->find(get_object_type());
|
||||
REALM_ASSERT(it != m_realm->config().schema->end());
|
||||
m_object_schema = &*it;
|
||||
}
|
||||
|
||||
return *m_object_schema;
|
||||
}
|
||||
|
||||
|
||||
StringData Results::get_object_type() const noexcept
|
||||
{
|
||||
return get_object_schema().name;
|
||||
if (!m_table) {
|
||||
return StringData();
|
||||
}
|
||||
|
||||
return ObjectStore::object_type_for_table_name(m_table->get_name());
|
||||
}
|
||||
|
||||
RowExpr Results::get(size_t row_ndx)
|
||||
@ -199,8 +190,8 @@ RowExpr Results::get(size_t row_ndx)
|
||||
break;
|
||||
case Mode::LinkView:
|
||||
if (update_linkview()) {
|
||||
if (row_ndx < m_link_view->size())
|
||||
return m_link_view->get(row_ndx);
|
||||
if (row_ndx < m_link_view->size())
|
||||
return m_link_view->get(row_ndx);
|
||||
break;
|
||||
}
|
||||
REALM_FALLTHROUGH;
|
||||
@ -209,8 +200,7 @@ RowExpr Results::get(size_t row_ndx)
|
||||
update_tableview();
|
||||
if (row_ndx >= m_table_view.size())
|
||||
break;
|
||||
// FIXME: If clear() was called on the underlying Table, then is_row_attached(row_ndx) will still return true (core issue #1837).
|
||||
if (!m_live && (m_table_view.get_parent().is_empty() || !m_table_view.is_row_attached(row_ndx)))
|
||||
if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx))
|
||||
return {};
|
||||
return m_table_view.get(row_ndx);
|
||||
}
|
||||
@ -260,6 +250,8 @@ util::Optional<RowExpr> Results::last()
|
||||
|
||||
bool Results::update_linkview()
|
||||
{
|
||||
REALM_ASSERT(m_update_policy == UpdatePolicy::Auto);
|
||||
|
||||
if (m_sort) {
|
||||
m_query = get_query();
|
||||
m_mode = Mode::Query;
|
||||
@ -269,9 +261,13 @@ bool Results::update_linkview()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Results::update_tableview()
|
||||
void Results::update_tableview(bool wants_notifications)
|
||||
{
|
||||
validate_read();
|
||||
if (m_update_policy == UpdatePolicy::Never) {
|
||||
REALM_ASSERT(m_mode == Mode::TableView);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
case Mode::Table:
|
||||
@ -286,10 +282,7 @@ void Results::update_tableview()
|
||||
m_mode = Mode::TableView;
|
||||
break;
|
||||
case Mode::TableView:
|
||||
if (!m_live) {
|
||||
return;
|
||||
}
|
||||
if (!m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) {
|
||||
if (wants_notifications && !m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) {
|
||||
m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
|
||||
_impl::RealmCoordinator::register_notifier(m_notifier);
|
||||
}
|
||||
@ -431,13 +424,16 @@ void Results::clear()
|
||||
validate_write();
|
||||
update_tableview();
|
||||
|
||||
if (m_live) {
|
||||
m_table_view.clear(RemoveMode::unordered);
|
||||
}
|
||||
else {
|
||||
// Copy the TableView because a non-live Results shouldn't have let its size() change.
|
||||
TableView table_view_copy = m_table_view;
|
||||
table_view_copy.clear(RemoveMode::unordered);
|
||||
switch (m_update_policy) {
|
||||
case UpdatePolicy::Auto:
|
||||
m_table_view.clear(RemoveMode::unordered);
|
||||
break;
|
||||
case UpdatePolicy::Never: {
|
||||
// Copy the TableView because a frozen Results shouldn't let its size() change.
|
||||
TableView copy(m_table_view);
|
||||
copy.clear(RemoveMode::unordered);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Mode::LinkView:
|
||||
@ -464,7 +460,9 @@ Query Results::get_query() const
|
||||
|
||||
// The TableView has no associated query so create one with no conditions that is restricted
|
||||
// to the rows in the TableView.
|
||||
m_table_view.sync_if_needed();
|
||||
if (m_update_policy == UpdatePolicy::Auto) {
|
||||
m_table_view.sync_if_needed();
|
||||
}
|
||||
return Query(*m_table, std::unique_ptr<TableViewBase>(new TableView(m_table_view)));
|
||||
}
|
||||
case Mode::LinkView:
|
||||
@ -498,12 +496,43 @@ TableView Results::get_tableview()
|
||||
Results Results::sort(realm::SortOrder&& sort) const
|
||||
{
|
||||
REALM_ASSERT(sort.column_indices.size() == sort.ascending.size());
|
||||
return Results(m_realm, *m_object_schema, get_query(), std::move(sort));
|
||||
return Results(m_realm, get_query(), std::move(sort));
|
||||
}
|
||||
|
||||
Results Results::filter(Query&& q) const
|
||||
{
|
||||
return Results(m_realm, *m_object_schema, get_query().and_query(std::move(q)), m_sort);
|
||||
return Results(m_realm, get_query().and_query(std::move(q)), m_sort);
|
||||
}
|
||||
|
||||
Results Results::snapshot() const &
|
||||
{
|
||||
validate_read();
|
||||
|
||||
return Results(*this).snapshot();
|
||||
}
|
||||
|
||||
Results Results::snapshot() &&
|
||||
{
|
||||
validate_read();
|
||||
|
||||
switch (m_mode) {
|
||||
case Mode::Empty:
|
||||
return Results();
|
||||
|
||||
case Mode::Table:
|
||||
case Mode::LinkView:
|
||||
m_query = get_query();
|
||||
m_mode = Mode::Query;
|
||||
|
||||
REALM_FALLTHROUGH;
|
||||
case Mode::Query:
|
||||
case Mode::TableView:
|
||||
update_tableview(false);
|
||||
m_notifier.reset();
|
||||
m_update_policy = UpdatePolicy::Never;
|
||||
return std::move(*this);
|
||||
}
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
void Results::prepare_async()
|
||||
@ -514,6 +543,9 @@ void Results::prepare_async()
|
||||
if (m_realm->is_in_transaction()) {
|
||||
throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction");
|
||||
}
|
||||
if (m_update_policy == UpdatePolicy::Never) {
|
||||
throw std::logic_error("Cannot create asynchronous query for snapshotted Results.");
|
||||
}
|
||||
|
||||
if (!m_notifier) {
|
||||
m_wants_background_updates = true;
|
||||
@ -553,6 +585,7 @@ bool Results::is_in_table_order() const
|
||||
|
||||
void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
|
||||
{
|
||||
REALM_ASSERT(results.m_update_policy != UpdatePolicy::Never);
|
||||
// If the previous TableView was never actually used, then stop generating
|
||||
// new ones until the user actually uses the Results object again
|
||||
if (results.m_mode == Mode::TableView) {
|
||||
@ -571,7 +604,7 @@ Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c
|
||||
, requested(r), valid_count(c) {}
|
||||
|
||||
Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation)
|
||||
: std::runtime_error(util::format("Cannot %1 property '%2': operation not supported for '%3' properties",
|
||||
: std::logic_error(util::format("Cannot %1 property '%2': operation not supported for '%3' properties",
|
||||
operation, table->get_column_name(column),
|
||||
string_for_property_type(static_cast<PropertyType>(table->get_column_type(column)))))
|
||||
, column_index(column)
|
||||
|
@ -49,10 +49,10 @@ public:
|
||||
// or a wrapper around a query and a sort order which creates and updates
|
||||
// the tableview as needed
|
||||
Results();
|
||||
Results(SharedRealm r, const ObjectSchema& o, Table& table);
|
||||
Results(SharedRealm r, const ObjectSchema& o, Query q, SortOrder s = {});
|
||||
Results(SharedRealm r, const ObjectSchema& o, TableView tv, SortOrder s);
|
||||
Results(SharedRealm r, const ObjectSchema& o, LinkViewRef lv, util::Optional<Query> q = {}, SortOrder s = {});
|
||||
Results(SharedRealm r, Table& table);
|
||||
Results(SharedRealm r, Query q, SortOrder s = {});
|
||||
Results(SharedRealm r, TableView tv, SortOrder s);
|
||||
Results(SharedRealm r, LinkViewRef lv, util::Optional<Query> q = {}, SortOrder s = {});
|
||||
~Results();
|
||||
|
||||
// Results is copyable and moveable
|
||||
@ -65,7 +65,7 @@ public:
|
||||
SharedRealm get_realm() const { return m_realm; }
|
||||
|
||||
// Object schema describing the vendored object type
|
||||
const ObjectSchema &get_object_schema() const { return *m_object_schema; }
|
||||
const ObjectSchema &get_object_schema() const;
|
||||
|
||||
// Get a query which will match the same rows as is contained in this Results
|
||||
// Returned query will not be valid if the current mode is Empty
|
||||
@ -83,9 +83,6 @@ public:
|
||||
// Get the LinkView this Results is derived from, if any
|
||||
LinkViewRef get_linkview() const { return m_link_view; }
|
||||
|
||||
// Set whether the TableView should sync if needed before accessing results
|
||||
void set_live(bool live);
|
||||
|
||||
// Get the size of this results
|
||||
// Can be either O(1) or O(N) depending on the state of things
|
||||
size_t size();
|
||||
@ -114,6 +111,10 @@ public:
|
||||
Results filter(Query&& q) const;
|
||||
Results sort(SortOrder&& sort) const;
|
||||
|
||||
// Return a snapshot of this Results that never updates to reflect changes in the underlying data.
|
||||
Results snapshot() const &;
|
||||
Results snapshot() &&;
|
||||
|
||||
// Get the min/max/average/sum of the given column
|
||||
// All but sum() returns none when there are zero matching rows
|
||||
// sum() returns 0, except for when it returns none
|
||||
@ -128,17 +129,20 @@ public:
|
||||
Empty, // Backed by nothing (for missing tables)
|
||||
Table, // Backed directly by a Table
|
||||
Query, // Backed by a query that has not yet been turned into a TableView
|
||||
LinkView, // Backed directly by a LinkView
|
||||
TableView // Backed by a TableView created from a Query
|
||||
LinkView, // Backed directly by a LinkView
|
||||
TableView, // Backed by a TableView created from a Query
|
||||
};
|
||||
// Get the currrent mode of the Results
|
||||
// Ideally this would not be public but it's needed for some KVO stuff
|
||||
Mode get_mode() const { return m_mode; }
|
||||
|
||||
// Is this Results associated with a Realm that has not been invalidated?
|
||||
bool is_valid() const;
|
||||
|
||||
// The Results object has been invalidated (due to the Realm being invalidated)
|
||||
// All non-noexcept functions can throw this
|
||||
struct InvalidatedException : public std::runtime_error {
|
||||
InvalidatedException() : std::runtime_error("Access to invalidated Results objects") {}
|
||||
struct InvalidatedException : public std::logic_error {
|
||||
InvalidatedException() : std::logic_error("Access to invalidated Results objects") {}
|
||||
};
|
||||
|
||||
// The input index parameter was out of bounds
|
||||
@ -149,20 +153,20 @@ public:
|
||||
};
|
||||
|
||||
// The input Row object is not attached
|
||||
struct DetatchedAccessorException : public std::runtime_error {
|
||||
DetatchedAccessorException() : std::runtime_error("Atempting to access an invalid object") {}
|
||||
struct DetatchedAccessorException : public std::logic_error {
|
||||
DetatchedAccessorException() : std::logic_error("Atempting to access an invalid object") {}
|
||||
};
|
||||
|
||||
// The input Row object belongs to a different table
|
||||
struct IncorrectTableException : public std::runtime_error {
|
||||
IncorrectTableException(StringData e, StringData a, const std::string &error)
|
||||
: std::runtime_error(error), expected(e), actual(a) {}
|
||||
struct IncorrectTableException : public std::logic_error {
|
||||
IncorrectTableException(StringData e, StringData a, const std::string &error) :
|
||||
std::logic_error(error), expected(e), actual(a) {}
|
||||
const StringData expected;
|
||||
const StringData actual;
|
||||
};
|
||||
|
||||
// The requested aggregate operation is not supported for the column type
|
||||
struct UnsupportedColumnTypeException : public std::runtime_error {
|
||||
struct UnsupportedColumnTypeException : public std::logic_error {
|
||||
size_t column_index;
|
||||
StringData column_name;
|
||||
DataType column_type;
|
||||
@ -187,27 +191,29 @@ public:
|
||||
friend class _impl::ResultsNotifier;
|
||||
static void set_table_view(Results& results, TableView&& tv);
|
||||
};
|
||||
|
||||
// Returns if this Results class is still valid
|
||||
bool is_valid() const;
|
||||
|
||||
private:
|
||||
enum class UpdatePolicy {
|
||||
Auto, // Update automatically to reflect changes in the underlying data.
|
||||
Never, // Never update.
|
||||
};
|
||||
|
||||
SharedRealm m_realm;
|
||||
const ObjectSchema *m_object_schema;
|
||||
mutable const ObjectSchema *m_object_schema = nullptr;
|
||||
Query m_query;
|
||||
TableView m_table_view;
|
||||
LinkViewRef m_link_view;
|
||||
Table* m_table = nullptr;
|
||||
SortOrder m_sort;
|
||||
bool m_live = true;
|
||||
|
||||
_impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
|
||||
|
||||
Mode m_mode = Mode::Empty;
|
||||
UpdatePolicy m_update_policy = UpdatePolicy::Auto;
|
||||
bool m_has_used_table_view = false;
|
||||
bool m_wants_background_updates = true;
|
||||
|
||||
void update_tableview();
|
||||
void update_tableview(bool wants_notifications = true);
|
||||
bool update_linkview();
|
||||
|
||||
void validate_read() const;
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
#include "schema.hpp"
|
||||
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -118,10 +118,8 @@ void Schema::validate() const
|
||||
}
|
||||
|
||||
// check indexable
|
||||
if (prop.is_indexed) {
|
||||
if (!prop.is_indexable()) {
|
||||
exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop));
|
||||
}
|
||||
if (prop.is_indexed && !prop.is_indexable()) {
|
||||
exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,12 @@
|
||||
#ifndef REALM_SCHEMA_HPP
|
||||
#define REALM_SCHEMA_HPP
|
||||
|
||||
#include "object_schema.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace realm {
|
||||
class ObjectSchema;
|
||||
|
||||
class Schema : private std::vector<ObjectSchema> {
|
||||
private:
|
||||
using base = std::vector<ObjectSchema>;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "binding_context.hpp"
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
#include "impl/transact_log_handler.hpp"
|
||||
#include "object_schema.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "util/format.hpp"
|
||||
@ -82,12 +83,12 @@ REALM_NOINLINE static void translate_file_exception(StringData path, bool read_o
|
||||
}
|
||||
catch (util::File::Exists const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(),
|
||||
util::format("File at path '%1' already exists", ex.get_path()),
|
||||
util::format("File at path '%1' already exists.", ex.get_path()),
|
||||
ex.what());
|
||||
}
|
||||
catch (util::File::NotFound const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(),
|
||||
util::format("Directory at path '%1' does not exists", ex.get_path()), ex.what());
|
||||
util::format("Directory at path '%1' does not exist.", ex.get_path()), ex.what());
|
||||
}
|
||||
catch (util::File::AccessError const& ex) {
|
||||
// Errors for `open()` include the path, but other errors don't. We
|
||||
@ -100,7 +101,7 @@ REALM_NOINLINE static void translate_file_exception(StringData path, bool read_o
|
||||
underlying.replace(pos - 1, ex.get_path().size() + 2, "");
|
||||
}
|
||||
throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(),
|
||||
util::format("Unable to open a realm at path '%1': %2", ex.get_path(), underlying), ex.what());
|
||||
util::format("Unable to open a realm at path '%1': %2.", ex.get_path(), underlying), ex.what());
|
||||
}
|
||||
catch (IncompatibleLockFile const& ex) {
|
||||
throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, path,
|
||||
@ -314,7 +315,7 @@ void Realm::verify_thread() const
|
||||
void Realm::verify_in_write() const
|
||||
{
|
||||
if (!is_in_transaction()) {
|
||||
throw InvalidTransactionException("Cannot modify persisted objects outside of a write transaction.");
|
||||
throw InvalidTransactionException("Cannot modify managed objects outside of a write transaction.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,4 +511,4 @@ util::Optional<int> Realm::file_format_upgraded_from_version() const
|
||||
}
|
||||
|
||||
MismatchedConfigException::MismatchedConfigException(StringData message, StringData path)
|
||||
: std::runtime_error(util::format(message.data(), path)) { }
|
||||
: std::logic_error(util::format(message.data(), path)) { }
|
||||
|
@ -228,19 +228,19 @@ namespace realm {
|
||||
std::string m_underlying;
|
||||
};
|
||||
|
||||
class MismatchedConfigException : public std::runtime_error {
|
||||
class MismatchedConfigException : public std::logic_error {
|
||||
public:
|
||||
MismatchedConfigException(StringData message, StringData path);
|
||||
};
|
||||
|
||||
class InvalidTransactionException : public std::runtime_error {
|
||||
class InvalidTransactionException : public std::logic_error {
|
||||
public:
|
||||
InvalidTransactionException(std::string message) : std::runtime_error(move(message)) {}
|
||||
InvalidTransactionException(std::string message) : std::logic_error(move(message)) {}
|
||||
};
|
||||
|
||||
class IncorrectThreadException : public std::runtime_error {
|
||||
class IncorrectThreadException : public std::logic_error {
|
||||
public:
|
||||
IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {}
|
||||
IncorrectThreadException() : std::logic_error("Realm accessed from incorrect thread.") {}
|
||||
};
|
||||
|
||||
class UninitializedRealmException : public std::runtime_error {
|
||||
@ -248,9 +248,9 @@ namespace realm {
|
||||
UninitializedRealmException(std::string message) : std::runtime_error(move(message)) {}
|
||||
};
|
||||
|
||||
class InvalidEncryptionKeyException : public std::runtime_error {
|
||||
class InvalidEncryptionKeyException : public std::logic_error {
|
||||
public:
|
||||
InvalidEncryptionKeyException() : std::runtime_error("Encryption key must be 64 bytes.") {}
|
||||
InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
|
||||
};
|
||||
}
|
||||
|
||||
|
34
src/object-store/src/util/compiler.hpp
Normal file
34
src/object-store/src/util/compiler.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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_UTIL_COMPILER_HPP
|
||||
#define REALM_UTIL_COMPILER_HPP
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
#define REALM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
|
||||
#else
|
||||
#define REALM_HAS_CPP_ATTRIBUTE(attr) 0
|
||||
#endif
|
||||
|
||||
#if REALM_HAS_CPP_ATTRIBUTE(clang::fallthrough)
|
||||
#define REALM_FALLTHROUGH [[clang::fallthrough]]
|
||||
#else
|
||||
#define REALM_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#endif // REALM_UTIL_COMPILER_HPP
|
@ -61,7 +61,7 @@ TEST_CASE("list") {
|
||||
|
||||
SECTION("add_notification_block()") {
|
||||
CollectionChangeSet change;
|
||||
List lst(r, *r->config().schema->find("origin"), lv);
|
||||
List lst(r, lv);
|
||||
|
||||
auto write = [&](auto&& f) {
|
||||
r->begin_transaction();
|
||||
@ -72,7 +72,7 @@ TEST_CASE("list") {
|
||||
};
|
||||
|
||||
auto require_change = [&] {
|
||||
auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
|
||||
auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
|
||||
change = c;
|
||||
});
|
||||
advance_and_notify(*r);
|
||||
@ -81,7 +81,7 @@ TEST_CASE("list") {
|
||||
|
||||
auto require_no_change = [&] {
|
||||
bool first = true;
|
||||
auto token = lst.add_notification_callback([&, first](CollectionChangeSet c, std::exception_ptr err) mutable {
|
||||
auto token = lst.add_notification_callback([&, first](CollectionChangeSet, std::exception_ptr) mutable {
|
||||
REQUIRE(first);
|
||||
first = false;
|
||||
});
|
||||
@ -190,7 +190,7 @@ TEST_CASE("list") {
|
||||
auto get_list = [&] {
|
||||
auto r = Realm::get_shared_realm(config);
|
||||
auto lv = r->read_group()->get_table("class_origin")->get_linklist(0, 0);
|
||||
return List(r, *r->config().schema->find("origin"), lv);
|
||||
return List(r, lv);
|
||||
};
|
||||
auto change_list = [&] {
|
||||
r->begin_transaction();
|
||||
@ -250,7 +250,7 @@ TEST_CASE("list") {
|
||||
lv2->add(0);
|
||||
r->commit_transaction();
|
||||
|
||||
List lst2(r, *r->config().schema->find("other_origin"), lv2);
|
||||
List lst2(r, lv2);
|
||||
|
||||
// Add a callback for list1, advance the version, then add a
|
||||
// callback for list2, so that the notifiers added at each source
|
||||
@ -299,7 +299,7 @@ TEST_CASE("list") {
|
||||
}
|
||||
|
||||
SECTION("sorted add_notification_block()") {
|
||||
List lst(r, *r->config().schema->find("origin"), lv);
|
||||
List lst(r, lv);
|
||||
Results results = lst.sort({{0}, {false}});
|
||||
|
||||
int notification_calls = 0;
|
||||
@ -355,7 +355,7 @@ TEST_CASE("list") {
|
||||
}
|
||||
|
||||
SECTION("filtered add_notification_block()") {
|
||||
List lst(r, *r->config().schema->find("origin"), lv);
|
||||
List lst(r, lv);
|
||||
Results results = lst.filter(target->where().less(0, 9));
|
||||
|
||||
int notification_calls = 0;
|
||||
@ -420,8 +420,8 @@ TEST_CASE("list") {
|
||||
}
|
||||
|
||||
SECTION("sort()") {
|
||||
auto objectschema = &*r->config().schema->find("origin");
|
||||
List list(r, *objectschema, lv);
|
||||
auto objectschema = &*r->config().schema->find("target");
|
||||
List list(r, lv);
|
||||
auto results = list.sort({{0}, {false}});
|
||||
|
||||
REQUIRE(&results.get_object_schema() == objectschema);
|
||||
@ -435,8 +435,8 @@ TEST_CASE("list") {
|
||||
}
|
||||
|
||||
SECTION("filter()") {
|
||||
auto objectschema = &*r->config().schema->find("origin");
|
||||
List list(r, *objectschema, lv);
|
||||
auto objectschema = &*r->config().schema->find("target");
|
||||
List list(r, lv);
|
||||
auto results = list.filter(target->where().greater(0, 5));
|
||||
|
||||
REQUIRE(&results.get_object_schema() == objectschema);
|
||||
@ -447,4 +447,41 @@ TEST_CASE("list") {
|
||||
REQUIRE(results.get(i).get_index() == i + 6);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot()") {
|
||||
auto objectschema = &*r->config().schema->find("target");
|
||||
List list(r, lv);
|
||||
|
||||
auto snapshot = list.snapshot();
|
||||
REQUIRE(&snapshot.get_object_schema() == objectschema);
|
||||
REQUIRE(snapshot.get_mode() == Results::Mode::TableView);
|
||||
REQUIRE(snapshot.size() == 10);
|
||||
|
||||
r->begin_transaction();
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
list.remove(0);
|
||||
}
|
||||
REQUIRE(snapshot.size() == 10);
|
||||
for (size_t i = 0; i < snapshot.size(); ++i) {
|
||||
REQUIRE(snapshot.get(i).is_attached());
|
||||
}
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
target->move_last_over(i);
|
||||
}
|
||||
REQUIRE(snapshot.size() == 10);
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
REQUIRE(!snapshot.get(i).is_attached());
|
||||
}
|
||||
for (size_t i = 5; i < 10; ++i) {
|
||||
REQUIRE(snapshot.get(i).is_attached());
|
||||
}
|
||||
list.add(0);
|
||||
REQUIRE(snapshot.size() == 10);
|
||||
}
|
||||
|
||||
SECTION("get_object_schema()") {
|
||||
List list(r, lv);
|
||||
auto objectschema = &*r->config().schema->find("target");
|
||||
REQUIRE(&list.get_object_schema() == objectschema);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <realm/commit_log.hpp>
|
||||
#include <realm/group_shared.hpp>
|
||||
#include <realm/link_view.hpp>
|
||||
#include <realm/query_engine.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@ -47,7 +48,7 @@ TEST_CASE("[results] notifications") {
|
||||
table->set_int(0, i, i * 2);
|
||||
r->commit_transaction();
|
||||
|
||||
Results results(r, *config.schema->find("object"), table->where().greater(0, 0).less(0, 10));
|
||||
Results results(r, table->where().greater(0, 0).less(0, 10));
|
||||
|
||||
SECTION("unsorted notifications") {
|
||||
int notification_calls = 0;
|
||||
@ -432,7 +433,7 @@ TEST_CASE("[results] async error handling") {
|
||||
|
||||
auto r = Realm::get_shared_realm(config);
|
||||
auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
|
||||
Results results(r, *config.schema->find("object"), *r->read_group()->get_table("class_object"));
|
||||
Results results(r, *r->read_group()->get_table("class_object"));
|
||||
|
||||
class OpenFileLimiter {
|
||||
public:
|
||||
@ -546,10 +547,10 @@ TEST_CASE("[results] notifications after move") {
|
||||
|
||||
auto r = Realm::get_shared_realm(config);
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
auto results = std::make_unique<Results>(r, *config.schema->find("object"), *table);
|
||||
auto results = std::make_unique<Results>(r, *table);
|
||||
|
||||
int notification_calls = 0;
|
||||
auto token = results->add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
|
||||
auto token = results->add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
|
||||
REQUIRE_FALSE(err);
|
||||
++notification_calls;
|
||||
});
|
||||
@ -595,7 +596,7 @@ TEST_CASE("[results] error messages") {
|
||||
|
||||
auto r = Realm::get_shared_realm(config);
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Results results(r, *config.schema->find("object"), *table);
|
||||
Results results(r, *table);
|
||||
|
||||
r->begin_transaction();
|
||||
table->add_empty_row();
|
||||
@ -609,3 +610,307 @@ TEST_CASE("[results] error messages") {
|
||||
REQUIRE_THROWS_WITH(results.sum(0), "Cannot sum property 'value': operation not supported for 'string' properties");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("results: snapshots") {
|
||||
InMemoryTestFile config;
|
||||
config.cache = false;
|
||||
config.automatic_change_notifications = false;
|
||||
config.schema = std::make_unique<Schema>(Schema{
|
||||
{"object", "", {
|
||||
{"value", PropertyType::Int},
|
||||
{"array", PropertyType::Array, "linked to object"}
|
||||
}},
|
||||
{"linked to object", "", {
|
||||
{"value", PropertyType::Int}
|
||||
}}
|
||||
});
|
||||
|
||||
auto r = Realm::get_shared_realm(config);
|
||||
|
||||
auto write = [&](auto&& f) {
|
||||
r->begin_transaction();
|
||||
f();
|
||||
r->commit_transaction();
|
||||
advance_and_notify(*r);
|
||||
};
|
||||
|
||||
SECTION("snapshot of empty Results") {
|
||||
Results results;
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results based on Table") {
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Results results(r, *table);
|
||||
|
||||
{
|
||||
// A newly-added row should not appear in the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
write([=]{
|
||||
table->add_empty_row();
|
||||
});
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Removing a row present in the snapshot should not affect the size of the snapshot,
|
||||
// but will result in the snapshot returning a detached row accessor.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
write([=]{
|
||||
table->move_last_over(0);
|
||||
});
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
|
||||
// Adding a row at the same index that was formerly present in the snapshot shouldn't
|
||||
// affect the state of the snapshot.
|
||||
write([=]{
|
||||
table->add_empty_row();
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results based on LinkView") {
|
||||
auto object = r->read_group()->get_table("class_object");
|
||||
auto linked_to = r->read_group()->get_table("class_linked to object");
|
||||
|
||||
write([=]{
|
||||
object->add_empty_row();
|
||||
});
|
||||
|
||||
LinkViewRef lv = object->get_linklist(1, 0);
|
||||
Results results(r, lv);
|
||||
|
||||
{
|
||||
// A newly-added row should not appear in the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
write([=]{
|
||||
lv->add(linked_to->add_empty_row());
|
||||
});
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Removing a row from the link list should not affect the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
write([=]{
|
||||
lv->remove(0);
|
||||
});
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(snapshot.get(0).is_attached());
|
||||
|
||||
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||
// returning a detached row accessor.
|
||||
write([=]{
|
||||
linked_to->remove(0);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
|
||||
// Adding a new row to the link list shouldn't affect the state of the snapshot.
|
||||
write([=]{
|
||||
lv->add(linked_to->add_empty_row());
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results based on Query") {
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Query q = table->column<Int>(0) > 0;
|
||||
Results results(r, std::move(q));
|
||||
|
||||
{
|
||||
// A newly-added row should not appear in the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
write([=]{
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Updating a row to no longer match the query criteria should not affect the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
write([=]{
|
||||
table->set_int(0, 0, 0);
|
||||
});
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(snapshot.get(0).is_attached());
|
||||
|
||||
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||
// returning a detached row accessor.
|
||||
write([=]{
|
||||
table->remove(0);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
|
||||
// Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
|
||||
write([=]{
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results based on TableView from query") {
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Query q = table->column<Int>(0) > 0;
|
||||
Results results(r, q.find_all(), {});
|
||||
|
||||
{
|
||||
// A newly-added row should not appear in the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
write([=]{
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Updating a row to no longer match the query criteria should not affect the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
write([=]{
|
||||
table->set_int(0, 0, 0);
|
||||
});
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(snapshot.get(0).is_attached());
|
||||
|
||||
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||
// returning a detached row accessor.
|
||||
write([=]{
|
||||
table->remove(0);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
|
||||
// Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
|
||||
write([=]{
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results based on TableView from backlinks") {
|
||||
auto object = r->read_group()->get_table("class_object");
|
||||
auto linked_to = r->read_group()->get_table("class_linked to object");
|
||||
|
||||
write([=]{
|
||||
linked_to->add_empty_row();
|
||||
});
|
||||
|
||||
TableView backlinks = linked_to->get_backlink_view(0, object.get(), 1);
|
||||
Results results(r, std::move(backlinks), {});
|
||||
|
||||
auto lv = object->get_linklist(1, object->add_empty_row());
|
||||
|
||||
{
|
||||
// A newly-added row should not appear in the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
write([=]{
|
||||
lv->add(0);
|
||||
});
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Removing the link should not affect the snapshot.
|
||||
auto snapshot = results.snapshot();
|
||||
REQUIRE(results.size() == 1);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
write([=]{
|
||||
lv->remove(0);
|
||||
});
|
||||
REQUIRE(results.size() == 0);
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(snapshot.get(0).is_attached());
|
||||
|
||||
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||
// returning a detached row accessor.
|
||||
write([=]{
|
||||
object->remove(0);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
|
||||
// Adding a new link shouldn't affect the state of the snapshot.
|
||||
write([=]{
|
||||
object->add_empty_row();
|
||||
auto lv = object->get_linklist(1, object->add_empty_row());
|
||||
lv->add(0);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 1);
|
||||
REQUIRE(!snapshot.get(0).is_attached());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("snapshot of Results with notification callback registered") {
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Query q = table->column<Int>(0) > 0;
|
||||
Results results(r, q.find_all(), {});
|
||||
|
||||
auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
|
||||
REQUIRE_FALSE(err);
|
||||
});
|
||||
advance_and_notify(*r);
|
||||
|
||||
SECTION("snapshot of lvalue") {
|
||||
auto snapshot = results.snapshot();
|
||||
write([=] {
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("snapshot of rvalue") {
|
||||
auto snapshot = std::move(results).snapshot();
|
||||
write([=] {
|
||||
table->set_int(0, table->add_empty_row(), 1);
|
||||
});
|
||||
REQUIRE(snapshot.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("adding notification callback to snapshot throws") {
|
||||
auto table = r->read_group()->get_table("class_object");
|
||||
Query q = table->column<Int>(0) > 0;
|
||||
Results results(r, q.find_all(), {});
|
||||
auto snapshot = results.snapshot();
|
||||
CHECK_THROWS(snapshot.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {}));
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ private:
|
||||
Group const& m_group;
|
||||
|
||||
LinkViewRef m_linkview;
|
||||
std::vector<int> m_initial;
|
||||
std::vector<int_fast64_t> m_initial;
|
||||
|
||||
void validate(CollectionChangeSet const& info)
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#endif
|
||||
|
||||
TestFile::TestFile()
|
||||
@ -60,6 +61,12 @@ public:
|
||||
if (value == 2)
|
||||
return;
|
||||
|
||||
if (value & 1) {
|
||||
// Synchronize on the first handover of a given coordinator.
|
||||
value &= ~1;
|
||||
m_signal.load();
|
||||
}
|
||||
|
||||
auto c = reinterpret_cast<realm::_impl::RealmCoordinator *>(value);
|
||||
c->on_change();
|
||||
m_signal.store(1, std::memory_order_relaxed);
|
||||
@ -72,20 +79,29 @@ public:
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void on_change(realm::_impl::RealmCoordinator* c)
|
||||
void on_change(const std::shared_ptr<realm::_impl::RealmCoordinator>& c)
|
||||
{
|
||||
m_signal.store(reinterpret_cast<uintptr_t>(c), std::memory_order_relaxed);
|
||||
auto& it = m_published_coordinators[c.get()];
|
||||
if (it.lock()) {
|
||||
m_signal.store(reinterpret_cast<uintptr_t>(c.get()), std::memory_order_relaxed);
|
||||
} else {
|
||||
// Synchronize on the first handover of a given coordinator.
|
||||
it = c;
|
||||
m_signal = reinterpret_cast<uintptr_t>(c.get()) | 1;
|
||||
}
|
||||
|
||||
while (m_signal.load(std::memory_order_relaxed) != 1) ;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> m_signal{0};
|
||||
std::thread m_thread;
|
||||
std::map<realm::_impl::RealmCoordinator*, std::weak_ptr<realm::_impl::RealmCoordinator>> m_published_coordinators;
|
||||
} s_worker;
|
||||
|
||||
void advance_and_notify(realm::Realm& realm)
|
||||
{
|
||||
s_worker.on_change(realm::_impl::RealmCoordinator::get_existing_coordinator(realm.config().path).get());
|
||||
s_worker.on_change(realm::_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
|
||||
realm.notify();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user