Merge pull request #535 from realm/tg/update-object-store

Update to the latest objectstore code
This commit is contained in:
Ari Lazier 2016-07-19 08:47:22 -07:00 committed by GitHub
commit 47c1c2d400
29 changed files with 746 additions and 291 deletions

View File

@ -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.

View File

@ -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>

View File

@ -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");

View File

@ -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>

View File

@ -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")

View File

@ -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}")

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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:

View File

@ -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()
}
}}

View File

@ -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());
}
}}
}
}

View File

@ -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();

View File

@ -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)

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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>;

View File

@ -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)) { }

View File

@ -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.") {}
};
}

View 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

View File

@ -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);
}
}

View File

@ -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) {}));
}
}

View File

@ -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)
{

View File

@ -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();
}