Merge pull request #462 from realm/al-os-latest

PR feedback fixes from object store pr
This commit is contained in:
Ari Lazier 2016-06-15 07:26:32 -07:00 committed by GitHub
commit 7c2518c01f
47 changed files with 818 additions and 253 deletions

View File

@ -117,7 +117,7 @@ export function createCollection(prototype, realmId, info, mutable) {
resize();
} catch (e) {
// If the error indicates the collection was deleted, then remove this listener.
if (e.message.indexOf('is not attached') > 0) {
if (e.message.indexOf('Access to invalidated') == 0) {
removeMutationListener(realmId, listener);
} else {
throw e;

View File

@ -104,10 +104,6 @@ function serialize(realmId, value) {
let id = value[idKey];
if (id) {
if (value[realmKey] != realmId) {
throw new Error('Unable to serialize value from another Realm');
}
return {id};
}

View File

@ -39,6 +39,7 @@ LOCAL_SRC_FILES := \
src/object-store/src/impl/transact_log_handler.cpp \
src/object-store/src/impl/android/external_commit_helper.cpp \
src/object-store/src/impl/android/weak_realm_notifier.cpp \
src/object-store/src/util/format.cpp \
vendor/base64.cpp
LOCAL_C_INCLUDES := src

View File

@ -3,7 +3,7 @@
set -e
set -o pipefail
: ${REALM_CORE_VERSION:=0.100.2} # set to "current" to always use the current build
: ${REALM_CORE_VERSION:=1.1.0} # set to "current" to always use the current build
# Start current working directory at the root of the project.
cd "$(dirname "$0")/.."

View File

@ -18,6 +18,7 @@
027A23131CD3E379000543AE /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; };
02D041F71CE11159000E4250 /* dates-v3.realm in Resources */ = {isa = PBXBuildFile; fileRef = 02D041F61CE11159000E4250 /* dates-v3.realm */; };
02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; };
02E008D51D10ABB600F3AA37 /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E008D21D10AB1B00F3AA37 /* format.cpp */; };
02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; };
02F59EC01C88F17D007F774C /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB11C88F17D007F774C /* list.cpp */; };
02F59EC11C88F17D007F774C /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB41C88F17D007F774C /* object_schema.cpp */; };
@ -67,6 +68,7 @@
F63FF3251C1642BB00B3B8E0 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF3161C1642BB00B3B8E0 /* GCDWebServerErrorResponse.m */; };
F63FF3261C1642BB00B3B8E0 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF3181C1642BB00B3B8E0 /* GCDWebServerFileResponse.m */; };
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF31A1C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m */; };
F64A059B1D10D928004ACDBE /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E008D21D10AB1B00F3AA37 /* format.cpp */; };
F674784A1CC81F1900F9273C /* platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F67478481CC81F1300F9273C /* platform.cpp */; };
F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; };
F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; };
@ -123,6 +125,9 @@
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
02D041F61CE11159000E4250 /* dates-v3.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v3.realm"; sourceTree = "<group>"; };
02E008D11D10AB1B00F3AA37 /* atomic_shared_ptr.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = atomic_shared_ptr.hpp; sourceTree = "<group>"; };
02E008D21D10AB1B00F3AA37 /* format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cpp; sourceTree = "<group>"; };
02E008D31D10AB1B00F3AA37 /* format.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = format.hpp; sourceTree = "<group>"; };
02F59EAE1C88F17D007F774C /* binding_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = binding_context.hpp; path = src/binding_context.hpp; sourceTree = "<group>"; };
02F59EAF1C88F17D007F774C /* index_set.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = index_set.cpp; path = src/index_set.cpp; sourceTree = "<group>"; };
02F59EB01C88F17D007F774C /* index_set.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = index_set.hpp; path = src/index_set.hpp; sourceTree = "<group>"; };
@ -352,6 +357,17 @@
path = data;
sourceTree = "<group>";
};
02E008D01D10AB1B00F3AA37 /* util */ = {
isa = PBXGroup;
children = (
02E008D11D10AB1B00F3AA37 /* atomic_shared_ptr.hpp */,
02E008D21D10AB1B00F3AA37 /* format.cpp */,
02E008D31D10AB1B00F3AA37 /* format.hpp */,
);
name = util;
path = src/util;
sourceTree = "<group>";
};
F62A35131C18E6E2004A917D /* iOS */ = {
isa = PBXGroup;
children = (
@ -365,6 +381,7 @@
F62A35141C18E783004A917D /* Object Store */ = {
isa = PBXGroup;
children = (
02E008D01D10AB1B00F3AA37 /* util */,
F63117EA1CEB0BFA00ECB2DE /* impl */,
F63117EC1CEB0C8100ECB2DE /* parser */,
02414B961CE6AADD00A8669F /* collection_notifications.cpp */,
@ -759,6 +776,7 @@
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */,
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */,
F6E931BB1CFEAE310016AF14 /* collection_change_builder.cpp in Sources */,
F64A059B1D10D928004ACDBE /* format.cpp in Sources */,
F60102D51CBB96AE00EC01BA /* list.cpp in Sources */,
F6E931BC1CFEAE340016AF14 /* collection_notifier.cpp in Sources */,
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */,
@ -782,6 +800,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
02E008D51D10ABB600F3AA37 /* format.cpp in Sources */,
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
02414BA61CE6ABCF00A8669F /* collection_notifier.cpp in Sources */,
02414BA71CE6ABCF00A8669F /* list_notifier.cpp in Sources */,
@ -860,6 +879,8 @@
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
REALM_DEBUG,
REALM_HAVE_CONFIG,
__ASSERTMACROS__,
);
@ -953,10 +974,6 @@
02B58CCB1AE99CEC009B348C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = ../tests/ios/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@ -994,7 +1011,6 @@
DYLIB_CURRENT_VERSION = 0.13.2;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"REALM_PLATFORM_NODE=1",
"$(inherited)",
);
@ -1050,11 +1066,7 @@
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios-bitcode.a";
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios-dbg.a";
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1063,7 +1075,7 @@
F63FF2B91C1241E500B3B8E0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios-bitcode.a";
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios.a";
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1073,10 +1085,6 @@
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@ -146,7 +146,9 @@ struct NativeAccessor {
static ValueType from_list(ContextType ctx, realm::List list) {
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);
}
static Mixed to_mixed(ContextType ctx, ValueType &val) {
throw std::runtime_error("'Any' type is unsupported");
}

View File

@ -265,12 +265,12 @@ inline typename T::Function RealmClass<T>::create_constructor(ContextType ctx) {
}
static inline void convert_outdated_datetime_columns(const SharedRealm &realm) {
if (realm->config().upgrade_initial_version != realm->config().upgrade_final_version &&
realm->config().upgrade_initial_version < 5) {
realm::util::Optional<int> old_file_format_version = realm->file_format_upgraded_from_version();
if (old_file_format_version && old_file_format_version < 5) {
// any versions earlier than file format 5 are stored as milliseconds and need to be converted to the new format
for (auto& object_schema : *realm->config().schema) {
auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
for (auto& property : object_schema.properties) {
for (auto& property : object_schema.persisted_properties) {
if (property.type == realm::PropertyType::Date) {
if (!realm->is_in_transaction()) {
realm->begin_transaction();

View File

@ -116,7 +116,7 @@ bool RealmObjectClass<T>::set_property(ContextType ctx, ObjectType object, const
template<typename T>
std::vector<String<T>> RealmObjectClass<T>::get_property_names(ContextType ctx, ObjectType object) {
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
auto &properties = realm_object->get_object_schema().properties;
auto &properties = realm_object->get_object_schema().persisted_properties;
std::vector<String> names;
names.reserve(properties.size());

View File

@ -52,7 +52,7 @@ struct Schema {
template<typename T>
typename T::Object Schema<T>::dict_for_property_array(ContextType ctx, const ObjectSchema &object_schema, ObjectType array) {
size_t count = object_schema.properties.size();
size_t count = object_schema.persisted_properties.size();
if (count != Object::validated_get_length(ctx, array)) {
throw std::runtime_error("Array must contain values for all object properties");
@ -62,7 +62,7 @@ typename T::Object Schema<T>::dict_for_property_array(ContextType ctx, const Obj
for (uint32_t i = 0; i < count; i++) {
ValueType value = Object::get_property(ctx, array, i);
Object::set_property(ctx, dict, object_schema.properties[i].name, value);
Object::set_property(ctx, dict, object_schema.persisted_properties[i].name, value);
}
return dict;
@ -177,14 +177,14 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
for (uint32_t i = 0; i < length; i++) {
ObjectType property_object = Object::validated_get_object(ctx, properties_object, i);
std::string property_name = Object::validated_get_string(ctx, property_object, name_string);
object_schema.properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults));
object_schema.persisted_properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults));
}
}
else {
auto property_names = Object::get_property_names(ctx, properties_object);
for (auto &property_name : property_names) {
ValueType property_value = Object::get_property(ctx, properties_object, property_name);
object_schema.properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults));
object_schema.persisted_properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults));
}
}
@ -240,7 +240,7 @@ typename T::Object Schema<T>::object_for_object_schema(ContextType ctx, const Ob
Object::set_property(ctx, object, name_string, Value::from_string(ctx, object_schema.name));
ObjectType properties = Object::create_empty(ctx);
for (auto& property : object_schema.properties) {
for (auto& property : object_schema.persisted_properties) {
Object::set_property(ctx, properties, property.name, object_for_property(ctx, property));
}

View File

@ -23,7 +23,8 @@
"../object-store/src/impl/apple/external_commit_helper.cpp",
"../object-store/src/impl/node/weak_realm_notifier.cpp",
"../object-store/src/parser/parser.cpp",
"../object-store/src/parser/query_builder.cpp"
"../object-store/src/parser/query_builder.cpp",
"../object-store/src/util/format.cpp"
],
"include_dirs": [
"..",

View File

@ -5,7 +5,9 @@ if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
endif()
if(SANITIZE_ADDRESS)
set(MAKEFLAGS "MAKEFLAGS=EXTRA_CFLAGS=-fsanitize=address EXTRA_LDFLAGS=-fsanitize=address")
set(EXPORT_MAKEFLAGS export MAKEFLAGS='EXTRA_CFLAGS=-fsanitize=address EXTRA_LDFLAGS=-fsanitize=address')
else()
set(EXPORT_MAKEFLAGS true)
endif()
if (${CMAKE_VERSION} VERSION_GREATER "3.4.0")
@ -68,8 +70,8 @@ function(download_realm_core core_version)
endfunction()
macro(define_built_realm_core_target core_directory)
set(core_library_debug ${core_directory}/src/realm/librealm-dbg${CMAKE_SHARED_LIBRARY_SUFFIX})
set(core_library_release ${core_directory}/src/realm/librealm${CMAKE_SHARED_LIBRARY_SUFFIX})
set(core_library_debug ${core_directory}/src/realm/librealm-dbg.a)
set(core_library_release ${core_directory}/src/realm/librealm.a)
set(core_libraries ${core_library_debug} ${core_library_release})
ExternalProject_Add_Step(realm-core ensure-libraries
@ -78,7 +80,7 @@ macro(define_built_realm_core_target core_directory)
DEPENDEES build
)
add_library(realm SHARED IMPORTED)
add_library(realm STATIC IMPORTED)
add_dependencies(realm realm-core)
set_property(TARGET realm PROPERTY IMPORTED_LOCATION_DEBUG ${core_library_debug})
@ -97,7 +99,7 @@ function(clone_and_build_realm_core branch)
PREFIX ${core_prefix_directory}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ""
BUILD_COMMAND export ${MAKEFLAGS} && ${MAKE_EQUAL_MAKE} sh build.sh build
BUILD_COMMAND ${EXPORT_MAKEFLAGS} && make -C src/realm librealm.a librealm-dbg.a
INSTALL_COMMAND ""
${USES_TERMINAL_BUILD}
)
@ -115,7 +117,7 @@ function(build_existing_realm_core core_directory)
BUILD_IN_SOURCE 1
BUILD_ALWAYS 1
CONFIGURE_COMMAND ""
BUILD_COMMAND export ${MAKEFLAGS} && ${MAKE_EQUAL_MAKE} sh build.sh build
BUILD_COMMAND ${EXPORT_MAKEFLAGS} && make -C src/realm librealm.a librealm-dbg.a
INSTALL_COMMAND ""
${USES_TERMINAL_BUILD}
)

View File

@ -10,7 +10,7 @@ include(CompilerFlags)
include(Sanitizers)
include(RealmCore)
set(REALM_CORE_VERSION "0.100.1" CACHE STRING "")
set(REALM_CORE_VERSION "1.1.0" CACHE STRING "")
use_realm_core(${REALM_CORE_VERSION})
set(PEGTL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/pegtl)

View File

@ -14,7 +14,8 @@ set(SOURCES
impl/results_notifier.cpp
impl/transact_log_handler.cpp
parser/parser.cpp
parser/query_builder.cpp)
parser/query_builder.cpp
util/format.cpp)
set(HEADERS
collection_notifications.hpp
@ -37,7 +38,8 @@ set(HEADERS
impl/weak_realm_notifier_base.hpp
parser/parser.hpp
parser/query_builder.hpp
util/atomic_shared_ptr.hpp)
util/atomic_shared_ptr.hpp
util/format.hpp)
if(APPLE)
list(APPEND SOURCES
@ -69,6 +71,6 @@ target_include_directories(realm-object-store-static PUBLIC ${INCLUDE_DIRS})
target_link_libraries(realm-object-store-static PUBLIC realm ${CF_LIBRARY})
# A dynamic library, linking together the prebuilt object files.
add_library(realm-object-store SHARED $<TARGET_OBJECTS:realm-object-store-objects>)
add_library(realm-object-store SHARED $<TARGET_OBJECTS:realm-object-store-objects> placeholder.cpp)
target_include_directories(realm-object-store PUBLIC ${INCLUDE_DIRS})
target_link_libraries(realm-object-store PRIVATE realm ${CF_LIBRARY})

View File

@ -41,7 +41,7 @@ NotificationToken::~NotificationToken()
}
}
NotificationToken::NotificationToken(NotificationToken&& rgt) = default;
NotificationToken::NotificationToken(NotificationToken&&) = default;
NotificationToken& NotificationToken::operator=(realm::NotificationToken&& rgt)
{

View File

@ -23,6 +23,7 @@
#include <errno.h>
#include <fcntl.h>
#include <sstream>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/stat.h>

View File

@ -69,6 +69,8 @@ WeakRealmNotifier::WeakRealmNotifier(WeakRealmNotifier&& rgt)
WeakRealmNotifier& WeakRealmNotifier::operator=(WeakRealmNotifier&& rgt)
{
WeakRealmNotifierBase::operator=(std::move(rgt));
invalidate();
m_runloop = rgt.m_runloop;
m_signal = rgt.m_signal;
rgt.m_runloop = nullptr;
@ -78,6 +80,11 @@ WeakRealmNotifier& WeakRealmNotifier::operator=(WeakRealmNotifier&& rgt)
}
WeakRealmNotifier::~WeakRealmNotifier()
{
invalidate();
}
void WeakRealmNotifier::invalidate()
{
if (m_signal) {
CFRunLoopSourceInvalidate(m_signal);

View File

@ -40,6 +40,8 @@ public:
void notify();
private:
void invalidate();
CFRunLoopRef m_runloop;
CFRunLoopSourceRef m_signal;
};

View File

@ -21,6 +21,8 @@
#include <realm/util/assert.hpp>
#include <algorithm>
#include <algorithm>
using namespace realm;
using namespace realm::_impl;
@ -50,7 +52,7 @@ void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c)
// First update any old moves
if (!c.moves.empty() || !c.deletions.empty() || !c.insertions.empty()) {
auto it = remove_if(begin(moves), end(moves), [&](auto& old) {
auto it = std::remove_if(begin(moves), end(moves), [&](auto& old) {
// Check if the moved row was moved again, and if so just update the destination
auto it = find_if(begin(c.moves), end(c.moves), [&](auto const& m) {
return old.to == m.from;
@ -80,7 +82,7 @@ void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c)
// Ignore new moves of rows which were previously inserted (the implicit
// delete from the move will remove the insert)
if (!insertions.empty() && !c.moves.empty()) {
c.moves.erase(remove_if(begin(c.moves), end(c.moves),
c.moves.erase(std::remove_if(begin(c.moves), end(c.moves),
[&](auto const& m) { return insertions.contains(m.from); }),
end(c.moves));
}
@ -125,7 +127,7 @@ void CollectionChangeBuilder::clean_up_stale_moves()
// Look for moves which are now no-ops, and remove them plus the associated
// insert+delete. Note that this isn't just checking for from == to due to
// that rows can also be shifted by other inserts and deletes
moves.erase(remove_if(begin(moves), end(moves), [&](auto const& move) {
moves.erase(std::remove_if(begin(moves), end(moves), [&](auto const& move) {
if (move.from - deletions.count(0, move.from) != move.to - insertions.count(0, move.to))
return false;
deletions.remove(move.from);

View File

@ -150,6 +150,9 @@ public:
void prepare_handover();
bool deliver(Realm&, SharedGroup&, std::exception_ptr);
template <typename T>
class Handle;
protected:
bool have_callbacks() const noexcept { return m_have_callbacks; }
void add_changes(CollectionChangeBuilder change) { m_accumulated_changes.merge(std::move(change)); }
@ -201,6 +204,43 @@ private:
CollectionChangeCallback next_callback();
};
// A smart pointer to a CollectionNotifier that unregisters the notifier when
// the pointer is destroyed. Movable. Copying will produce a null Handle.
template <typename T>
class CollectionNotifier::Handle : public std::shared_ptr<T> {
public:
using std::shared_ptr<T>::shared_ptr;
Handle() = default;
~Handle() { reset(); }
// Copying a Handle produces a null Handle.
Handle(const Handle&) : Handle() { }
Handle& operator=(const Handle& other)
{
if (this != &other) {
reset();
}
return *this;
}
Handle(Handle&&) = default;
Handle& operator=(Handle&& other)
{
reset();
std::shared_ptr<T>::shared_ptr::operator=(std::move(other));
return *this;
}
void reset()
{
if (*this) {
this->get()->unregister();
std::shared_ptr<T>::reset();
}
}
};
} // namespace _impl
} // namespace realm

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////////////
#include <nan.h>
#include <uv.h>
#include "impl/weak_realm_notifier.hpp"
@ -46,6 +47,7 @@ WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr<Realm>& realm, bool c
WeakRealmNotifier::WeakRealmNotifier(WeakRealmNotifier&& rgt)
: WeakRealmNotifierBase(std::move(rgt))
, m_handle(rgt.m_handle)
{
rgt.m_handle = nullptr;
}
@ -53,8 +55,7 @@ WeakRealmNotifier::WeakRealmNotifier(WeakRealmNotifier&& rgt)
WeakRealmNotifier& WeakRealmNotifier::operator=(WeakRealmNotifier&& rgt)
{
WeakRealmNotifierBase::operator=(std::move(rgt));
m_handle = rgt.m_handle;
rgt.m_handle = nullptr;
std::swap(m_handle, rgt.m_handle);
return *this;
}

View File

@ -16,10 +16,10 @@
//
////////////////////////////////////////////////////////////////////////////
#include <uv.h>
#include "impl/weak_realm_notifier_base.hpp"
typedef struct uv_async_s uv_async_t;
namespace realm {
class Realm;

View File

@ -63,22 +63,21 @@ std::shared_ptr<RealmCoordinator> RealmCoordinator::get_existing_coordinator(Str
std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config)
{
std::lock_guard<std::mutex> lock(m_realm_mutex);
if ((!m_config.read_only && !m_notifier) || (m_config.read_only && m_weak_realm_notifiers.empty())) {
m_config = config;
}
else {
if (m_config.read_only != config.read_only) {
throw MismatchedConfigException("Realm at path already opened with different read permissions.");
throw MismatchedConfigException("Realm at path '%1' already opened with different read permissions.", config.path);
}
if (m_config.in_memory != config.in_memory) {
throw MismatchedConfigException("Realm at path already opened with different inMemory settings.");
throw MismatchedConfigException("Realm at path '%1' already opened with different inMemory settings.", config.path);
}
if (m_config.encryption_key != config.encryption_key) {
throw MismatchedConfigException("Realm at path already opened with a different encryption key.");
throw MismatchedConfigException("Realm at path '%1' already opened with a different encryption key.", config.path);
}
if (m_config.schema_version != config.schema_version && config.schema_version != ObjectStore::NotVersioned) {
throw MismatchedConfigException("Realm at path already opened with different schema version.");
throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path);
}
// FIXME: verify that schema is compatible
// Needs to verify that all tables present in both are identical, and
@ -87,7 +86,7 @@ std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config)
// Public API currently doesn't make it possible to have non-matching
// schemata so it's not a huge issue
if ((false) && m_config.schema != config.schema) {
throw MismatchedConfigException("Realm at path already opened with different schema");
throw MismatchedConfigException("Realm at path '%1' already opened with different schema", config.path);
}
}
@ -102,19 +101,19 @@ std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config)
}
}
}
auto realm = std::make_shared<Realm>(std::move(config));
realm->init(shared_from_this());
if (!config.read_only && !m_notifier && config.automatic_change_notifications) {
try {
m_notifier = std::make_unique<ExternalCommitHelper>(*this);
}
catch (std::system_error const& ex) {
throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message());
throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message(), "");
}
}
m_weak_realm_notifiers.emplace_back(realm, m_config.cache);
return realm;
}

View File

@ -18,8 +18,6 @@
#include "impl/results_notifier.hpp"
#include "results.hpp"
using namespace realm;
using namespace realm::_impl;
@ -34,6 +32,14 @@ ResultsNotifier::ResultsNotifier(Results& target)
m_query_handover = Realm::Internal::get_shared_group(*get_realm()).export_for_handover(q, MutableSourcePayload::Move);
}
void ResultsNotifier::target_results_moved(Results& old_target, Results& new_target)
{
auto lock = lock_target();
REALM_ASSERT(m_target_results == &old_target);
m_target_results = &new_target;
}
void ResultsNotifier::release_data() noexcept
{
m_query = nullptr;

View File

@ -30,6 +30,8 @@ class ResultsNotifier : public CollectionNotifier {
public:
ResultsNotifier(Results& target);
void target_results_moved(Results& old_target, Results& new_target);
private:
// Target Results to update
// Can only be used with lock_target() held

View File

@ -21,21 +21,21 @@
#include "impl/list_notifier.hpp"
#include "impl/realm_coordinator.hpp"
#include "results.hpp"
#include "shared_realm.hpp"
#include "util/format.hpp"
#include <realm/link_view.hpp>
#include <realm/util/to_string.hpp>
#include <stdexcept>
using namespace realm;
using namespace realm::_impl;
List::List() noexcept = default;
List::~List()
{
if (m_notifier) {
m_notifier->unregister();
}
}
List::~List() = default;
List::List(const List&) = default;
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
: m_realm(std::move(r))
@ -50,12 +50,17 @@ Query List::get_query() const
return m_link_view->get_target_table().where(m_link_view);
}
size_t List::get_origin_row_index() const
{
verify_attached();
return m_link_view->get_origin_row_index();
}
void List::verify_valid_row(size_t row_ndx, bool insertion) const
{
size_t size = m_link_view->size();
if (row_ndx > size || (!insertion && row_ndx == size)) {
throw std::out_of_range("Index " + util::to_string(row_ndx) + " is outside of range 0..." +
util::to_string(size) + ".");
throw OutOfBoundsIndexException{row_ndx, size + insertion};
}
}
@ -68,7 +73,7 @@ bool List::is_valid() const
void List::verify_attached() const
{
if (!is_valid()) {
throw std::runtime_error("LinkView is not attached");
throw InvalidatedException{};
}
}
@ -93,6 +98,11 @@ RowExpr List::get(size_t row_ndx) const
return m_link_view->get(row_ndx);
}
size_t List::get_unchecked(size_t row_ndx) const noexcept
{
return m_link_view->get(row_ndx).get_index();
}
size_t List::find(ConstRow const& row) const
{
verify_attached();
@ -193,3 +203,7 @@ NotificationToken List::add_notification_callback(CollectionChangeCallback cb)
}
return {m_notifier, m_notifier->add_callback(std::move(cb))};
}
List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c))
, requested(r), valid_count(c) {}

View File

@ -20,6 +20,7 @@
#define REALM_LIST_HPP
#include "collection_notifications.hpp"
#include "impl/collection_notifier.hpp"
#include <realm/link_view_fwd.hpp>
#include <realm/row.hpp>
@ -36,19 +37,21 @@ class Realm;
class Results;
struct SortOrder;
namespace _impl {
class BackgroundCollection;
}
class List {
public:
List() noexcept;
List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept;
~List();
List(const List&);
List& operator=(const List&);
List(List&&);
List& operator=(List&&);
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; }
size_t get_origin_row_index() const;
bool is_valid() const;
void verify_attached() const;
@ -56,6 +59,7 @@ public:
size_t size() const;
RowExpr get(size_t row_ndx) const;
size_t get_unchecked(size_t row_ndx) const noexcept;
size_t find(ConstRow const& row) const;
void add(size_t target_row_ndx);
@ -85,11 +89,25 @@ public:
template <typename ValueType, typename ContextType>
void set(ContextType ctx, ValueType value, size_t list_ndx);
// 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") {}
};
// The input index parameter was out of bounds
struct OutOfBoundsIndexException : public std::out_of_range {
OutOfBoundsIndexException(size_t r, size_t c);
size_t requested;
size_t valid_count;
};
private:
std::shared_ptr<Realm> m_realm;
const ObjectSchema* m_object_schema;
LinkViewRef m_link_view;
std::shared_ptr<_impl::CollectionNotifier> m_notifier;
_impl::CollectionNotifier::Handle<_impl::CollectionNotifier> m_notifier;
void verify_valid_row(size_t row_ndx, bool insertion = false) const;

View File

@ -22,11 +22,14 @@
#include "list.hpp"
#include "object_schema.hpp"
#include "object_store.hpp"
#include "results.hpp"
#include "schema.hpp"
#include "shared_realm.hpp"
#include "util/format.hpp"
#include <string>
#include <realm/link_view.hpp>
#include <realm/table_view.hpp>
namespace realm {
@ -109,11 +112,14 @@ namespace realm {
// object index for an existing object
static size_t to_existing_object_index(ContextType ctx, SharedRealm realm, ValueType &val);
// list value acessors
// list value accessors
static size_t list_size(ContextType ctx, ValueType &val);
static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index);
static ValueType from_list(ContextType ctx, List);
// results value accessors
static ValueType from_results(ContextType ctx, Results);
//
// Deprecated
//
@ -150,9 +156,16 @@ namespace realm {
const std::string object_type;
};
class MutationOutsideTransactionException : public std::runtime_error
{
public:
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) {}
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) {}
};
@ -220,7 +233,7 @@ namespace realm {
auto string_value = Accessor::to_string(ctx, value);
m_row.set_string(column, string_value);
break;
}
}
case PropertyType::Data:
m_row.set_binary(column, BinaryData(Accessor::to_binary(ctx, value)));
break;
@ -251,6 +264,10 @@ 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));
}
}
@ -295,6 +312,14 @@ namespace realm {
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)))));
}
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), {});
return Accessor::from_results(ctx, std::move(results));
}
}
}
@ -335,7 +360,7 @@ namespace realm {
// populate
Object object(realm, object_schema, table->get(row_index));
for (const Property &prop : object_schema.properties) {
for (const Property& prop : object_schema.persisted_properties) {
if (created || !prop.is_primary) {
if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) {
object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update);

View File

@ -43,9 +43,9 @@ ASSERT_PROPERTY_TYPE_VALUE(Array, LinkList);
ObjectSchema::ObjectSchema() = default;
ObjectSchema::~ObjectSchema() = default;
ObjectSchema::ObjectSchema(std::string name, std::string primary_key, std::initializer_list<Property> properties)
ObjectSchema::ObjectSchema(std::string name, std::string primary_key, std::initializer_list<Property> persisted_properties)
: name(std::move(name))
, properties(properties)
, persisted_properties(persisted_properties)
, primary_key(std::move(primary_key))
{
set_primary_key_property();
@ -55,7 +55,7 @@ ObjectSchema::ObjectSchema(const Group *group, const std::string &name) : name(n
ConstTableRef table = ObjectStore::table_for_object_type(group, name);
size_t count = table->get_column_count();
properties.reserve(count);
persisted_properties.reserve(count);
for (size_t col = 0; col < count; col++) {
Property property;
property.name = table->get_column_name(col).data();
@ -69,7 +69,7 @@ ObjectSchema::ObjectSchema(const Group *group, const std::string &name) : name(n
ConstTableRef linkTable = table->get_link_target(col);
property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
}
properties.push_back(std::move(property));
persisted_properties.push_back(std::move(property));
}
primary_key = realm::ObjectStore::get_primary_key_for_object(group, name);
@ -77,7 +77,12 @@ ObjectSchema::ObjectSchema(const Group *group, const std::string &name) : name(n
}
Property *ObjectSchema::property_for_name(StringData name) {
for (auto& prop : properties) {
for (auto& prop : persisted_properties) {
if (StringData(prop.name) == name) {
return &prop;
}
}
for (auto& prop : computed_properties) {
if (StringData(prop.name) == name) {
return &prop;
}

View File

@ -31,7 +31,7 @@ struct Property;
class ObjectSchema {
public:
ObjectSchema();
ObjectSchema(std::string name, std::string primary_key, std::initializer_list<Property> properties);
ObjectSchema(std::string name, std::string primary_key, std::initializer_list<Property> persisted_properties);
~ObjectSchema();
// create object schema from existing table
@ -39,7 +39,8 @@ public:
ObjectSchema(const Group *group, const std::string &name);
std::string name;
std::vector<Property> properties;
std::vector<Property> persisted_properties;
std::vector<Property> computed_properties;
std::string primary_key;
Property *property_for_name(StringData name);

View File

@ -19,12 +19,12 @@
#include "object_store.hpp"
#include "schema.hpp"
#include "util/format.hpp"
#include <realm/group.hpp>
#include <realm/table.hpp>
#include <realm/table_view.hpp>
#include <realm/util/assert.hpp>
#include <realm/util/to_string.hpp>
#include <string.h>
@ -161,8 +161,8 @@ void ObjectStore::verify_schema(Schema const& actual_schema, Schema& target_sche
auto matching_schema = actual_schema.find(object_schema);
if (matching_schema == actual_schema.end()) {
if (!allow_missing_tables) {
errors.emplace_back(ObjectSchemaValidationException(object_schema.name,
"Missing table for object type '" + object_schema.name + "'."));
errors.emplace_back(object_schema.name,
util::format("Missing table for object type '%1'.", object_schema.name));
}
continue;
}
@ -180,7 +180,7 @@ std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(O
std::vector<ObjectSchemaValidationException> exceptions;
// check to see if properties are the same
for (auto& current_prop : table_schema.properties) {
for (auto& current_prop : table_schema.persisted_properties) {
auto target_prop = target_schema.property_for_name(current_prop.name);
if (!target_prop) {
@ -202,7 +202,7 @@ std::vector<ObjectSchemaValidationException> ObjectStore::verify_object_schema(O
}
// check for new missing properties
for (auto& target_prop : target_schema.properties) {
for (auto& target_prop : target_schema.persisted_properties) {
if (!table_schema.property_for_name(target_prop.name)) {
exceptions.emplace_back(ExtraPropertyException(table_schema.name, target_prop));
}
@ -270,10 +270,10 @@ void ObjectStore::create_tables(Group *group, Schema &target_schema, bool update
for (auto& target_object_schema : to_update) {
TableRef table = table_for_object_type(group, target_object_schema->name);
ObjectSchema current_schema(group, target_object_schema->name);
std::vector<Property> &target_props = target_object_schema->properties;
std::vector<Property> &target_props = target_object_schema->persisted_properties;
// handle columns changing from required to optional
for (auto& current_prop : current_schema.properties) {
for (auto& current_prop : current_schema.persisted_properties) {
auto target_prop = target_object_schema->property_for_name(current_prop.name);
if (!target_prop || !property_can_be_migrated_to_nullable(current_prop, *target_prop))
continue;
@ -292,13 +292,13 @@ void ObjectStore::create_tables(Group *group, Schema &target_schema, bool update
// remove extra columns
size_t deleted = 0;
for (auto& current_prop : current_schema.properties) {
for (auto& current_prop : current_schema.persisted_properties) {
current_prop.table_column -= deleted;
auto target_prop = target_object_schema->property_for_name(current_prop.name);
if (!target_prop || (property_has_changed(current_prop, *target_prop)
&& !property_can_be_migrated_to_nullable(current_prop, *target_prop))) {
if (deleted == current_schema.properties.size() - 1) {
if (deleted == current_schema.persisted_properties.size() - 1) {
// We're about to remove the last column from the table. Insert a placeholder column to preserve
// the number of rows in the table for the addition of new columns below.
table->add_column(type_Bool, "placeholder");
@ -377,14 +377,14 @@ bool ObjectStore::needs_update(Schema const& old_schema, Schema const& schema) {
return true;
}
if (matching_schema->properties.size() != target_schema.properties.size()) {
if (matching_schema->persisted_properties.size() != target_schema.persisted_properties.size()) {
// If the number of properties don't match then a migration is required
return false;
}
// Check that all of the property indexes are up to date
for (size_t i = 0, count = target_schema.properties.size(); i < count; ++i) {
if (target_schema.properties[i].is_indexed != matching_schema->properties[i].is_indexed) {
for (size_t i = 0, count = target_schema.persisted_properties.size(); i < count; ++i) {
if (target_schema.persisted_properties[i].is_indexed != matching_schema->persisted_properties[i].is_indexed) {
return true;
}
}
@ -446,7 +446,7 @@ bool ObjectStore::update_indexes(Group *group, Schema &schema) {
continue;
}
for (auto& property : object_schema.properties) {
for (auto& property : object_schema.persisted_properties) {
if (property.requires_index() == table->has_search_index(property.table_column)) {
continue;
}
@ -507,22 +507,25 @@ 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 = "Provided schema version " + util::to_string(new_version) + " is less than last set version " + util::to_string(old_version) + ".";
m_what = util::format("Provided schema version %1 is less than last set version %2.", old_version, new_version);
}
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property) :
m_object_type(object_type), m_property(property)
{
m_what = "Primary key property '" + property.name + "' has duplicate values after migration.";
m_what = util::format("Primary key property '%1' has duplicate values after migration.", property.name);
}
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type, Property const& property, const std::string message) : m_object_type(object_type), m_property(property)
DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string const& object_type,
Property const& property,
const std::string message)
: m_object_type(object_type), m_property(property)
{
m_what = message;
}
SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors) :
m_validation_errors(errors)
SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
: m_validation_errors(errors)
{
m_what = "Schema validation failed due to the following errors: ";
for (auto const& error : errors) {
@ -539,74 +542,122 @@ m_validation_errors(errors)
}
}
PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property)
PropertyTypeNotIndexableException::PropertyTypeNotIndexableException(std::string const& object_type,
Property const& property)
: ObjectSchemaPropertyException(object_type, property)
{
m_what = "Can't index property " + object_type + "." + property.name + ": indexing a property of type '" + string_for_property_type(property.type) + "' 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));
}
ExtraPropertyException::ExtraPropertyException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property)
{
m_what = "Property '" + property.name + "' has been added to latest object model.";
m_what = util::format("Property '%1' has been added to latest object model.", property.name);
}
MissingPropertyException::MissingPropertyException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property)
{
m_what = "Property '" + property.name + "' is missing from latest object model.";
m_what = util::format("Property '%1' is missing from latest object model.", property.name);
}
InvalidNullabilityException::InvalidNullabilityException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property)
{
if (property.type == PropertyType::Object) {
m_what = "'Object' property '" + property.name + "' must be nullable.";
}
else {
m_what = "Array or Mixed property '" + property.name + "' cannot be nullable";
switch (property.type) {
case PropertyType::Object:
m_what = util::format("'Object' property '%1' must be nullable.", property.name);
break;
case PropertyType::Any:
case PropertyType::Array:
case PropertyType::LinkingObjects:
m_what = util::format("Property '%1' of type '%2' cannoy be nullable",
property.name, string_for_property_type(property.type));
break;
case PropertyType::Int:
case PropertyType::Bool:
case PropertyType::Data:
case PropertyType::Date:
case PropertyType::Float:
case PropertyType::Double:
case PropertyType::String:
REALM_ASSERT(false);
}
}
MissingObjectTypeException::MissingObjectTypeException(std::string const& object_type, Property const& property) :
ObjectSchemaPropertyException(object_type, property)
MissingObjectTypeException::MissingObjectTypeException(std::string const& object_type, Property const& property)
: ObjectSchemaPropertyException(object_type, property)
{
m_what = "Property '" + property.name + "' has an invalid type '" + property.object_type + "'.";
m_what = util::format("Target type '%1' doesn't exist for property '%2'.",
property.object_type, property.name);
}
MismatchedPropertiesException::MismatchedPropertiesException(std::string const& object_type, Property const& old_property, Property const& new_property) :
MismatchedPropertiesException::MismatchedPropertiesException(std::string const& object_type,
Property const& old_property,
Property const& new_property) :
ObjectSchemaValidationException(object_type), m_old_property(old_property), m_new_property(new_property)
{
if (new_property.type != old_property.type) {
m_what = "Property types for '" + old_property.name + "' property do not match. Old type '" + string_for_property_type(old_property.type) +
"', new type '" + string_for_property_type(new_property.type) + "'";
m_what = util::format("Property types for '%1' property doe 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 = "Target object type for property '" + old_property.name + "' do not match. Old type '" + old_property.object_type + "', new type '" + new_property.object_type + "'";
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 = "Nullability for property '" + old_property.name + "' has changed from '" + util::to_string(old_property.is_nullable) + "' to '" + util::to_string(new_property.is_nullable) + "'.";
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);
}
}
ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string const& object_type, std::string const& old_primary, std::string const& new_primary) : ObjectSchemaValidationException(object_type), m_old_primary(old_primary), m_new_primary(new_primary)
ChangedPrimaryKeyException::ChangedPrimaryKeyException(std::string const& object_type,
std::string const& old_primary,
std::string const& new_primary)
: ObjectSchemaValidationException(object_type), m_old_primary(old_primary), m_new_primary(new_primary)
{
if (old_primary.size()) {
m_what = "Property '" + old_primary + "' is no longer a primary key.";
m_what = util::format("Property '%1' is no longer a primary key.", old_primary);
}
else {
m_what = "Property '" + new_primary + "' has been made a primary key.";
m_what = util::format("Property '%1' has been made a primary key.", new_primary);
}
}
InvalidPrimaryKeyException::InvalidPrimaryKeyException(std::string const& object_type, std::string const& primary) :
ObjectSchemaValidationException(object_type), m_primary_key(primary)
{
m_what = "Specified primary key property '" + primary + "' does not exist.";
m_what = util::format("Specified primary key property '%1' does not exist.", primary);
}
DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const& object_type) : ObjectSchemaValidationException(object_type)
DuplicatePrimaryKeysException::DuplicatePrimaryKeysException(std::string const& object_type)
: ObjectSchemaValidationException(object_type)
{
m_what = "Duplicate primary keys for object '" + object_type + "'.";
m_what = util::format("Duplicate primary keys for object '%1'.", object_type);
}
InvalidLinkingObjectsPropertyException::InvalidLinkingObjectsPropertyException(Type error_type, std::string const& object_type, Property const& property)
: ObjectSchemaPropertyException(object_type, property)
{
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",
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",
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",
property.object_type, property.link_origin_property_name,
object_type, property.name);
break;
}
}

View File

@ -157,11 +157,6 @@ namespace realm {
private:
std::vector<ObjectSchemaValidationException> m_validation_errors;
};
class SchemaUpdateValidationException : public SchemaValidationException {
public:
SchemaUpdateValidationException(std::vector<ObjectSchemaValidationException> const& errors);
};
class SchemaMismatchException : public ObjectStoreException {
public:
@ -245,6 +240,16 @@ namespace realm {
private:
std::string m_primary_key;
};
class InvalidLinkingObjectsPropertyException : public ObjectSchemaPropertyException {
public:
enum class Type {
OriginPropertyDoesNotExist,
OriginPropertyIsNotALink,
OriginPropertyInvalidLinkTarget,
};
InvalidLinkingObjectsPropertyException(Type error_type, std::string const& object_type, Property const& property);
};
}
#endif /* defined(REALM_OBJECT_STORE_HPP) */

View File

@ -19,10 +19,11 @@
#include "query_builder.hpp"
#include "parser.hpp"
#include <realm.hpp>
#include "object_store.hpp"
#include "schema.hpp"
#include "util/format.hpp"
#include <realm.hpp>
#include <assert.h>
namespace realm {
@ -30,12 +31,12 @@ namespace query_builder {
using namespace parser;
template<typename T>
T stot(const std::string s) {
T stot(std::string const& s) {
std::istringstream iss(s);
T value;
iss >> value;
if (iss.fail()) {
throw std::invalid_argument("Cannot convert string '" + s + "'");
throw std::invalid_argument(util::format("Cannot convert string '%1'", s));
}
return value;
}
@ -95,12 +96,13 @@ struct PropertyExpression
for (size_t index = 0; index < key_path.size(); index++) {
if (prop) {
precondition(prop->type == PropertyType::Object || prop->type == PropertyType::Array,
(std::string)"Property '" + key_path[index] + "' is not a link in object of type '" + desc->name + "'");
util::format("Property '%1' is not a link in object of type '%2'", key_path[index], desc->name));
indexes.push_back(prop->table_column);
}
prop = desc->property_for_name(key_path[index]);
precondition(prop != nullptr, "No property '" + key_path[index] + "' on object of type '" + desc->name + "'");
precondition(prop != nullptr,
util::format("No property '%1' on object of type '%2'", key_path[index], desc->name));
if (prop->object_type.size()) {
desc = schema.find(prop->object_type);
@ -416,9 +418,8 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object
case PropertyType::Array:
add_link_constraint_to_query(query, cmp.op, expr, link_argument(lhs, rhs, args));
break;
default: {
throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported");
}
default:
throw std::runtime_error(util::format("Object type '%1' not supported", string_for_property_type(type)));
}
}
@ -501,10 +502,9 @@ void do_add_null_comparison_to_query(Query &query, const Schema &schema, const O
do_add_null_comparison_to_query<Link>(query, cmp.op, expr, args);
break;
case realm::PropertyType::Array:
throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported");
break;
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::runtime_error(std::string("Object type ") + string_for_property_type(type) + " not supported");
}
}
}

View File

@ -22,8 +22,6 @@
#include "parser.hpp"
#include "object_accessor.hpp"
#include <realm/util/to_string.hpp>
namespace realm {
class Query;
class Schema;
@ -70,10 +68,7 @@ class ArgumentConverter : public Arguments {
SharedRealm m_realm;
ValueType &argument_at(size_t index) {
if (index >= m_arguments.size()) {
throw std::out_of_range((std::string)"Argument index " + util::to_string(index) + " out of range 0.." + util::to_string(m_arguments.size()-1));
}
return m_arguments[index];
return m_arguments.at(index);
}
};
}

View File

@ -0,0 +1 @@
// This file is intentionally left blank.

View File

@ -33,12 +33,14 @@ namespace realm {
Date = 8,
Object = 12,
Array = 13,
LinkingObjects = 14,
};
struct Property {
std::string name;
PropertyType type;
std::string object_type;
std::string link_origin_property_name;
bool is_primary = false;
bool is_indexed = false;
bool is_nullable = false;
@ -55,11 +57,13 @@ namespace realm {
#if __GNUC__ < 5
// GCC 4.9 does not support C++14 braced-init with NSDMIs
Property(std::string name="", PropertyType type=PropertyType::Int, std::string object_type="",
Property(std::string name="", PropertyType type=PropertyType::Int,
std::string object_type="", std::string link_origin_property_name="",
bool is_primary=false, bool is_indexed=false, bool is_nullable=false)
: name(std::move(name))
, type(type)
, object_type(std::move(object_type))
, link_origin_property_name(std::move(link_origin_property_name))
, is_primary(is_primary)
, is_indexed(is_indexed)
, is_nullable(is_nullable)

View File

@ -21,6 +21,7 @@
#include "impl/realm_coordinator.hpp"
#include "impl/results_notifier.hpp"
#include "object_store.hpp"
#include "util/format.hpp"
#include <stdexcept>
@ -38,6 +39,9 @@ using namespace realm;
#define REALM_FALLTHROUGH
#endif
Results::Results() = default;
Results::~Results() = default;
Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s)
: m_realm(std::move(r))
, m_object_schema(&o)
@ -83,13 +87,45 @@ Results::Results(SharedRealm r, const ObjectSchema& o, TableView tv, SortOrder s
REALM_ASSERT(m_sort.column_indices.size() == m_sort.ascending.size());
}
Results::~Results()
Results::Results(const Results&) = default;
// Cannot be defaulted as TableViewBase::operator= is missing from the core static library.
// Delegate to the copy constructor and move-assignment operators instead.
Results& Results::operator=(const Results& other)
{
if (this != &other) {
*this = Results(other);
}
return *this;
}
Results::Results(Results&& other)
: m_realm(std::move(other.m_realm))
, m_object_schema(std::move(other.m_object_schema))
, m_query(std::move(other.m_query))
, m_table_view(std::move(other.m_table_view))
, 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_has_used_table_view(other.m_has_used_table_view)
, m_wants_background_updates(other.m_wants_background_updates)
{
if (m_notifier) {
m_notifier->unregister();
m_notifier->target_results_moved(other, *this);
}
}
Results& Results::operator=(Results&& other)
{
this->~Results();
new (this) Results(std::move(other));
return *this;
}
bool Results::is_valid() const
{
if (m_realm)
@ -300,6 +336,7 @@ size_t Results::index_of(size_t row_ndx)
template<typename Int, typename Float, typename Double, typename Timestamp>
util::Optional<Mixed> Results::aggregate(size_t column, bool return_none_for_empty,
const char* name,
Int agg_int, Float agg_float,
Double agg_double, Timestamp agg_timestamp)
{
@ -338,13 +375,13 @@ util::Optional<Mixed> Results::aggregate(size_t column, bool return_none_for_emp
case type_Float: return do_agg(agg_float);
case type_Int: return do_agg(agg_int);
default:
throw UnsupportedColumnTypeException{column, m_table};
throw UnsupportedColumnTypeException{column, m_table, name};
}
}
util::Optional<Mixed> Results::max(size_t column)
{
return aggregate(column, true,
return aggregate(column, true, "max",
[=](auto const& table) { return table.maximum_int(column); },
[=](auto const& table) { return table.maximum_float(column); },
[=](auto const& table) { return table.maximum_double(column); },
@ -353,7 +390,7 @@ util::Optional<Mixed> Results::max(size_t column)
util::Optional<Mixed> Results::min(size_t column)
{
return aggregate(column, true,
return aggregate(column, true, "min",
[=](auto const& table) { return table.minimum_int(column); },
[=](auto const& table) { return table.minimum_float(column); },
[=](auto const& table) { return table.minimum_double(column); },
@ -362,20 +399,20 @@ util::Optional<Mixed> Results::min(size_t column)
util::Optional<Mixed> Results::sum(size_t column)
{
return aggregate(column, false,
return aggregate(column, false, "sum",
[=](auto const& table) { return table.sum_int(column); },
[=](auto const& table) { return table.sum_float(column); },
[=](auto const& table) { return table.sum_double(column); },
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "sum"}; });
}
util::Optional<Mixed> Results::average(size_t column)
{
return aggregate(column, true,
return aggregate(column, true, "average",
[=](auto const& table) { return table.average_int(column); },
[=](auto const& table) { return table.average_float(column); },
[=](auto const& table) { return table.average_double(column); },
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; });
[=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "average"}; });
}
void Results::clear()
@ -479,6 +516,7 @@ void Results::prepare_async()
}
if (!m_notifier) {
m_wants_background_updates = true;
m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
_impl::RealmCoordinator::register_notifier(m_notifier);
}
@ -510,6 +548,7 @@ bool Results::is_in_table_order() const
case Mode::TableView:
return m_table_view.is_in_table_order();
}
REALM_UNREACHABLE(); // keep gcc happy
}
void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
@ -527,8 +566,14 @@ void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
REALM_ASSERT(results.m_table_view.is_attached());
}
Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table)
: std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns")
Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
: std::out_of_range(util::format("Requested index %1 greater than max %2", r, 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",
operation, table->get_column_name(column),
string_for_property_type(static_cast<PropertyType>(table->get_column_type(column)))))
, column_index(column)
, column_name(table->get_column_name(column))
, column_type(table->get_column_type(column))

View File

@ -21,10 +21,10 @@
#include "collection_notifications.hpp"
#include "shared_realm.hpp"
#include "impl/collection_notifier.hpp"
#include <realm/table_view.hpp>
#include <realm/util/optional.hpp>
#include <realm/util/to_string.hpp>
namespace realm {
template<typename T> class BasicRowExpr;
@ -48,7 +48,7 @@ public:
// Results can be either be backed by nothing, a thin wrapper around a table,
// or a wrapper around a query and a sort order which creates and updates
// the tableview as needed
Results() = default;
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);
@ -56,10 +56,10 @@ public:
~Results();
// Results is copyable and moveable
Results(Results const&) = default;
Results(Results&&) = default;
Results& operator=(Results&&) = default;
Results& operator=(Results const&) = default;
Results(Results&&);
Results& operator=(Results&&);
Results(const Results&);
Results& operator=(const Results&);
// Get the Realm
SharedRealm get_realm() const { return m_realm; }
@ -137,18 +137,13 @@ public:
// 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
{
struct InvalidatedException : public std::runtime_error {
InvalidatedException() : std::runtime_error("Access to invalidated Results objects") {}
};
// The input index parameter was out of bounds
struct OutOfBoundsIndexException : public std::out_of_range
{
OutOfBoundsIndexException(size_t r, size_t c) :
std::out_of_range((std::string)"Requested index " + util::to_string(r) +
" greater than max " + util::to_string(c)),
requested(r), valid_count(c) {}
struct OutOfBoundsIndexException : public std::out_of_range {
OutOfBoundsIndexException(size_t r, size_t c);
const size_t requested;
const size_t valid_count;
};
@ -160,8 +155,8 @@ public:
// 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) {}
IncorrectTableException(StringData e, StringData a, const std::string &error)
: std::runtime_error(error), expected(e), actual(a) {}
const StringData expected;
const StringData actual;
};
@ -172,7 +167,7 @@ public:
StringData column_name;
DataType column_type;
UnsupportedColumnTypeException(size_t column, const Table* table);
UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation);
};
// Create an async query from this Results
@ -206,7 +201,7 @@ private:
SortOrder m_sort;
bool m_live = true;
std::shared_ptr<_impl::ResultsNotifier> m_notifier;
_impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
Mode m_mode = Mode::Empty;
bool m_has_used_table_view = false;
@ -222,6 +217,7 @@ private:
template<typename Int, typename Float, typename Double, typename Timestamp>
util::Optional<Mixed> aggregate(size_t column, bool return_none_for_empty,
const char* name,
Int agg_int, Float agg_float,
Double agg_double, Timestamp agg_timestamp);

View File

@ -66,15 +66,42 @@ void Schema::validate() const
std::vector<ObjectSchemaValidationException> exceptions;
for (auto const& object : *this) {
const Property *primary = nullptr;
for (auto const& prop : object.properties) {
std::vector<Property> all_properties = object.persisted_properties;
all_properties.insert(all_properties.end(), object.computed_properties.begin(), object.computed_properties.end());
for (auto const& prop : all_properties) {
// check object_type existence
if (!prop.object_type.empty() && find(prop.object_type) == end()) {
exceptions.emplace_back(MissingObjectTypeException(object.name, prop));
if (!prop.object_type.empty()) {
auto it = find(prop.object_type);
if (it == end()) {
exceptions.emplace_back(MissingObjectTypeException(object.name, prop));
}
// validate linking objects property.
else if (!prop.link_origin_property_name.empty()) {
using ErrorType = InvalidLinkingObjectsPropertyException::Type;
util::Optional<ErrorType> error;
const Property *origin_property = it->property_for_name(prop.link_origin_property_name);
if (!origin_property) {
error = ErrorType::OriginPropertyDoesNotExist;
}
else if (origin_property->type != PropertyType::Object && origin_property->type != PropertyType::Array) {
error = ErrorType::OriginPropertyIsNotALink;
}
else if (origin_property->object_type != object.name) {
error = ErrorType::OriginPropertyInvalidLinkTarget;
}
if (error) {
exceptions.emplace_back(InvalidLinkingObjectsPropertyException(*error, object.name, prop));
}
}
}
// check nullablity
if (prop.is_nullable) {
if (prop.type == PropertyType::Array || prop.type == PropertyType::Any) {
if (prop.type == PropertyType::Array || prop.type == PropertyType::Any || prop.type == PropertyType::LinkingObjects) {
exceptions.emplace_back(InvalidNullabilityException(object.name, prop));
}
}

View File

@ -23,6 +23,7 @@
#include "impl/transact_log_handler.hpp"
#include "object_store.hpp"
#include "schema.hpp"
#include "util/format.hpp"
#include <realm/commit_log.hpp>
#include <realm/group_shared.hpp>
@ -61,61 +62,89 @@ Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c)
Realm::Realm(Config config)
: m_config(std::move(config))
{
open_with_config(m_config, m_history, m_shared_group, m_read_only_group);
open_with_config(m_config, m_history, m_shared_group, m_read_only_group, this);
if (m_read_only_group) {
m_group = m_read_only_group.get();
}
}
void Realm::open_with_config(Config& config,
REALM_NOINLINE static void translate_file_exception(StringData path, bool read_only=false)
{
try {
throw;
}
catch (util::File::PermissionDenied const& ex) {
throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(),
util::format("Unable to open a realm at path '%1'. Please use a path where your app has %2 permissions.",
ex.get_path(), read_only ? "read" : "read-write"),
ex.what());
}
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()),
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());
}
catch (util::File::AccessError const& ex) {
// Errors for `open()` include the path, but other errors don't. We
// don't want two copies of the path in the error, so strip it out if it
// appears, and then include it in our prefix.
std::string underlying = ex.what();
auto pos = underlying.find(ex.get_path());
if (pos != std::string::npos && pos > 0) {
// One extra char at each end for the quotes
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());
}
catch (IncompatibleLockFile const& ex) {
throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, path,
"Realm file is currently open in another process "
"which cannot share access with this process. "
"All processes sharing a single file must be the same architecture.",
ex.what());
}
catch (FileFormatUpgradeRequired const& ex) {
throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, path,
"The Realm file format must be allowed to be upgraded "
"in order to proceed.",
ex.what());
}
}
void Realm::open_with_config(const Config& config,
std::unique_ptr<Replication>& history,
std::unique_ptr<SharedGroup>& shared_group,
std::unique_ptr<Group>& read_only_group)
std::unique_ptr<Group>& read_only_group,
Realm *realm)
{
if (config.encryption_key.data() && config.encryption_key.size() != 64) {
throw InvalidEncryptionKeyException();
}
try {
if (config.read_only) {
read_only_group = std::make_unique<Group>(config.path, config.encryption_key.data(), Group::mode_ReadOnly);
}
else {
if (config.encryption_key.data() && config.encryption_key.size() != 64) {
throw InvalidEncryptionKeyException();
}
history = realm::make_client_history(config.path, config.encryption_key.data());
SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly : SharedGroup::durability_Full;
SharedGroup::DurabilityLevel durability = config.in_memory ? SharedGroup::durability_MemOnly :
SharedGroup::durability_Full;
shared_group = std::make_unique<SharedGroup>(*history, durability, config.encryption_key.data(), !config.disable_format_upgrade,
[&](int from_version, int to_version) {
config.upgrade_initial_version = from_version;
config.upgrade_final_version = to_version;
if (realm) {
realm->upgrade_initial_version = from_version;
realm->upgrade_final_version = to_version;
}
});
}
}
catch (util::File::PermissionDenied const& ex) {
throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(),
"Unable to open a realm at path '" + ex.get_path() +
"'. Please use a path where your app has " + (config.read_only ? "read" : "read-write") + " permissions.");
}
catch (util::File::Exists const& ex) {
throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(),
"File at path '" + ex.get_path() + "' already exists.");
}
catch (util::File::NotFound const& ex) {
throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(),
"File at path '" + ex.get_path() + "' does not exist.");
}
catch (util::File::AccessError const& ex) {
throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(),
"Unable to open a realm at path '" + ex.get_path() + "'");
}
catch (IncompatibleLockFile const& ex) {
throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, config.path,
"Realm file is currently open in another process "
"which cannot share access with this process. All processes sharing a single file must be the same architecture.");
}
catch (FileFormatUpgradeRequired const& ex) {
throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, config.path,
"The Realm file format must be allowed to be upgraded "
"in order to proceed.");
catch (...) {
translate_file_exception(config.path, config.read_only);
}
}
@ -141,7 +170,7 @@ void Realm::init(std::shared_ptr<RealmCoordinator> coordinator)
if (target_schema) {
if (m_config.read_only) {
if (m_config.schema_version == ObjectStore::NotVersioned) {
throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema");
throw UninitializedRealmException("Can't open an un-initialized Realm without a Schema");
}
target_schema->validate();
ObjectStore::verify_schema(*m_config.schema, *target_schema, true);
@ -150,13 +179,13 @@ void Realm::init(std::shared_ptr<RealmCoordinator> coordinator)
else {
update_schema(std::move(target_schema), target_schema_version);
}
}
if (!m_config.read_only) {
// End the read transaction created to validation/update the
// schema to avoid pinning the version even if the user never
// actually reads data
invalidate();
}
// End the read transaction created to validation/update the
// schema to avoid pinning the version even if the user never
// actually reads data
if (!m_config.read_only) {
invalidate();
}
}
catch (...) {
@ -374,6 +403,18 @@ bool Realm::compact()
return m_shared_group->compact();
}
void Realm::write_copy(StringData path, BinaryData key)
{
REALM_ASSERT(!key.data() || key.size() == 64);
verify_thread();
try {
read_group()->write(path, key.data());
}
catch (...) {
translate_file_exception(path);
}
}
void Realm::notify()
{
verify_thread();
@ -459,3 +500,14 @@ void Realm::close()
m_binding_context = nullptr;
m_coordinator = nullptr;
}
util::Optional<int> Realm::file_format_upgraded_from_version() const
{
if (upgrade_initial_version != upgrade_final_version) {
return upgrade_initial_version;
}
return util::Optional<int>();
}
MismatchedConfigException::MismatchedConfigException(StringData message, StringData path)
: std::runtime_error(util::format(message.data(), path)) { }

View File

@ -19,19 +19,20 @@
#ifndef REALM_REALM_HPP
#define REALM_REALM_HPP
#include "schema.hpp"
#include <memory>
#include <string>
#include <thread>
#include <vector>
namespace realm {
class BinaryData;
class BindingContext;
class Group;
class Realm;
class Replication;
class Schema;
class SharedGroup;
class StringData;
typedef std::shared_ptr<Realm> SharedRealm;
typedef std::weak_ptr<Realm> WeakRealm;
@ -42,6 +43,10 @@ namespace realm {
class ResultsNotifier;
}
namespace util {
template<typename T> class Optional;
}
class Realm : public std::enable_shared_from_this<Realm> {
public:
typedef std::function<void(SharedRealm old_realm, SharedRealm realm)> MigrationFunction;
@ -79,10 +84,7 @@ namespace realm {
// everything can be done deterministically on one thread, and
// speeds up tests that don't need notifications.
bool automatic_change_notifications = true;
// File format versions populated when a file format upgrade takes place
// during realm opening
int upgrade_initial_version = 0, upgrade_final_version = 0;
Config();
Config(Config&&);
Config(const Config& c);
@ -124,6 +126,7 @@ namespace realm {
void invalidate();
bool compact();
void write_copy(StringData path, BinaryData encryption_key);
std::thread::id thread_id() const { return m_thread_id; }
void verify_thread() const;
@ -137,6 +140,9 @@ namespace realm {
bool is_closed() { return !m_read_only_group && !m_shared_group; }
// returns the file format version upgraded from if an upgrade took place
util::Optional<int> file_format_upgraded_from_version() const;
~Realm();
void init(std::shared_ptr<_impl::RealmCoordinator> coordinator);
@ -160,10 +166,11 @@ namespace realm {
static _impl::RealmCoordinator& get_coordinator(Realm& realm) { return *realm.m_coordinator; }
};
static void open_with_config(Config& config,
static void open_with_config(const Config& config,
std::unique_ptr<Replication>& history,
std::unique_ptr<SharedGroup>& shared_group,
std::unique_ptr<Group>& read_only_group);
std::unique_ptr<Group>& read_only_group,
Realm *realm = nullptr);
private:
Config m_config;
@ -178,6 +185,9 @@ namespace realm {
std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
// File format versions populated when a file format upgrade takes place during realm opening
int upgrade_initial_version = 0, upgrade_final_version = 0;
public:
std::unique_ptr<BindingContext> m_binding_context;
@ -204,24 +214,26 @@ namespace realm {
/** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
FormatUpgradeRequired,
};
RealmFileException(Kind kind, std::string path, std::string message) :
std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)) {}
RealmFileException(Kind kind, std::string path, std::string message, std::string underlying) :
std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {}
Kind kind() const { return m_kind; }
const std::string& path() const { return m_path; }
const std::string& underlying() const { return m_underlying; }
private:
Kind m_kind;
std::string m_path;
std::string m_underlying;
};
class MismatchedConfigException : public std::runtime_error {
public:
MismatchedConfigException(std::string message) : std::runtime_error(message) {}
MismatchedConfigException(StringData message, StringData path);
};
class InvalidTransactionException : public std::runtime_error {
public:
InvalidTransactionException(std::string message) : std::runtime_error(message) {}
InvalidTransactionException(std::string message) : std::runtime_error(move(message)) {}
};
class IncorrectThreadException : public std::runtime_error {
@ -229,9 +241,9 @@ namespace realm {
IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {}
};
class UnitializedRealmException : public std::runtime_error {
class UninitializedRealmException : public std::runtime_error {
public:
UnitializedRealmException(std::string message) : std::runtime_error(message) {}
UninitializedRealmException(std::string message) : std::runtime_error(move(message)) {}
};
class InvalidEncryptionKeyException : public std::runtime_error {

View File

@ -0,0 +1,82 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 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.
//
////////////////////////////////////////////////////////////////////////////
#include "util/format.hpp"
#include <sstream>
#include <realm/string_data.hpp>
#include <realm/util/assert.hpp>
namespace realm { namespace _impl {
Printable::Printable(StringData value) : m_type(Type::String), m_string(value.data()) { }
void Printable::print(std::ostream& out) const
{
switch (m_type) {
case Printable::Type::Bool:
out << (m_uint ? "true" : "false");
break;
case Printable::Type::Uint:
out << m_uint;
break;
case Printable::Type::Int:
out << m_int;
break;
case Printable::Type::String:
out << m_string;
break;
}
}
std::string format(const char* fmt, std::initializer_list<Printable> values)
{
std::stringstream ss;
while (*fmt) {
auto next = strchr(fmt, '%');
// emit the rest of the format string if there are no more percents
if (!next) {
ss << fmt;
break;
}
// emit everything up to the next percent
ss.write(fmt, next - fmt);
++next;
REALM_ASSERT(*next);
// %% produces a single escaped %
if (*next == '%') {
ss << '%';
fmt = next + 1;
continue;
}
REALM_ASSERT(isdigit(*next));
// The const_cast is safe because stroul does not actually modify
// the pointed-to string, but it lacks a const overload
auto index = strtoul(next, const_cast<char**>(&fmt), 10) - 1;
REALM_ASSERT(index < values.size());
(values.begin() + index)->print(ss);
}
return ss.str();
}
} // namespace _impl
} // namespace realm

View File

@ -0,0 +1,75 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 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_FORMAT_HPP
#define REALM_UTIL_FORMAT_HPP
#include <cstdint>
#include <iosfwd>
#include <initializer_list>
#include <string>
namespace realm {
class StringData;
namespace _impl {
class Printable {
public:
Printable(bool value) : m_type(Type::Bool), m_uint(value) { }
Printable(unsigned char value) : m_type(Type::Uint), m_uint(value) { }
Printable(unsigned int value) : m_type(Type::Uint), m_uint(value) { }
Printable(unsigned long value) : m_type(Type::Uint), m_uint(value) { }
Printable(unsigned long long value) : m_type(Type::Uint), m_uint(value) { }
Printable(char value) : m_type(Type::Int), m_int(value) { }
Printable(int value) : m_type(Type::Int), m_int(value) { }
Printable(long value) : m_type(Type::Int), m_int(value) { }
Printable(long long value) : m_type(Type::Int), m_int(value) { }
Printable(const char* value) : m_type(Type::String), m_string(value) { }
Printable(std::string const& value) : m_type(Type::String), m_string(value.c_str()) { }
Printable(StringData value);
void print(std::ostream& out) const;
private:
enum class Type {
Bool,
Int,
Uint,
String
} m_type;
union {
uintmax_t m_uint;
intmax_t m_int;
const char* m_string;
};
};
std::string format(const char* fmt, std::initializer_list<Printable>);
} // namespace _impl
namespace util {
template<typename... Args>
std::string format(const char* fmt, Args&&... args)
{
return _impl::format(fmt, {_impl::Printable(args)...});
}
} // namespace util
} // namespace realm
#endif // REALM_UTIL_FORMAT_HPP

View File

@ -17,20 +17,20 @@
using namespace realm;
TEST_CASE("Results") {
TEST_CASE("[results] notifications") {
InMemoryTestFile config;
config.cache = false;
config.automatic_change_notifications = false;
config.schema = std::make_unique<Schema>(Schema{
{"object", "", {
{"value", PropertyType::Int},
{"link", PropertyType::Object, "linked to object", false, false, true}
{"link", PropertyType::Object, "linked to object", "", false, false, true}
}},
{"other object", "", {
{"value", PropertyType::Int}
}},
{"linking object", "", {
{"link", PropertyType::Object, "object", false, false, true}
{"link", PropertyType::Object, "object", "", false, false, true}
}},
{"linked to object", "", {
{"value", PropertyType::Int}
@ -420,7 +420,7 @@ TEST_CASE("Results") {
}
}
TEST_CASE("Async Results error handling") {
TEST_CASE("[results] async error handling") {
InMemoryTestFile config;
config.cache = false;
config.automatic_change_notifications = false;
@ -533,3 +533,79 @@ TEST_CASE("Async Results error handling") {
}
}
}
TEST_CASE("[results] notifications after move") {
InMemoryTestFile config;
config.cache = false;
config.automatic_change_notifications = false;
config.schema = std::make_unique<Schema>(Schema{
{"object", "", {
{"value", PropertyType::Int},
}},
});
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);
int notification_calls = 0;
auto token = results->add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
REQUIRE_FALSE(err);
++notification_calls;
});
advance_and_notify(*r);
auto write = [&](auto&& f) {
r->begin_transaction();
f();
r->commit_transaction();
advance_and_notify(*r);
};
SECTION("notifications continue to work after Results is moved (move-constructor)") {
Results r(std::move(*results));
results.reset();
write([&] {
table->set_int(0, table->add_empty_row(), 1);
});
REQUIRE(notification_calls == 2);
}
SECTION("notifications continue to work after Results is moved (move-assignment)") {
Results r;
r = std::move(*results);
results.reset();
write([&] {
table->set_int(0, table->add_empty_row(), 1);
});
REQUIRE(notification_calls == 2);
}
}
TEST_CASE("[results] error messages") {
InMemoryTestFile config;
config.schema = std::make_unique<Schema>(Schema{
{"object", "", {
{"value", PropertyType::String},
}},
});
auto r = Realm::get_shared_realm(config);
auto table = r->read_group()->get_table("class_object");
Results results(r, *config.schema->find("object"), *table);
r->begin_transaction();
table->add_empty_row();
r->commit_transaction();
SECTION("out of bounds access") {
REQUIRE_THROWS_WITH(results.get(5), "Requested index 5 greater than max 1");
}
SECTION("unsupported aggregate operation") {
REQUIRE_THROWS_WITH(results.sum(0), "Cannot sum property 'value': operation not supported for 'string' properties");
}
}

View File

@ -114,7 +114,7 @@ TEST_CASE("Transaction log parsing") {
config.schema = std::make_unique<Schema>(Schema{
{"table", "", {
{"unindexed", PropertyType::Int},
{"indexed", PropertyType::Int, "", false, true}
{"indexed", PropertyType::Int, "", "", false, true}
}},
});
auto r = Realm::get_shared_realm(config);
@ -807,7 +807,7 @@ TEST_CASE("DeepChangeChecker") {
config.schema = std::make_unique<Schema>(Schema{
{"table", "", {
{"int", PropertyType::Int},
{"link", PropertyType::Object, "table", false, false, true},
{"link", PropertyType::Object, "table", "", false, false, true},
{"array", PropertyType::Array, "table"}
}},
});

View File

@ -3,6 +3,7 @@
#include "impl/realm_coordinator.hpp"
#include <realm/disable_sync_to_disk.hpp>
#include <realm/string_data.hpp>
#include <cstdlib>
#include <unistd.h>

View File

@ -382,7 +382,7 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema) {
std::vector<std::string> properties;
for (auto &prop : object_schema.properties) {
for (auto &prop : object_schema.persisted_properties) {
properties.push_back(prop.name);
}

View File

@ -52,14 +52,22 @@ export function getTestNames() {
export async function runTests() {
let testNames = getTestNames();
let passed = true;
for (let suiteName in testNames) {
console.log('Starting ' + suiteName);
for (let testName of testNames[suiteName]) {
await runTest(suiteName, testName);
try {
await runTest(suiteName, testName);
}
catch (e) {
passed = false;
}
}
}
return passed;
}
export async function runTest(suiteName, testName) {