Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/ios.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarinofaggiana <marino@marinofaggiana.com>2020-08-17 16:01:21 +0300
committermarinofaggiana <marino@marinofaggiana.com>2020-08-17 16:01:21 +0300
commit172a1cd7e3010ff0fe802eac30a1472a81e95bc3 (patch)
tree7537a6e9e1314a1e553fd793921ea2fc2adcb75f /Carthage
parent8c84b6fba6cfb1d00703aadb18ea49a73b6b4ca0 (diff)
coding
Diffstat (limited to 'Carthage')
l---------Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore1
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore32
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules3
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake66
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake127
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake382
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake41
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt46
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile23
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile187
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE269
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list4
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt142
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.cpp23
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.hpp42
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp180
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.cpp61
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp183
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/feature_checks.hpp30
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp243
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.hpp80
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp143
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.hpp39
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp673
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp81
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp500
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp353
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp299
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.hpp70
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/external_commit_helper.hpp40
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp50
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp50
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp104
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp58
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/notification_wrapper.hpp51
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp224
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp64
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp45
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp1162
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp274
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp362
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp117
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp564
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp62
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp54
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp67
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.cpp69
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp97
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.cpp707
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.hpp325
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp507
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp319
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp163
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp188
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp349
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp343
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp82
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp926
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp170
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/placeholder.cpp1
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp297
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp1114
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp393
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp258
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp183
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp1029
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp540
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp243
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp59
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/aligned_union.hpp65
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/atomic_shared_ptr.hpp148
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp60
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.cpp80
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.hpp33
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt83
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp965
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/index_set.cpp610
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp912
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp52
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp2059
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/CMakeLists.txt13
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.cpp246
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.hpp56
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-linkview.cpp21
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-query.cpp21
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-linkview.cpp21
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-query.cpp21
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp308
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input-lv/038
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/020
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/134
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp1017
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp77
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp857
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/query.json381
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp1863
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp3261
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp811
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp896
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp1714
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.cpp215
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.hpp56
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp47
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp329
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp155
-rw-r--r--Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/uuid.cpp50
-rwxr-xr-xCarthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh37
-rwxr-xr-xCarthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/docker_build_wrapper.sh100
-rwxr-xr-xCarthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/local_docker_build.sh20
-rwxr-xr-xCarthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh31
110 files changed, 0 insertions, 33816 deletions
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore
deleted file mode 120000
index 3e4e48b0b..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore
+++ /dev/null
@@ -1 +0,0 @@
-.gitignore \ No newline at end of file
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore
deleted file mode 100644
index 81a24da69..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore
+++ /dev/null
@@ -1,32 +0,0 @@
-# CMake
-.ninja_deps
-.ninja_log
-CMakeCache.txt
-CMakeFiles/
-Makefile
-build.ninja
-cmake_install.cmake
-rules.ninja
-CMakeScripts/
-realm-object-store.xcodeproj/
-
-# Build products
-src/librealm-object-store.a
-tests/tests
-*.build/
-Debug/
-Release/
-RelWithDebInfo/
-MinSizeRel/
-
-# IntelliJ
-.idea/
-
-# Visual Studio
-/.vs
-
-# Linters
-compile_commands.json
-
-# vim
-*.swp
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules
deleted file mode 100644
index f667cb6e5..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "external/catch"]
- path = external/catch
- url = https://github.com/catchorg/Catch2
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake
deleted file mode 100644
index e9de69d8f..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake
+++ /dev/null
@@ -1,66 +0,0 @@
-###########################################################################
-#
-# 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.
-#
-###########################################################################
-
-find_program(LCOV_PATH lcov)
-find_program(GENHTML_PATH genhtml)
-find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
-
-set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage -DCATCH_CONFIG_FAST_COMPILE")
-mark_as_advanced(CMAKE_CXX_FLAGS_COVERAGE)
-
-if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
- if(NOT (LCOV_PATH AND GENHTML_PATH AND GCOVR_PATH))
- message(FATAL_ERROR "Generating a coverage report requires lcov and gcovr")
- endif()
-
- function(create_coverage_target targetname testrunner)
- add_custom_target(${targetname}
- # Clear previous coverage information
- COMMAND ${LCOV_PATH} --directory . --zerocounters
-
- # Run the tests
- COMMAND ${testrunner}
-
- # Generate new coverage report
- COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
- COMMAND ${LCOV_PATH} --extract coverage.info '${CMAKE_SOURCE_DIR}/src/*' --output-file coverage.info.cleaned
- COMMAND ${GENHTML_PATH} -o coverage coverage.info.cleaned
- COMMAND ${CMAKE_COMMAND} -E remove coverage.info coverage.info.cleaned
-
- COMMAND echo Open coverage/index.html in your browser to view the coverage report.
-
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- )
-
- add_custom_target(${targetname}-cobertura
- COMMAND ${testrunner}
- COMMAND ${GCOVR_PATH} -x -o coverage.xml -f 'src/.*' -f "${CMAKE_SOURCE_DIR}/src.*" --exclude-directories "${CMAKE_BINARY_DIR}/tests" --exclude-directories="${CMAKE_SOURCE_DIR}/\.tmp" --exclude ".*realm\-core.*" --exclude ".*realm\-sync.*"
- COMMAND echo Code coverage report written to coverage.xml
-
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- )
- endfunction()
-else()
- function(create_coverage_target targetname testrunner)
- add_custom_target(${targetname}
- COMMAND echo "Configure with -DCMAKE_BUILD_TYPE=Coverage to generate coverage reports")
-
- add_custom_target(${targetname}-cobertura
- COMMAND echo "Configure with -DCMAKE_BUILD_TYPE=Coverage to generate coverage reports")
- endfunction()
-endif()
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake
deleted file mode 100644
index b42604fb2..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake
+++ /dev/null
@@ -1,127 +0,0 @@
-###########################################################################
-#
-# 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(CheckSymbolExists)
-
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED on)
-set(CMAKE_CXX_EXTENSIONS off)
-
-set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
- $<$<CONFIG:DEBUG>:REALM_DEBUG>
- $<$<CONFIG:COVERAGE>:REALM_DEBUG>
-)
-
-if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
- add_compile_options(
- -Wall
- -Wextra
- -Wno-missing-field-initializers
- -Wno-unevaluated-expression
- -Wempty-body
- -Wparentheses
- -Wunknown-pragmas
- -Wunreachable-code
- -DREALM_HAVE_CONFIG
- )
-endif()
-
-if(MSVC)
- add_definitions(
- /D_UNICODE
- /DWIN32_LEAN_AND_MEAN
- /D_CRT_SECURE_NO_WARNINGS
- /D_SCL_SECURE_NO_WARNINGS
- /D_ENABLE_EXTENDED_ALIGNED_STORAGE #https://developercommunity.visualstudio.com/comments/279328/view.html
- )
- add_compile_options(
- /MP # Enable multi-processor compilation
- )
- if(NOT WINDOWS_STORE)
- # Statically link the run-time library
- # https://docs.microsoft.com/bg-bg/cpp/build/reference/md-mt-ld-use-run-time-library
- # https://cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F
- foreach(flag_var
- CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
- if(${flag_var} MATCHES "/MD")
- string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
- endif()
- endforeach()
- endif()
-endif()
-
-if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
- add_compile_options(
- -Wassign-enum
- -Wbool-conversion
- -Wconditional-uninitialized
- -Wconstant-conversion
- -Wenum-conversion
- -Wimplicit-fallthrough
- -Wint-conversion
- -Wmissing-prototypes
- -Wnewline-eof
- -Wshorten-64-to-32
- -Wthread-safety
- -Wthread-safety-negative
- )
-endif()
-
-if(${CMAKE_GENERATOR} STREQUAL "Ninja")
- 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")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
- endif()
-endif()
-
-if(APPLE)
- find_library(CF_LIBRARY CoreFoundation)
- list(APPEND PLATFORM_LIBRARIES ${CF_LIBRARY})
-elseif(REALM_PLATFORM STREQUAL "Android")
- find_library(ANDROID_LIBRARY android)
- find_library(ANDROID_LOG_LIBRARY log)
- list(APPEND PLATFORM_LIBRARIES ${ANDROID_LIBRARY})
- list(APPEND PLATFORM_LIBRARIES ${ANDROID_LOG_LIBRARY})
- set(PLATFORM_DEFINES "__STDC_CONSTANT_MACROS=1")
-endif()
-
-if(NOT REALM_PLATFORM OR REALM_PLATFORM STREQUAL "Node")
- find_library(UV_LIBRARY NAMES uv libuv)
- if(UV_LIBRARY)
- find_path(UV_INCLUDE_DIR uv.h)
-
- list(APPEND PLATFORM_LIBRARIES ${UV_LIBRARY})
- add_definitions(-DREALM_HAVE_UV)
- endif()
-endif()
-
-if(REALM_PLATFORM STREQUAL "Node")
- set(PLATFORM_DEFINES "REALM_PLATFORM_NODE=1")
- if(NOT UV_LIBRARY)
- message(FATAL_ERROR "Platform set to Node but libuv was not found!")
- endif()
-endif()
-
-check_symbol_exists(epoll_create sys/epoll.h REALM_HAVE_EPOLL)
-if(REALM_HAVE_EPOLL)
- add_definitions(-DREALM_HAVE_EPOLL)
-endif()
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake
deleted file mode 100644
index 1c440e340..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake
+++ /dev/null
@@ -1,382 +0,0 @@
-#
-# 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(ExternalProject)
-include(ProcessorCount)
-
-find_package(Threads)
-
-# Load dependency info from dependencies.list into REALM_FOO_VERSION variables.
-set(DEPENDENCIES_FILE "dependencies.list" CACHE STRING "path to dependencies list")
-file(STRINGS ${DEPENDENCIES_FILE} DEPENDENCIES)
-message("Dependencies: ${DEPENDENCIES}")
-foreach(DEPENDENCY IN LISTS DEPENDENCIES)
- string(REGEX MATCHALL "([^=]+)" COMPONENT_AND_VERSION ${DEPENDENCY})
- list(GET COMPONENT_AND_VERSION 0 COMPONENT)
- list(GET COMPONENT_AND_VERSION 1 VERSION)
- set(${COMPONENT} ${VERSION})
-endforeach()
-
-
-if(APPLE)
- find_library(FOUNDATION_FRAMEWORK Foundation)
- find_library(SECURITY_FRAMEWORK Security)
-
- set(CRYPTO_LIBRARIES "")
- set(SSL_LIBRARIES ${FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK})
-elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
- # Windows doesn't do crypto right now, but that is subject to change
- set(CRYPTO_LIBRARIES "")
- set(SSL_LIBRARIES "")
-else()
- if(NOT EXISTS ${CMAKE_BINARY_DIR}/openssl/lib/cmake/OpenSSL/OpenSSLConfig.cmake)
- set(OPENSSL_URL "http://static.realm.io/downloads/openssl/${OPENSSL_VERSION}/Linux/x86_64/openssl.tgz")
- if(REALM_PLATFORM STREQUAL "Android")
- set(OPENSSL_URL "http://static.realm.io/downloads/openssl/${OPENSSL_VERSION}/Android/${ANDROID_ABI}/openssl.tgz")
- endif()
-
- message(STATUS "Downloading OpenSSL...")
- file(DOWNLOAD "${OPENSSL_URL}" "${CMAKE_BINARY_DIR}/openssl/openssl.tgz" STATUS download_status)
-
- list(GET download_status 0 status_code)
- if (NOT "${status_code}" STREQUAL "0")
- message(FATAL_ERROR "Downloading ${OPENSSL_URL}... Failed. Status: ${download_status}")
- endif()
-
- message(STATUS "Uncompressing OpenSSL...")
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E tar xfz "openssl.tgz"
- WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/openssl"
- )
- message(STATUS "Importing OpenSSL...")
- endif()
- set(OpenSSL_DIR "${CMAKE_BINARY_DIR}/openssl/lib/cmake/OpenSSL")
- find_package(OpenSSL REQUIRED CONFIG)
-
- set(CRYPTO_LIBRARIES OpenSSL::Crypto)
- set(SSL_LIBRARIES OpenSSL::SSL)
-endif()
-
-if (${CMAKE_VERSION} VERSION_GREATER "3.4.0")
- set(USES_TERMINAL_BUILD USES_TERMINAL_BUILD 1)
-endif()
-
-function(use_realm_core enable_sync core_prefix sync_prefix)
- if(core_prefix)
- build_existing_realm_core(${core_prefix})
- if(sync_prefix)
- build_existing_realm_sync(${sync_prefix})
- endif()
- elseif(enable_sync)
- # FIXME: Support building against prebuilt sync binaries.
- clone_and_build_realm_core("v${REALM_CORE_VERSION}")
- clone_and_build_realm_sync("v${REALM_SYNC_VERSION}")
- else()
- if(APPLE OR REALM_PLATFORM STREQUAL "Android" OR CMAKE_SYSTEM_NAME MATCHES "^Windows")
- download_realm_core(${REALM_CORE_VERSION})
- else()
- clone_and_build_realm_core("v${REALM_CORE_VERSION}")
- endif()
- endif()
-endfunction()
-
-function(download_realm_tarball url target libraries)
- get_filename_component(tarball_name "${url}" NAME)
-
- set(tarball_parent_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
- set(tarball_path "${tarball_parent_directory}/${tarball_name}")
- set(temp_tarball_path "/tmp/${tarball_name}")
-
- if (NOT EXISTS ${tarball_path})
- if (NOT EXISTS ${temp_tarball_path})
- message("Downloading ${url}.")
- file(DOWNLOAD ${url} ${temp_tarball_path}.tmp SHOW_PROGRESS)
- file(RENAME ${temp_tarball_path}.tmp ${temp_tarball_path})
- endif()
- file(COPY ${temp_tarball_path} DESTINATION ${tarball_parent_directory})
- endif()
-
- if(APPLE)
- add_custom_command(
- COMMENT "Extracting ${tarball_name}"
- OUTPUT ${libraries}
- COMMAND ${CMAKE_COMMAND} -E tar xf ${tarball_path}
- COMMAND ${CMAKE_COMMAND} -E remove_directory ${target}
- COMMAND ${CMAKE_COMMAND} -E rename core ${target}
- COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${libraries})
- elseif(REALM_PLATFORM STREQUAL "Android" OR CMAKE_SYSTEM_NAME MATCHES "^Windows")
- add_custom_command(
- COMMENT "Extracting ${tarball_name}"
- OUTPUT ${libraries}
- COMMAND "${CMAKE_COMMAND}" -E make_directory "${target}"
- COMMAND "${CMAKE_COMMAND}" -E chdir "${target}" "${CMAKE_COMMAND}" -E tar xf "${tarball_path}"
- COMMAND "${CMAKE_COMMAND}" -E touch_nocreate ${libraries})
- endif()
-endfunction()
-
-function(download_realm_core core_version)
- if(CMAKE_SYSTEM_NAME MATCHES "Windows")
- set(compression "tar.gz")
- set(library_directory "lib")
-
- if(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]$")
- set(architecture "ARM")
- elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Xx]64$" OR CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(architecture "x64")
- else()
- set(architecture "Win32")
- endif()
-
- if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
- set(platform "Windows")
- elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
- set(platform "UWP")
- endif()
-
- set(tarball_name_debug "realm-core-Debug-v${core_version}-${platform}-${architecture}-devel.tar.gz")
- set(tarball_name_release "realm-core-Release-v${core_version}-${platform}-${architecture}-devel.tar.gz")
-
- set(url_debug "https://static.realm.io/downloads/core/${tarball_name_debug}")
- set(url_release "https://static.realm.io/downloads/core/${tarball_name_release}")
-
- set(core_directory_parent "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
- set(core_directory_debug "${core_directory_parent}/realm-core-${core_version}-debug")
- set(core_directory_release "${core_directory_parent}/realm-core-${core_version}-release")
- set(core_directory "${core_directory_debug}")
-
- set(core_library_debug "${core_directory_debug}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_library_release "${core_directory_release}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_parser_library_debug "${core_directory_debug}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_parser_library_release "${core_directory_release}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_libraries ${core_library_debug} ${core_library_release} ${core_parser_library_debug} ${core_parser_library_release})
-
- download_realm_tarball(${url_debug} ${core_directory_debug} ${core_library_debug} ${core_parser_library_debug})
- download_realm_tarball(${url_release} ${core_directory_release} ${core_library_release} ${core_parser_library_release})
- else()
- if(APPLE)
- set(basename "realm-core-cocoa")
- set(compression "tar.xz")
- set(platform "-macosx")
- elseif(REALM_PLATFORM STREQUAL "Android")
- set(basename "realm-core-android")
- set(compression "tar.gz")
- set(platform "-android-${ANDROID_ABI}")
- endif()
-
- set(tarball_name "${basename}-v${core_version}.${compression}")
- set(url "https://static.realm.io/downloads/core/${tarball_name}")
- set(core_directory_parent "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
- set(core_directory "${core_directory_parent}/realm-core-${core_version}")
-
- set(core_library_debug ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm${platform}-dbg${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(core_library_release ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm${platform}${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(core_parser_library_debug ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${platform}-dbg${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(core_parser_library_release ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${platform}${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(core_libraries ${core_library_debug} ${core_library_release} ${core_parser_library_debug} ${core_parser_library_release})
-
- download_realm_tarball(${url} ${core_directory} "${core_libraries}")
- endif()
-
- add_custom_target(realm-core DEPENDS ${core_libraries})
-
- add_library(realm STATIC IMPORTED)
- add_dependencies(realm realm-core)
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_DEBUG ${core_library_debug})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_COVERAGE ${core_library_debug})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_RELEASE ${core_library_release})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION ${core_library_release})
-
- set_property(TARGET realm PROPERTY INTERFACE_LINK_LIBRARIES ${CRYPTO_LIBRARIES} Threads::Threads)
-
- # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
- # configure time, when they'd otherwise not be created until we download and extract core.
- file(MAKE_DIRECTORY ${core_directory}/include)
- set_property(TARGET realm PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${core_directory}/include)
-
- add_library(realm-parser STATIC IMPORTED)
- add_dependencies(realm-parser realm-core)
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_DEBUG ${core_parser_library_debug})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_COVERAGE ${core_parser_library_debug})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_RELEASE ${core_parser_library_release})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION ${core_parser_library_release})
-endfunction()
-
-macro(build_realm_core)
- set(core_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-core")
-
- ExternalProject_Add(realm-core
- PREFIX ${core_prefix_directory}
- BUILD_IN_SOURCE 1
- UPDATE_DISCONNECTED 1
- INSTALL_COMMAND ""
- CONFIGURE_COMMAND cmake -B build.debug -DOpenSSL_DIR=${CMAKE_BINARY_DIR}/openssl/lib/cmake/OpenSSL -D CMAKE_BUILD_TYPE=Debug ${CORE_SANITIZER_FLAGS} -G Ninja
- && cmake -B build.release -DOpenSSL_DIR=${CMAKE_BINARY_DIR}/openssl/lib/cmake/OpenSSL -D CMAKE_BUILD_TYPE=RelWithDebInfo ${CORE_SANITIZER_FLAGS} -G Ninja
-
- BUILD_COMMAND cmake --build build.debug --target Storage --target QueryParser
- && cmake --build build.release --target Storage --target QueryParser
-
- ${USES_TERMINAL_BUILD}
- ${ARGN}
- )
- ExternalProject_Get_Property(realm-core SOURCE_DIR)
-
- set(core_debug_binary_dir "${SOURCE_DIR}/build.debug")
- set(core_release_binary_dir "${SOURCE_DIR}/build.release")
- set(core_library_debug "${core_debug_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_library_release "${core_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_parser_library_debug "${core_debug_binary_dir}/src/realm/parser/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(core_parser_library_release "${core_release_binary_dir}/src/realm/parser/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${CMAKE_STATIC_LIBRARY_SUFFIX}")
-
- ExternalProject_Add_Step(realm-core ensure-libraries
- DEPENDEES build
- BYPRODUCTS ${core_library_debug} ${core_library_release}
- ${core_parser_library_debug} ${core_parser_library_release}
- )
-
- set(core_generated_headers_dir_debug "${core_debug_binary_dir}/src")
- set(core_generated_headers_dir_release "${core_release_binary_dir}/src")
-
- add_library(realm STATIC IMPORTED)
- add_dependencies(realm realm-core)
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_DEBUG ${core_library_debug})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_COVERAGE ${core_library_debug})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION_RELEASE ${core_library_release})
- set_property(TARGET realm PROPERTY IMPORTED_LOCATION ${core_library_release})
-
- set_property(TARGET realm PROPERTY INTERFACE_LINK_LIBRARIES ${CRYPTO_LIBRARIES} Threads::Threads)
-
- # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
- # configure time, when they'd otherwise not be created until we download and build core.
- file(MAKE_DIRECTORY "${core_generated_headers_dir_debug}" "${core_generated_headers_dir_release}" "${SOURCE_DIR}/src")
-
- set_property(TARGET realm PROPERTY INTERFACE_INCLUDE_DIRECTORIES
- ${SOURCE_DIR}/src
- $<$<CONFIG:Debug>:${core_generated_headers_dir_debug}>
- $<$<NOT:$<CONFIG:Debug>>:${core_generated_headers_dir_release}>
- )
-
- add_library(realm-parser STATIC IMPORTED)
- add_dependencies(realm realm-core)
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_DEBUG ${core_parser_library_debug})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_COVERAGE ${core_parser_library_debug})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_RELEASE ${core_parser_library_release})
- set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION ${core_parser_library_release})
-endmacro()
-
-function(clone_and_build_realm_core branch)
- build_realm_core(GIT_REPOSITORY "https://github.com/realm/realm-core.git"
- GIT_TAG ${branch}
- )
-endfunction()
-
-function(build_existing_realm_core core_directory)
- get_filename_component(core_directory ${core_directory} ABSOLUTE)
-
- build_realm_core(SOURCE_DIR ${core_directory}
- URL ""
- BUILD_ALWAYS 1
- )
-endfunction()
-
-macro(build_realm_sync)
- set(sync_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-sync")
-
- ExternalProject_Get_Property(realm-core SOURCE_DIR)
- set(core_directory ${SOURCE_DIR})
-
- separate_arguments(sync_cfg_args UNIX_COMMAND "-DREALM_BUILD_DOGLESS=OFF ${CORE_SANITIZER_FLAGS} -G Ninja")
- ExternalProject_Add(realm-sync-lib
- DEPENDS realm-core
- PREFIX ${sync_prefix_directory}
- BUILD_IN_SOURCE 1
- UPDATE_DISCONNECTED 1
- INSTALL_COMMAND ""
- CONFIGURE_COMMAND cmake -B build.debug -DCMAKE_BUILD_TYPE=Debug -DRealmCore_DIR=${core_directory}/build.debug ${sync_cfg_args}
- && cmake -B build.release -DCMAKE_BUILD_TYPE=RelWithDebInfo -DRealmCore_DIR=${core_directory}/build.release ${sync_cfg_args}
- BUILD_COMMAND cmake --build build.debug --target Sync --target SyncServer
- && cmake --build build.release --target Sync --target SyncServer
- ${USES_TERMINAL_BUILD}
- ${ARGN}
- )
-
- ExternalProject_Get_Property(realm-sync-lib SOURCE_DIR)
-
- set(sync_debug_binary_dir "${SOURCE_DIR}/build.debug")
- set(sync_release_binary_dir "${SOURCE_DIR}/build.release")
- set(sync_library_debug "${sync_debug_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-sync-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(sync_library_release "${sync_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-sync${CMAKE_STATIC_LIBRARY_SUFFIX}")
-
- ExternalProject_Add_Step(realm-sync-lib ensure-libraries
- BYPRODUCTS ${sync_library_debug} ${sync_library_release}
- DEPENDEES build
- )
-
- add_library(realm-sync STATIC IMPORTED)
- add_dependencies(realm-sync realm-sync-lib)
-
- set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_DEBUG ${sync_library_debug})
- set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_COVERAGE ${sync_library_debug})
- set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_RELEASE ${sync_library_release})
- set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION ${sync_library_release})
-
- set_property(TARGET realm-sync PROPERTY INTERFACE_LINK_LIBRARIES ${SSL_LIBRARIES})
-
- # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
- # configure time, when they'd otherwise not be created until we download and build sync.
- file(MAKE_DIRECTORY ${SOURCE_DIR}/src)
- set_property(TARGET realm-sync PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SOURCE_DIR}/src)
-
- # Sync server library is built as part of the sync library build
- set(sync_server_library_debug "${sync_debug_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-server-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(sync_server_library_release "${sync_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-server${CMAKE_STATIC_LIBRARY_SUFFIX}")
-
- ExternalProject_Add_Step(realm-sync-lib ensure-server-libraries
- BYPRODUCTS ${sync_server_library_debug} ${sync_server_library_release}
- DEPENDEES build
- )
-
- add_library(realm-sync-server STATIC IMPORTED)
- add_dependencies(realm-sync-server realm-sync-lib)
-
- set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_DEBUG ${sync_server_library_debug})
- set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_COVERAGE ${sync_server_library_debug})
- set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_RELEASE ${sync_server_library_release})
- set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION ${sync_server_library_release})
-
- find_package(PkgConfig)
- pkg_check_modules(YAML QUIET yaml-cpp)
- set_property(TARGET realm-sync-server PROPERTY INTERFACE_LINK_LIBRARIES ${SSL_LIBRARIES} ${YAML_LDFLAGS})
-endmacro()
-
-function(build_existing_realm_sync sync_directory)
- get_filename_component(sync_directory ${sync_directory} ABSOLUTE)
- build_realm_sync(URL ""
- SOURCE_DIR ${sync_directory}
- BUILD_ALWAYS 1
- )
-
-endfunction()
-
-function(clone_and_build_realm_sync branch)
- set(cmake_files ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
-
- build_realm_sync(GIT_REPOSITORY "git@github.com:realm/realm-sync.git"
- GIT_TAG ${branch}
- CONFIGURE_COMMAND ${config_cmd}
- )
-
-endfunction()
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake
deleted file mode 100644
index 89cea81f3..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake
+++ /dev/null
@@ -1,41 +0,0 @@
-###########################################################################
-#
-# 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.
-#
-###########################################################################
-
-option(SANITIZE_ADDRESS "build with ASan")
-option(SANITIZE_THREAD "build with TSan")
-option(SANITIZE_UNDEFINED "build with UBSan")
-
-if(SANITIZE_ADDRESS)
- set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=address")
- set(CORE_SANITIZER_FLAGS ${CORE_SANITIZER_FLAGS};-D;REALM_ASAN=ON)
-endif()
-
-if(SANITIZE_THREAD)
- set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=thread")
- set(CORE_SANITIZER_FLAGS ${CORE_SANITIZER_FLAGS};-D;REALM_TSAN=ON)
-endif()
-
-if(SANITIZE_UNDEFINED)
- set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=undefined")
- set(CORE_SANITIZER_FLAGS ${CORE_SANITIZER_FLAGS};-D;REALM_USAN=ON)
-endif()
-
-if(SANITIZE_ADDRESS OR SANITIZE_THREAD OR SANITIZE_UNDEFINED)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS} -fno-omit-frame-pointer")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
-endif()
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt
deleted file mode 100644
index c1ccd4e03..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-cmake_minimum_required(VERSION 3.2.0)
-
-if(REALM_PLATFORM STREQUAL "Android")
- # This must be before project()
- set(CMAKE_TOOLCHAIN_FILE "${ANDROID_NDK}/build/cmake/android.toolchain.cmake")
- set(ANDROID_ABI "x86" CACHE STRING "")
- set(ANDROID_NATIVE_API_LEVEL "android-16" CACHE STRING "")
-endif()
-
-set(CMAKE_BUILD_TYPE Debug CACHE STRING "")
-project(realm-object-store)
-
-list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
-
-include(CodeCoverage)
-include(CompilerFlags)
-include(Sanitizers)
-
-# Sync is disabled unless -DREALM_ENABLE_SYNC=1 is specified when invoking CMake.
-# FIXME: Flip the default once we can build against prebuilt sync binaries.
-set(REALM_ENABLE_SYNC OFF CACHE BOOL "")
-
-if(REALM_SYNC_PREFIX AND NOT REALM_CORE_PREFIX)
- message(FATAL_ERROR "REALM_CORE_PREFIX must be set when specifying REALM_SYNC_PREFIX.")
-endif()
-if(REALM_SYNC_PREFIX AND NOT REALM_ENABLE_SYNC)
- message(FATAL_ERROR "REALM_ENABLE_SYNC must be set when specifying REALM_SYNC_PREFIX.")
-endif()
-if(REALM_CORE_PREFIX AND REALM_ENABLE_SYNC AND NOT REALM_SYNC_PREFIX)
- message(FATAL_ERROR "REALM_SYNC_PREFIX must be set when specifying REALM_CORE_PREFIX when REALM_ENABLE_SYNC is set.")
-endif()
-
-set(REALM_ENABLE_SERVER OFF CACHE BOOL "Enable the server-only functionality.")
-if(REALM_ENABLE_SERVER AND NOT REALM_ENABLE_SYNC)
- message(FATAL_ERROR "REALM_ENABLE_SERVER requires REALM_ENABLE_SYNC.")
-endif()
-
-include(RealmCore)
-use_realm_core("${REALM_ENABLE_SYNC}" "${REALM_CORE_PREFIX}" "${REALM_SYNC_PREFIX}")
-
-if(REALM_ENABLE_SYNC)
- add_definitions(-DREALM_ENABLE_SYNC)
-endif()
-
-add_subdirectory(src)
-add_subdirectory(tests)
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile
deleted file mode 100644
index 8ce5bd041..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM ubuntu:xenial
-
-RUN apt-get update && \
- apt-get install -y wget build-essential lcov curl cmake gcovr libprocps4-dev libssl-dev \
- git python-cheetah libuv1-dev ninja-build adb xutils-dev
-
-# Install the Android NDK
-RUN mkdir -p /tmp/android-ndk && \
- cd /tmp/android-ndk && \
- wget -q http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin -O android-ndk.bin && \
- chmod a+x ./android-ndk.bin && sync && ./android-ndk.bin && \
- mv ./android-ndk-r10e /opt/android-ndk && \
- chmod -R a+rX /opt/android-ndk && \
- rm -rf /tmp/android-ndk
-
-ENV ANDROID_NDK_PATH /opt/android-ndk
-
-# Ensure a new enough version of CMake is available.
-RUN cd /opt \
- && wget https://cmake.org/files/v3.15/cmake-3.15.2-Linux-x86_64.tar.gz \
- && tar zxvf cmake-3.15.2-Linux-x86_64.tar.gz
-
-ENV PATH "/opt/cmake-3.15.2-Linux-x86_64/bin:$PATH"
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile
deleted file mode 100644
index 1c74e9e3d..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile
+++ /dev/null
@@ -1,187 +0,0 @@
-#!groovy
-
-@Library('realm-ci') _
-
-def getSourceArchive() {
- deleteDir()
- unstash 'source'
-}
-
-def readGitTag() {
- sh "git describe --exact-match --tags HEAD | tail -n 1 > tag.txt 2>&1 || true"
- def tag = readFile('tag.txt').trim()
- return tag
-}
-
-def readGitSha() {
- sh "git rev-parse HEAD | cut -b1-8 > sha.txt"
- def sha = readFile('sha.txt').readLines().last().trim()
- return sha
-}
-
-def buildDockerEnv(name, dockerfile='Dockerfile', extra_args='') {
- docker.withRegistry("https://${env.DOCKER_REGISTRY}", "ecr:eu-west-1:aws-ci-user") {
- sh "sh ./workflow/docker_build_wrapper.sh $name . ${extra_args}"
- }
- return docker.image(name)
-}
-
-def publishCoverageReport(String label) {
- // Unfortunately, we cannot add a title or tag to individual coverage reports.
- echo "Unstashing coverage-${label}"
- unstash("coverage-${label}")
-
- step([
- $class: 'CoberturaPublisher',
- autoUpdateHealth: false,
- autoUpdateStability: false,
- coberturaReportFile: "${label}.build/coverage.xml",
- failNoReports: true,
- failUnhealthy: false,
- failUnstable: false,
- maxNumberOfBuilds: 0,
- onlyStable: false,
- sourceEncoding: 'ASCII',
- zoomCoverageChart: false
- ])
-}
-
-if (env.BRANCH_NAME == 'master') {
- env.DOCKER_PUSH = "1"
-}
-
-def doDockerBuild(String flavor, Boolean enableSync, String sanitizerFlags = "") {
- def sync = enableSync ? "sync" : ""
- def label = "${flavor}${enableSync ? '-sync' : ''}"
-
- return {
- node('docker') {
- getSourceArchive()
- def image = buildDockerEnv("ci/realm-object-store:${flavor}")
- sshagent(['realm-ci-ssh']) {
- image.inside("-v /etc/passwd:/etc/passwd:ro -v ${env.HOME}:${env.HOME} -v ${env.SSH_AUTH_SOCK}:${env.SSH_AUTH_SOCK} -e HOME=${env.HOME}") {
- sh "./workflow/build.sh ${flavor} ${sync} ${sanitizerFlags}"
- }
- }
- }
- }
-}
-
-def doAndroidDockerBuild() {
- return {
- node('docker') {
- getSourceArchive()
- wrap([$class: 'AnsiColorBuildWrapper']) {
- def image = docker.build('realm-object-store:ndk21', '-f android.Dockerfile .')
- docker.image('tracer0tong/android-emulator').withRun { emulator ->
- image.inside("--link ${emulator.id}:emulator") {
- sh """
- cmake -B build -DREALM_PLATFORM=Android -DANDROID_NDK=\${ANDROID_NDK} -GNinja -DCMAKE_MAKE_PROGRAM=ninja
- cmake --build build
- adb connect emulator
- timeout 10m adb wait-for-device
- adb push build/tests/tests /data/local/tmp
- adb shell '/data/local/tmp/tests || echo __ADB_FAIL__' | tee adb.log
- ! grep __ADB_FAIL__ adb.log
- """
- }
- }
- }
- }
- }
-}
-
-def doBuild(String nodeSpec, String flavor, Boolean enableSync, String version) {
- def sync = enableSync ? "sync" : "false"
- def label = "${flavor}${enableSync ? '-sync' : ''}"
- return {
- node(nodeSpec) {
- getSourceArchive()
- sshagent(['realm-ci-ssh']) {
- sh "./workflow/test_coverage.sh ${sync} ${version} && mv coverage.build ${label}.build"
- }
- echo "Stashing coverage-${label}"
- stash includes: "${label}.build/coverage.xml", name: "coverage-${label}"
- }
- }
-}
-
-def doWindowsBuild() {
- return {
- node('windows') {
- getSourceArchive()
-
- bat """
- "${tool 'cmake'}" . -DCMAKE_SYSTEM_VERSION="8.1"
- "${tool 'cmake'}" --build . --config Release
- tests\\Release\\tests.exe
- """
- }
- }
-}
-
-def doWindowsUniversalBuild() {
- return {
- node('windows') {
- getSourceArchive()
-
- bat """
- "${tool 'cmake'}" . -DCMAKE_SYSTEM_NAME="WindowsStore" -DCMAKE_SYSTEM_VERSION="10.0"
- "${tool 'cmake'}" --build . --config Release --target realm-object-store
- """
- }
- }
-}
-
-def setBuildName(newBuildName) {
- currentBuild.displayName = "${currentBuild.displayName} - ${newBuildName}"
-}
-
-stage('prepare') {
- node('docker') {
- checkout scm
- sh 'git clean -ffdx -e .????????'
- sshagent(['realm-ci-ssh']) {
- sh 'git submodule update --init --recursive'
- }
-
- gitTag = readGitTag()
- gitSha = readGitSha()
- echo "tag: ${gitTag}"
- if (gitTag == "") {
- echo "No tag given for this build"
- setBuildName("${gitSha}")
- } else {
- echo "Building release: '${gitTag}'"
- setBuildName("Tag ${gitTag}")
- }
-
- stash includes: '**', name: 'source'
- }
-}
-
-stage('unit-tests') {
- parallel(
- linux: doDockerBuild('linux', false),
- linux_sync: doDockerBuild('linux', true),
- linux_asan: doDockerBuild('linux', true, '-DSANITIZE_ADDRESS=1'),
- linux_tsan: doDockerBuild('linux', true, '-DSANITIZE_THREAD=1'),
- android: doAndroidDockerBuild(),
- macos: doBuild('osx', 'macOS', false, ''),
- macos_sync: doBuild('osx', 'macOS', true, ''),
- win32: doWindowsBuild(),
- windows_universal: doWindowsUniversalBuild()
- )
- currentBuild.result = 'SUCCESS'
-}
-
-stage('publish') {
- node('docker') {
- // we need sources to allow the coverage report to display them
- rlmCheckout(scm)
- // coverage reports assume sources are in the parent directory
- dir("build") {
- publishCoverageReport('macOS-sync')
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE
deleted file mode 100644
index 843725a69..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE
+++ /dev/null
@@ -1,269 +0,0 @@
-TABLE OF CONTENTS
-
-1. Apache License version 2.0
-2. Realm Components
-3. Export Compliance
-
--------------------------------------------------------------------------------
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- 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.
-
-
-REALM COMPONENTS
-
-This software contains components with separate copyright and license terms.
-Your use of these components is subject to the terms and conditions of the
-following licenses.
-
-For the Realm Core component
-
- Realm Core Binary License
-
- Copyright (c) 2011-2016 Realm Inc All rights reserved
-
- Redistribution and use in binary form, with or without modification, is
- permitted provided that the following conditions are met:
-
- 1. You agree not to attempt to decompile, disassemble, reverse engineer or
- otherwise discover the source code from which the binary code was derived.
- You may, however, access and obtain a separate license for most of the
- source code from which this Software was created, at
- http://realm.io/pricing/.
-
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- 3. Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
-EXPORT COMPLIANCE
-
-You understand that the Software may contain cryptographic functions that may be
-subject to export restrictions, and you represent and warrant that you are not
-located in a country that is subject to United States export restriction or embargo,
-including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region, and that you
-are not on the Department of Commerce list of Denied Persons, Unverified Parties,
-or affiliated with a Restricted Entity.
-
-You agree to comply with all export, re-export and import restrictions and
-regulations of the Department of Commerce or other agency or authority of the
-United States or other applicable countries. You also agree not to transfer, or
-authorize the transfer of, directly or indirectly, the Software to any prohibited
-country, including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region,
-or to any person or organization on or affiliated with the Department of
-Commerce lists of Denied Persons, Unverified Parties or Restricted Entities, or
-otherwise in violation of any such restrictions or regulations.
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
deleted file mode 100644
index 9710b0a6d..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
+++ /dev/null
@@ -1,4 +0,0 @@
-REALM_CORE_VERSION=6.0.10
-REALM_SYNC_VERSION=5.0.9
-REALM_CORE_PACKAGING=2
-OPENSSL_VERSION=1.1.1b
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt
deleted file mode 100644
index 30d703995..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt
+++ /dev/null
@@ -1,142 +0,0 @@
-set(SOURCES
- binding_callback_thread_observer.cpp
- collection_notifications.cpp
- index_set.cpp
- list.cpp
- object.cpp
- object_changeset.cpp
- object_schema.cpp
- object_store.cpp
- results.cpp
- schema.cpp
- shared_realm.cpp
- thread_safe_reference.cpp
-
- impl/collection_change_builder.cpp
- impl/collection_notifier.cpp
- impl/list_notifier.cpp
- impl/object_notifier.cpp
- impl/realm_coordinator.cpp
- impl/results_notifier.cpp
- impl/transact_log_handler.cpp
- impl/weak_realm_notifier.cpp
- util/scheduler.cpp
- util/uuid.cpp)
-
-set(HEADERS
- binding_callback_thread_observer.hpp
- collection_notifications.hpp
- feature_checks.hpp
- index_set.hpp
- keypath_helpers.hpp
- list.hpp
- object.hpp
- object_accessor.hpp
- object_changeset.hpp
- object_schema.hpp
- object_store.hpp
- property.hpp
- results.hpp
- schema.hpp
- shared_realm.hpp
- thread_safe_reference.hpp
-
- impl/apple/external_commit_helper.hpp
- impl/apple/keychain_helper.hpp
- impl/epoll/external_commit_helper.hpp
- impl/generic/external_commit_helper.hpp
-
- impl/collection_change_builder.hpp
- impl/collection_notifier.hpp
- impl/external_commit_helper.hpp
- impl/list_notifier.hpp
- impl/notification_wrapper.hpp
- impl/object_accessor_impl.hpp
- impl/object_notifier.hpp
- impl/realm_coordinator.hpp
- impl/results_notifier.hpp
- impl/transact_log_handler.hpp
- impl/weak_realm_notifier.hpp
-
- util/android/scheduler.hpp
- util/apple/scheduler.hpp
- util/generic/scheduler.hpp
- util/uv/scheduler.hpp
-
- util/aligned_union.hpp
- util/atomic_shared_ptr.hpp
- util/checked_mutex.hpp
- util/copyable_atomic.hpp
- util/event_loop_dispatcher.hpp
- util/scheduler.hpp
- util/tagged_bool.hpp
- util/uuid.hpp)
-
-if(APPLE)
- list(APPEND SOURCES impl/apple/external_commit_helper.cpp impl/apple/keychain_helper.cpp)
-elseif(REALM_HAVE_EPOLL)
- list(APPEND SOURCES impl/epoll/external_commit_helper.cpp)
-elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
- list(APPEND SOURCES impl/windows/external_commit_helper.cpp)
-else()
- list(APPEND SOURCES impl/generic/external_commit_helper.cpp)
-endif()
-
-set(INCLUDE_DIRS
- ${UV_INCLUDE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-
-if(REALM_ENABLE_SYNC)
- list(APPEND HEADERS
- sync/async_open_task.hpp
- sync/partial_sync.hpp
- sync/subscription_state.hpp
- sync/sync_config.hpp
- sync/sync_manager.hpp
- sync/sync_session.hpp
- sync/sync_user.hpp
- sync/impl/sync_client.hpp
- sync/impl/sync_file.hpp
- sync/impl/sync_metadata.hpp
- sync/impl/work_queue.hpp)
- list(APPEND SOURCES
- sync/async_open_task.cpp
- sync/partial_sync.cpp
- sync/sync_config.cpp
- sync/sync_manager.cpp
- sync/sync_session.cpp
- sync/sync_user.cpp
- sync/impl/sync_file.cpp
- sync/impl/sync_metadata.cpp
- sync/impl/work_queue.cpp)
- if(APPLE)
- list(APPEND SOURCES
- sync/impl/apple/network_reachability_observer.cpp
- sync/impl/apple/system_configuration.cpp)
- endif()
- find_package(ZLIB REQUIRED)
- list(APPEND INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS})
-endif()
-
-if(REALM_ENABLE_SERVER)
- list(APPEND HEADERS
- server/adapter.hpp
- server/admin_realm.hpp
- server/global_notifier.hpp)
- list(APPEND SOURCES
- server/adapter.cpp
- server/admin_realm.cpp
- server/global_notifier.cpp)
- list(APPEND INCLUDE_DIRS ../external/json)
-endif()
-
-add_library(realm-object-store STATIC ${SOURCES} ${HEADERS})
-set_target_properties(realm-object-store PROPERTIES POSITION_INDEPENDENT_CODE 1)
-target_compile_definitions(realm-object-store PRIVATE ${PLATFORM_DEFINES})
-target_include_directories(realm-object-store PUBLIC ${INCLUDE_DIRS})
-target_link_libraries(realm-object-store PUBLIC realm ${PLATFORM_LIBRARIES})
-
-if(REALM_ENABLE_SYNC)
- # Add the sync libraries separately to reduce merge conflicts.
- target_link_libraries(realm-object-store PUBLIC realm-sync ${ZLIB_LIBRARIES})
-endif()
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.cpp
deleted file mode 100644
index 0c388d068..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "binding_callback_thread_observer.hpp"
-
-namespace realm {
-BindingCallbackThreadObserver* g_binding_callback_thread_observer = nullptr;
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.hpp
deleted file mode 100644
index e956349b2..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_callback_thread_observer.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
-#define REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
-
-#include <exception>
-
-namespace realm {
-// Interface for bindings interested in registering callbacks before/after the ObjectStore thread runs.
-// This is for example helpful to attach/detach the pthread to the JavaVM in order to be able to perform JNI calls.
-class BindingCallbackThreadObserver {
-public:
- // This method is called just before the thread is started
- virtual void did_create_thread() = 0;
-
- // This method is called just before the thread is being destroyed
- virtual void will_destroy_thread() = 0;
-
- // This method is called with any exception throws by client.run().
- virtual void handle_error(std::exception const& e) = 0;
-};
-
-extern BindingCallbackThreadObserver* g_binding_callback_thread_observer;
-}
-
-#endif // REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp
deleted file mode 100644
index de83966b1..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp
+++ /dev/null
@@ -1,180 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 BINDING_CONTEXT_HPP
-#define BINDING_CONTEXT_HPP
-
-#include "index_set.hpp"
-
-#include <realm/keys.hpp>
-
-#include <memory>
-#include <tuple>
-#include <unordered_map>
-#include <vector>
-
-namespace realm {
-// BindingContext is the extension point for adding binding-specific behavior to
-// a SharedRealm. It can be used to store additional data associated with the
-// Realm which is needed by the binding, and there are several methods which
-// can be overridden to receive notifications of state changes within the Realm.
-//
-// A simple implementation which lets the user register functions to be
-// called on refresh could look like the following:
-//
-// class BindingContextImplementation : public BindingContext {
-// public:
-// // A token returned from add_notification that can be used to remove the
-// // notification later
-// struct token : private std::list<std::function<void ()>>::iterator {
-// token(std::list<std::function<void ()>>::iterator it) : std::list<std::function<void ()>>::iterator(it) { }
-// friend class DelegateImplementation;
-// };
-//
-// token add_notification(std::function<void ()> func)
-// {
-// m_registered_notifications.push_back(std::move(func));
-// return token(std::prev(m_registered_notifications.end()));
-// }
-//
-// void remove_notification(token entry)
-// {
-// m_registered_notifications.erase(entry);
-// }
-//
-// // Override the did_change method to call each registered notification
-// void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
-// {
-// // Loop oddly so that unregistering a notification from within the
-// // registered function works
-// for (auto it = m_registered_notifications.begin(); it != m_registered_notifications.end(); ) {
-// (*it++)();
-// }
-// }
-//
-// private:
-// std::list<std::function<void ()>> m_registered_notifications;
-// };
-class Realm;
-class Schema;
-class BindingContext {
-public:
- virtual ~BindingContext() = default;
-
- std::weak_ptr<Realm> realm;
-
- // Called when the Realm is about to send notifications about Realm,
- // Collection or Object changes. This method will be called even if
- // no notification callbacks have been registered.
- virtual void will_send_notifications() { }
-
- // Called when the Realm is done sending all change notifications. This method
- // will be called even if no notification callbacks have been registered.
- virtual void did_send_notifications() { }
-
- // Called by the Realm when refresh called or a notification arrives which
- // is triggered through write transaction committed by itself or a different
- // Realm instance.
- virtual void before_notify() { }
-
- // Called by the Realm when a write transaction is committed to the file by
- // a different Realm instance (possibly in a different process)
- virtual void changes_available() { }
-
- struct ObserverState;
-
- // Override this function if you want to receive detailed information about
- // external changes to a specific set of objects.
- // This is called before each operation which may advance the read
- // transaction to include
- // ObserverStates for each row for which detailed change information is
- // desired.
- virtual std::vector<ObserverState> get_observed_rows() { return {}; }
-
- // Called immediately before the read transaction is advanced if detailed
- // change information was requested (by returning a non-empty array from
- // get_observed_rows()).
- // The observers vector is the vector returned by get_observed_row(),
- // updated with change information. The invalidated vector is a list of the
- // `info` fields of observed rows which will be deleted.
- virtual void will_change(std::vector<ObserverState> const& observers,
- std::vector<void*> const& invalidated);
-
- // Called immediately after the read transaction version is advanced. Unlike
- // will_change(), this is called even if detailed change information was not
- // requested or if the Realm is not actually in a read transaction, although
- // both vectors will be empty in that case.
- virtual void did_change(std::vector<ObserverState> const& observers,
- std::vector<void*> const& invalidated,
- bool version_changed=true);
-
- // Called immediately after the corresponding Realm's schema is changed through
- // update_schema()/set_schema_subset() or the schema is changed by another Realm
- // instance. The parameter is a schema reference which is the same as the return
- // value of Realm::schema().
- virtual void schema_did_change(Schema const&) {}
-
- // Change information for a single field of a row
- struct ColumnInfo {
- // What kind of change occurred?
- // Always Set or None for everything but LinkList columns.
- enum class Kind {
- None, // No change
- Set, // The value or entries at `indices` were assigned to
- Insert, // New values were inserted at each of the indices given
- Remove, // Values were removed at each of the indices given
- SetAll // The entire LinkList has been replaced with a new set of values
- } kind = Kind::None;
- // The indices where things happened for Set, Insert and Remove on
- // LinkList columns. Not used for other types or for None or SetAll.
- IndexSet indices;
- };
-
- // Information about an observed row in a table
- //
- // Each object which needs detailed change information should have an
- // ObserverState entry in the vector returned from get_observed_rows(), with
- // the initial table and row indexes set (and optionally the info field).
- // The Realm parses the transaction log, and populates the `changes` vector
- // in each ObserverState with information about what changes were made.
- struct ObserverState {
- // Table and row which is observed
- realm::TableKey table_key;
- int64_t obj_key;
-
- // Opaque userdata for the delegate's use
- void* info;
-
- // Populated with information about which columns were changed
- // May be shorter than the actual number of columns if the later columns
- // are not modified
- std::unordered_map<int64_t, ColumnInfo> changes;
-
- // Simple lexographic ordering
- friend bool operator<(ObserverState const& lft, ObserverState const& rgt)
- {
- return std::tie(lft.table_key, lft.obj_key) < std::tie(rgt.table_key, rgt.obj_key);
- }
- };
-};
-
-inline void BindingContext::will_change(std::vector<ObserverState> const&, std::vector<void*> const&) { }
-inline void BindingContext::did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) { }
-} // namespace realm
-
-#endif /* BINDING_CONTEXT_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.cpp
deleted file mode 100644
index 03fad73f1..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "collection_notifications.hpp"
-
-#include "impl/collection_notifier.hpp"
-
-using namespace realm;
-using namespace realm::_impl;
-
-NotificationToken::NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, uint64_t token)
-: m_notifier(std::move(notifier)), m_token(token)
-{
-}
-
-NotificationToken::~NotificationToken()
-{
- // m_notifier itself (and not just the pointed-to thing) needs to be accessed
- // atomically to ensure that there are no data races when the token is
- // destroyed after being modified on a different thread.
- // This is needed despite the token not being thread-safe in general as
- // users find it very surprising for obj-c objects to care about what
- // thread they are deallocated on.
- if (auto notifier = m_notifier.exchange({})) {
- notifier->remove_callback(m_token);
- }
-}
-
-NotificationToken::NotificationToken(NotificationToken&&) = default;
-
-NotificationToken& NotificationToken::operator=(realm::NotificationToken&& rgt)
-{
- if (this != &rgt) {
- if (auto notifier = m_notifier.exchange({})) {
- notifier->remove_callback(m_token);
- }
- m_notifier = std::move(rgt.m_notifier);
- m_token = rgt.m_token;
- }
- return *this;
-}
-
-void NotificationToken::suppress_next()
-{
- m_notifier.load()->suppress_next_notification(m_token);
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp
deleted file mode 100644
index 3b4dca13f..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp
+++ /dev/null
@@ -1,183 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_COLLECTION_NOTIFICATIONS_HPP
-#define REALM_COLLECTION_NOTIFICATIONS_HPP
-
-#include "index_set.hpp"
-#include "util/atomic_shared_ptr.hpp"
-
-#include <exception>
-#include <memory>
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
-
-namespace realm {
-namespace _impl {
- class CollectionNotifier;
-}
-
-// A token which keeps an asynchronous query alive
-struct NotificationToken {
- NotificationToken() = default;
- NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, uint64_t token);
- ~NotificationToken();
-
- NotificationToken(NotificationToken&&);
- NotificationToken& operator=(NotificationToken&&);
-
- NotificationToken(NotificationToken const&) = delete;
- NotificationToken& operator=(NotificationToken const&) = delete;
-
- void suppress_next();
-
-private:
- util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier;
- uint64_t m_token;
-};
-
-struct CollectionChangeSet {
- struct Move {
- size_t from;
- size_t to;
-
- bool operator==(Move m) const noexcept { return from == m.from && to == m.to; }
- };
-
- // Indices which were removed from the _old_ collection
- IndexSet deletions;
-
- // Indices in the _new_ collection which are new insertions
- IndexSet insertions;
-
- // Indices of objects in the _old_ collection which were modified
- IndexSet modifications;
-
- // Indices in the _new_ collection which were modified. This will always
- // have the same number of indices as `modifications` and conceptually
- // represents the same entries, just in different versions of the collection.
- // It exists for the sake of code which finds it easier to process
- // modifications after processing deletions and insertions rather than before.
- IndexSet modifications_new;
-
- // Rows in the collection which moved.
- //
- // Every `from` index will also be present in `deletions` and every `to`
- // index will be present in `insertions`.
- //
- // This is currently not reliably calculated for all types of collections. A
- // reported move will always actually be a move, but there may also be
- // unreported moves which show up only as a delete/insert pair.
- std::vector<Move> moves;
-
- // Per-column version of `modifications`
- std::unordered_map<int64_t, IndexSet> columns;
-
- bool empty() const noexcept
- {
- return deletions.empty() && insertions.empty() && modifications.empty()
- && modifications_new.empty() && moves.empty();
- }
-};
-
-// A type-erasing wrapper for the callback for collection notifications. Can be
-// constructed with either any callable compatible with the signature
-// `void (CollectionChangeSet, std::exception_ptr)`, an object with member
-// functions `void before(CollectionChangeSet)`, `void after(CollectionChangeSet)`,
-// `void error(std::exception_ptr)`, or a pointer to such an object. If a pointer
-// is given, the caller is responsible for ensuring that the pointed-to object
-// outlives the collection.
-class CollectionChangeCallback {
-public:
- CollectionChangeCallback(std::nullptr_t={}) { }
-
- template<typename Callback>
- CollectionChangeCallback(Callback cb) : m_impl(make_impl(std::move(cb))) { }
- template<typename Callback>
- CollectionChangeCallback& operator=(Callback cb) { m_impl = make_impl(std::move(cb)); return *this; }
-
- // Explicitly default the copy/move constructors as otherwise they'll use
- // the above ones and add an extra layer of wrapping
- CollectionChangeCallback(CollectionChangeCallback&&) = default;
- CollectionChangeCallback(CollectionChangeCallback const&) = default;
- CollectionChangeCallback& operator=(CollectionChangeCallback&&) = default;
- CollectionChangeCallback& operator=(CollectionChangeCallback const&) = default;
-
- void before(CollectionChangeSet const& c) { m_impl->before(c); }
- void after(CollectionChangeSet const& c) { m_impl->after(c); }
- void error(std::exception_ptr e) { m_impl->error(e); }
-
- explicit operator bool() const { return !!m_impl; }
-
-private:
- struct Base {
- virtual ~Base() {}
- virtual void before(CollectionChangeSet const&)=0;
- virtual void after(CollectionChangeSet const&)=0;
- virtual void error(std::exception_ptr)=0;
- };
-
- template<typename Callback, typename = decltype(std::declval<Callback>()(CollectionChangeSet(), std::exception_ptr()))>
- std::shared_ptr<Base> make_impl(Callback cb)
- {
- return std::make_shared<Impl<Callback>>(std::move(cb));
- }
-
- template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
- std::shared_ptr<Base> make_impl(Callback cb)
- {
- return std::make_shared<Impl2<Callback>>(std::move(cb));
- }
-
- template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
- std::shared_ptr<Base> make_impl(Callback* cb)
- {
- return std::make_shared<Impl3<Callback>>(cb);
- }
-
- template<typename T>
- struct Impl : public Base {
- T impl;
- Impl(T impl) : impl(std::move(impl)) { }
- void before(CollectionChangeSet const&) override { }
- void after(CollectionChangeSet const& change) override { impl(change, {}); }
- void error(std::exception_ptr error) override { impl({}, error); }
- };
- template<typename T>
- struct Impl2 : public Base {
- T impl;
- Impl2(T impl) : impl(std::move(impl)) { }
- void before(CollectionChangeSet const& c) override { impl.before(c); }
- void after(CollectionChangeSet const& c) override { impl.after(c); }
- void error(std::exception_ptr error) override { impl.error(error); }
- };
- template<typename T>
- struct Impl3 : public Base {
- T* impl;
- Impl3(T* impl) : impl(impl) { }
- void before(CollectionChangeSet const& c) override { impl->before(c); }
- void after(CollectionChangeSet const& c) override { impl->after(c); }
- void error(std::exception_ptr error) override { impl->error(error); }
- };
-
- std::shared_ptr<Base> m_impl;
-};
-} // namespace realm
-
-#endif // REALM_COLLECTION_NOTIFICATIONS_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/feature_checks.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/feature_checks.hpp
deleted file mode 100644
index a3fd8a51c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/feature_checks.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_FEATURE_CHECKS_HPP
-#define REALM_OS_FEATURE_CHECKS_HPP
-
-#include <realm/version.hpp>
-
-#if REALM_ENABLE_SYNC
-
-#include <realm/sync/version.hpp>
-
-#endif // REALM_ENABLE_SYNC
-
-#endif // REALM_OS_FEATURE_CHECKS_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp
deleted file mode 100644
index 1c4dab027..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/external_commit_helper.hpp"
-#include "impl/realm_coordinator.hpp"
-
-#include <realm/db_options.hpp>
-#include <realm/util/fifo_helper.hpp>
-
-#include <asl.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <sstream>
-#include <sys/event.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <system_error>
-#include <unistd.h>
-
-using namespace realm;
-using namespace realm::_impl;
-
-namespace {
-// Write a byte to a pipe to notify anyone waiting for data on the pipe
-void notify_fd(int fd, int read_fd)
-{
- while (true) {
- char c = 0;
- ssize_t ret = write(fd, &c, 1);
- if (ret == 1) {
- break;
- }
-
- // If the pipe's buffer is full, we need to read some of the old data in
- // it to make space. We don't just read in the code waiting for
- // notifications so that we can notify multiple waiters with a single
- // write.
- assert(ret == -1 && errno == EAGAIN);
- char buff[1024];
- read(read_fd, buff, sizeof buff);
- }
-}
-
-} // anonymous namespace
-
-void ExternalCommitHelper::FdHolder::close()
-{
- if (m_fd != -1) {
- ::close(m_fd);
- }
- m_fd = -1;
-}
-
-// Inter-thread and inter-process notifications of changes are done using a
-// named pipe in the filesystem next to the Realm file. Everyone who wants to be
-// notified of commits waits for data to become available on the pipe, and anyone
-// who commits a write transaction writes data to the pipe after releasing the
-// write lock. Note that no one ever actually *reads* from the pipe: the data
-// actually written is meaningless, and trying to read from a pipe from multiple
-// processes at once is fraught with race conditions.
-
-// When a RLMRealm instance is created, we add a CFRunLoopSource to the current
-// thread's runloop. On each cycle of the run loop, the run loop checks each of
-// its sources for work to do, which in the case of CFRunLoopSource is just
-// checking if CFRunLoopSourceSignal has been called since the last time it ran,
-// and if so invokes the function pointer supplied when the source is created,
-// which in our case just invokes `[realm handleExternalChange]`.
-
-// Listening for external changes is done using kqueue() on a background thread.
-// kqueue() lets us efficiently wait until the amount of data which can be read
-// from one or more file descriptors has changed, and tells us which of the file
-// descriptors it was that changed. We use this to wait on both the shared named
-// pipe, and a local anonymous pipe. When data is written to the named pipe, we
-// signal the runloop source and wake up the target runloop, and when data is
-// written to the anonymous pipe the background thread removes the runloop
-// source from the runloop and and shuts down.
-ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
-: m_parent(parent)
-{
- m_kq = kqueue();
- if (m_kq == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
-#if !TARGET_OS_TV
-
-
- // Object Store needs to create a named pipe in order to coordinate notifications.
- // This can be a problem on some file systems (e.g. FAT32) or due to security policies in SELinux. Most commonly
- // it is a problem when saving Realms on external storage: https://stackoverflow.com/questions/2740321/how-to-create-named-pipe-mkfifo-in-android
- //
- // For this reason we attempt to create this file in a temporary location known to be safe to write these files.
- //
- // In order of priority we attempt to write the file in the following locations:
- // 1) Next to the Realm file itself
- // 2) A location defined by `Realm::Config::fifo_files_fallback_path`
- // 3) A location defined by `DBOptions::set_sys_tmp_dir()`
- //
- // Core has a similar policy for its named pipes.
- //
- // Also see https://github.com/realm/realm-java/issues/3140
- // Note that hash collisions are okay here because they just result in doing extra work instead of resulting
- // in correctness problems.
-
- std::string path;
- std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
- std::string sys_temp_dir = util::normalize_dir(DBOptions::get_sys_tmp_dir());
-
- path = parent.get_path() + ".note";
- bool fifo_created = realm::util::try_create_fifo(path);
- if (!fifo_created && !temp_dir.empty()) {
- path = util::format("%1realm_%2.note", temp_dir, std::hash<std::string>()(parent.get_path()));
- fifo_created = realm::util::try_create_fifo(path);
- }
- if (!fifo_created && !sys_temp_dir.empty()) {
- path = util::format("%1realm_%2.note", sys_temp_dir, std::hash<std::string>()(parent.get_path()));
- realm::util::create_fifo(path);
- }
-
- m_notify_fd = open(path.c_str(), O_RDWR);
- if (m_notify_fd == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- // Make writing to the pipe return -1 when the pipe's buffer is full
- // rather than blocking until there's space available
- int ret = fcntl(m_notify_fd, F_SETFL, O_NONBLOCK);
- if (ret == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
-#else // !TARGET_OS_TV
-
- // tvOS does not support named pipes, so use an anonymous pipe instead
- int notification_pipe[2];
- int ret = pipe(notification_pipe);
- if (ret == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- m_notify_fd = notification_pipe[0];
- m_notify_fd_write = notification_pipe[1];
-
-#endif // TARGET_OS_TV
-
- // Create the anonymous pipe for shutdown notifications
- int shutdown_pipe[2];
- ret = pipe(shutdown_pipe);
- if (ret == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- m_shutdown_read_fd = shutdown_pipe[0];
- m_shutdown_write_fd = shutdown_pipe[1];
-
- m_thread = std::thread([=] {
- try {
- listen();
- }
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- catch (std::exception const& e) {
- fprintf(stderr, "uncaught exception in notifier thread: %s: %s\n", typeid(e).name(), e.what());
- asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread: %s: %s", typeid(e).name(), e.what());
- throw;
- }
- catch (...) {
- fprintf(stderr, "uncaught exception in notifier thread\n");
- asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread");
- throw;
- }
-#pragma clang diagnostic pop
- });
-}
-
-ExternalCommitHelper::~ExternalCommitHelper()
-{
- notify_fd(m_shutdown_write_fd, m_shutdown_read_fd);
- m_thread.join(); // Wait for the thread to exit
-}
-
-void ExternalCommitHelper::listen()
-{
- pthread_setname_np("Realm notification listener");
-
- // Set up the kqueue
- // EVFILT_READ indicates that we care about data being available to read
- // on the given file descriptor.
- // EV_CLEAR makes it wait for the amount of data available to be read to
- // change rather than just returning when there is any data to read.
- struct kevent ke[2];
- EV_SET(&ke[0], m_notify_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
- EV_SET(&ke[1], m_shutdown_read_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
- int ret = kevent(m_kq, ke, 2, nullptr, 0, nullptr);
- assert(ret == 0);
-
- while (true) {
- struct kevent event;
- // Wait for data to become on either fd
- // Return code is number of bytes available or -1 on error
- ret = kevent(m_kq, nullptr, 0, &event, 1, nullptr);
- if (ret == 0 || (ret < 0 && errno == EINTR)) {
- // Spurious wakeup; just wait again
- continue;
- }
- assert(ret > 0);
-
- // Check which file descriptor had activity: if it's the shutdown
- // pipe, then someone called -stop; otherwise it's the named pipe
- // and someone committed a write transaction
- if (event.ident == (uint32_t)m_shutdown_read_fd) {
- return;
- }
- assert(event.ident == (uint32_t)m_notify_fd);
-
- m_parent.on_change();
- }
-}
-
-void ExternalCommitHelper::notify_others()
-{
- if (m_notify_fd_write != -1) {
- notify_fd(m_notify_fd_write, m_notify_fd);
- }
- else {
- notify_fd(m_notify_fd, m_notify_fd);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.hpp
deleted file mode 100644
index 1fc99ed7a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.hpp
+++ /dev/null
@@ -1,80 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include <thread>
-
-namespace realm {
-class Realm;
-
-namespace _impl {
-class RealmCoordinator;
-
-class ExternalCommitHelper {
-public:
- ExternalCommitHelper(RealmCoordinator& parent);
- ~ExternalCommitHelper();
-
- void notify_others();
-
-private:
- // A RAII holder for a file descriptor which automatically closes the wrapped
- // fd when it's deallocated
- class FdHolder {
- public:
- FdHolder() = default;
- ~FdHolder() { close(); }
- operator int() const { return m_fd; }
-
- FdHolder& operator=(int newFd) {
- close();
- m_fd = newFd;
- return *this;
- }
-
- private:
- int m_fd = -1;
- void close();
-
- FdHolder& operator=(FdHolder const&) = delete;
- FdHolder(FdHolder const&) = delete;
- };
-
- void listen();
-
- RealmCoordinator& m_parent;
-
- // The listener thread
- std::thread m_thread;
-
- // Pipe which is waited on for changes and written to when there is a new
- // commit to notify others of. When using a named pipe m_notify_fd is
- // read-write and m_notify_fd_write is unused; when using an anonymous pipe
- // (on tvOS) m_notify_fd is read-only and m_notify_fd_write is write-only.
- FdHolder m_notify_fd;
- FdHolder m_notify_fd_write;
-
- // File descriptor for the kqueue
- FdHolder m_kq;
-
- // The two ends of an anonymous pipe used to notify the kqueue() thread that
- // it should be shut down.
- FdHolder m_shutdown_read_fd;
- FdHolder m_shutdown_write_fd;
-};
-} // namespace _impl
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp
deleted file mode 100644
index 92b5b1ea5..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "impl/apple/keychain_helper.hpp"
-
-
-#include <realm/util/cf_ptr.hpp>
-#include <realm/util/optional.hpp>
-
-#include <Security/Security.h>
-
-#include <string>
-
-using realm::util::CFPtr;
-using realm::util::adoptCF;
-using realm::util::retainCF;
-
-namespace realm {
-namespace keychain {
-
-KeychainAccessException::KeychainAccessException(int32_t error_code)
-: std::runtime_error(util::format("Keychain returned unexpected status code: %1", error_code)) { }
-
-namespace {
-
-constexpr size_t key_size = 64;
-
-#if !TARGET_IPHONE_SIMULATOR
-CFPtr<CFStringRef> convert_string(const std::string& string)
-{
- auto result = adoptCF(CFStringCreateWithBytes(nullptr, reinterpret_cast<const UInt8*>(string.data()),
- string.size(), kCFStringEncodingASCII, false));
- if (!result) {
- throw std::bad_alloc();
- }
- return result;
-}
-#endif
-
-CFPtr<CFMutableDictionaryRef> build_search_dictionary(CFStringRef account, CFStringRef service,
- __unused util::Optional<std::string> group)
-{
- auto d = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- if (!d)
- throw std::bad_alloc();
-
- CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword);
- CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue);
- CFDictionaryAddValue(d.get(), kSecAttrAccount, account);
- CFDictionaryAddValue(d.get(), kSecAttrService, service);
-#if !TARGET_IPHONE_SIMULATOR
- if (group)
- CFDictionaryAddValue(d.get(), kSecAttrAccessGroup, convert_string(*group).get());
-#endif
- return d;
-}
-
-/// Get the encryption key for a given service, returning it only if it exists.
-util::Optional<std::vector<char>> get_key(CFStringRef account, CFStringRef service)
-{
- auto search_dictionary = build_search_dictionary(account, service, none);
- CFDataRef retained_key_data;
- if (OSStatus status = SecItemCopyMatching(search_dictionary.get(), (CFTypeRef *)&retained_key_data)) {
- if (status != errSecItemNotFound)
- throw KeychainAccessException(status);
-
- // Key was not found.
- return none;
- }
-
- // Key was previously stored. Extract it.
- CFPtr<CFDataRef> key_data = adoptCF(retained_key_data);
- if (key_size != CFDataGetLength(key_data.get()))
- throw std::runtime_error("Password stored in keychain was not expected size.");
-
- auto key_bytes = reinterpret_cast<const char *>(CFDataGetBytePtr(key_data.get()));
- return std::vector<char>(key_bytes, key_bytes + key_size);
-}
-
-void set_key(const std::vector<char>& key, CFStringRef account, CFStringRef service)
-{
- auto search_dictionary = build_search_dictionary(account, service, none);
- CFDictionaryAddValue(search_dictionary.get(), kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock);
- auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast<const UInt8 *>(key.data()), key_size));
- if (!key_data)
- throw std::bad_alloc();
-
- CFDictionaryAddValue(search_dictionary.get(), kSecValueData, key_data.get());
- if (OSStatus status = SecItemAdd(search_dictionary.get(), nullptr))
- throw KeychainAccessException(status);
-}
-
-} // anonymous namespace
-
-std::vector<char> metadata_realm_encryption_key(bool check_legacy_service)
-{
- CFStringRef account = CFSTR("metadata");
- CFStringRef legacy_service = CFSTR("io.realm.sync.keychain");
-
- CFPtr<CFStringRef> service;
- if (CFStringRef bundle_id = CFBundleGetIdentifier(CFBundleGetMainBundle()))
- service = adoptCF(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ - Realm Sync Metadata Key"), bundle_id));
- else {
- service = retainCF(legacy_service);
- check_legacy_service = false;
- }
-
- // Try retrieving the key.
- if (auto existing_key = get_key(account, service.get())) {
- return *existing_key;
- } else if (check_legacy_service) {
- // See if there's a key stored using the legacy shared keychain item.
- if (auto existing_legacy_key = get_key(account, legacy_service)) {
- // If so, copy it to the per-app keychain item before returning it.
- set_key(*existing_legacy_key, account, service.get());
- return *existing_legacy_key;
- }
- }
- // Make a completely new key.
- std::vector<char> key(key_size);
- arc4random_buf(key.data(), key_size);
- set_key(key, account, service.get());
- return key;
-}
-
-} // keychain
-} // realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.hpp
deleted file mode 100644
index 8915e78ee..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OS_KEYCHAIN_HELPER_HPP
-#define REALM_OS_KEYCHAIN_HELPER_HPP
-
-#include <cstdint>
-#include <stdexcept>
-#include <vector>
-
-namespace realm {
-namespace keychain {
-
-std::vector<char> metadata_realm_encryption_key(bool check_legacy_service);
-
-class KeychainAccessException : public std::runtime_error {
-public:
- KeychainAccessException(int32_t error_code);
-};
-
-}
-}
-
-#endif // REALM_OS_KEYCHAIN_HELPER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp
deleted file mode 100644
index 5695b4b71..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "impl/collection_change_builder.hpp"
-
-#include <realm/util/assert.hpp>
-
-#include <algorithm>
-
-using namespace realm;
-using namespace realm::_impl;
-
-CollectionChangeBuilder::CollectionChangeBuilder(IndexSet deletions,
- IndexSet insertions,
- IndexSet modifications,
- std::vector<Move> moves)
-: CollectionChangeSet({std::move(deletions), std::move(insertions), std::move(modifications), {}, std::move(moves)})
-{
- for (auto&& move : this->moves) {
- this->deletions.add(move.from);
- this->insertions.add(move.to);
- }
-}
-
-void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c)
-{
- if (c.empty())
- return;
- if (empty()) {
- *this = std::move(c);
- return;
- }
-
- verify();
- c.verify();
-
- // FIXME: this is comically wasteful
- std::unordered_set<int64_t> col_keys;
- if (m_track_columns) {
- for (auto& col : columns)
- col_keys.insert(col.first);
- for (auto& col : c.columns)
- col_keys.insert(col.first);
- }
-
- auto for_each_col = [&](auto&& f) {
- f(modifications, c.modifications);
- if (m_track_columns) {
- for (auto col : col_keys)
- f(columns[col], c.columns[col]);
- }
- };
-
- // First update any old moves
- if (!c.moves.empty() || !c.deletions.empty() || !c.insertions.empty()) {
- 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;
- });
- if (it != c.moves.end()) {
- for_each_col([&](auto& col, auto& other) {
- if (col.contains(it->from))
- other.add(it->to);
- });
- old.to = it->to;
- *it = c.moves.back();
- c.moves.pop_back();
- return false;
- }
-
- // Check if the destination was deleted
- // Removing the insert for this move will happen later
- if (c.deletions.contains(old.to))
- return true;
-
- // Update the destination to adjust for any new insertions and deletions
- old.to = c.insertions.shift(c.deletions.unshift(old.to));
- return false;
- });
- moves.erase(it, end(moves));
- }
-
- // 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(std::remove_if(begin(c.moves), end(c.moves),
- [&](auto const& m) { return insertions.contains(m.from); }),
- end(c.moves));
- }
-
- // Ensure that any previously modified rows which were moved are still modified
- if (!modifications.empty() && !c.moves.empty()) {
- for (auto const& move : c.moves) {
- for_each_col([&](auto& col, auto& other) {
- if (col.contains(move.from))
- other.add(move.to);
- });
- }
- }
-
- // Update the source position of new moves to compensate for the changes made
- // in the old changeset
- if (!deletions.empty() || !insertions.empty()) {
- for (auto& move : c.moves)
- move.from = deletions.shift(insertions.unshift(move.from));
- }
-
- moves.insert(end(moves), begin(c.moves), end(c.moves));
-
- // New deletion indices have been shifted by the insertions, so unshift them
- // before adding
- deletions.add_shifted_by(insertions, c.deletions);
-
- // Drop any inserted-then-deleted rows, then merge in new insertions
- insertions.erase_at(c.deletions);
- insertions.insert_at(c.insertions);
-
- clean_up_stale_moves();
-
- for_each_col([&](auto& col, auto& other) {
- col.erase_at(c.deletions);
- col.shift_for_insert_at(c.insertions);
- col.add(other);
- });
-
- c = {};
- verify();
-}
-
-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(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);
- insertions.remove(move.to);
- return true;
- }), end(moves));
-}
-
-void CollectionChangeBuilder::modify(size_t ndx, size_t col)
-{
- modifications.add(ndx);
- if (!m_track_columns || col == IndexSet::npos)
- return;
- columns[col].add(ndx);
-}
-
-template<typename Func>
-void CollectionChangeBuilder::for_each_col(Func&& f)
-{
- f(modifications);
- if (m_track_columns) {
- for (auto& col : columns)
- f(col.second);
- }
-}
-
-void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_moves)
-{
- REALM_ASSERT(count != 0);
-
- for_each_col([=](auto& col) { col.shift_for_insert_at(index, count); });
- if (!track_moves)
- return;
-
- insertions.insert_at(index, count);
-
- for (auto& move : moves) {
- if (move.to >= index)
- move.to += count;
- }
-}
-
-void CollectionChangeBuilder::erase(size_t index)
-{
- for_each_col([=](auto& col) { col.erase_at(index); });
- size_t unshifted = insertions.erase_or_unshift(index);
- if (unshifted != IndexSet::npos)
- deletions.add_shifted(unshifted);
-
- for (size_t i = 0; i < moves.size(); ++i) {
- auto& move = moves[i];
- if (move.to == index) {
- moves.erase(moves.begin() + i);
- --i;
- }
- else if (move.to > index)
- --move.to;
- }
-}
-
-void CollectionChangeBuilder::clear(size_t old_size)
-{
- for (auto range : deletions)
- old_size += range.second - range.first;
- for (auto range : insertions)
- old_size -= range.second - range.first;
-
- modifications.clear();
- insertions.clear();
- moves.clear();
- columns.clear();
- deletions.set(old_size);
-}
-
-void CollectionChangeBuilder::move(size_t from, size_t to)
-{
- REALM_ASSERT(from != to);
-
- bool updated_existing_move = false;
- for (auto& move : moves) {
- if (move.to != from) {
- // Shift other moves if this row is moving from one side of them
- // to the other
- if (move.to >= to && move.to < from)
- ++move.to;
- else if (move.to <= to && move.to > from)
- --move.to;
- continue;
- }
- REALM_ASSERT(!updated_existing_move);
-
- // Collapse A -> B, B -> C into a single A -> C move
- move.to = to;
- updated_existing_move = true;
-
- insertions.erase_at(from);
- insertions.insert_at(to);
- }
-
- if (!updated_existing_move) {
- auto shifted_from = insertions.erase_or_unshift(from);
- insertions.insert_at(to);
-
- // Don't report deletions/moves for newly inserted rows
- if (shifted_from != IndexSet::npos) {
- shifted_from = deletions.add_shifted(shifted_from);
- moves.push_back({shifted_from, to});
- }
- }
-
- for_each_col([=](auto& col) {
- bool modified = col.contains(from);
- col.erase_at(from);
-
- if (modified)
- col.insert_at(to);
- else
- col.shift_for_insert_at(to);
- });
-}
-
-void CollectionChangeBuilder::verify()
-{
-#ifdef REALM_DEBUG
- for (auto&& move : moves) {
- REALM_ASSERT(deletions.contains(move.from));
- REALM_ASSERT(insertions.contains(move.to));
- }
-#endif
-}
-
-namespace {
-struct RowInfo {
- int64_t key;
- size_t prev_tv_index;
- size_t tv_index;
-};
-
-#if 0 // FIXME: this is applicable to backlinks still
-// Calculates the insertions/deletions required for a query on a table without
-// a sort, where `removed` includes the rows which were modified to no longer
-// match the query (but not outright deleted rows, which are filtered out long
-// before any of this logic), and `move_candidates` tracks the rows which may
-// be the result of a move.
-//
-// This function is not strictly required, as calculate_moves_sorted() will
-// produce correct results even for the scenarios where this function is used.
-// However, this function has asymptotically better worst-case performance and
-// extremely cheap best-case performance, and is guaranteed to produce a minimal
-// diff.
-void calculate_moves_backlinks(std::vector<RowInfo>& new_rows, IndexSet& removed,
- IndexSet const& move_candidates,
- CollectionChangeSet& changeset)
-{
- // Here we track which row we expect to see, which in the absence of swap()
- // is always the row immediately after the last row which was not moved.
- size_t expected = 0;
- for (auto& row : new_rows) {
- if (row.shifted_tv_index == expected) {
- ++expected;
- continue;
- }
-
- // We didn't find the row we were expecting to find, which means that
- // either a row was moved forward to here, the row we were expecting was
- // removed, or the row we were expecting moved back.
-
- // First check if this row even could have moved. If it can't, just
- // treat it as a match and move on, and we'll handle the row we were
- // expecting when we hit it later.
- if (!move_candidates.contains(row.key)) {
- expected = row.shifted_tv_index + 1;
- continue;
- }
-
- // Next calculate where we expect this row to be based on the insertions
- // and removals (i.e. rows changed to not match the query), as it could
- // be that the row actually ends up in this spot due to the rows before
- // it being removed.
- size_t calc_expected = row.tv_index - changeset.insertions.count(0, row.tv_index) + removed.count(0, row.prev_tv_index);
- if (row.shifted_tv_index == calc_expected) {
- expected = calc_expected + 1;
- continue;
- }
-
- // The row still isn't the expected one, so record it as a move
- changeset.moves.push_back({row.prev_tv_index, row.tv_index});
- changeset.insertions.add(row.tv_index);
- removed.add(row.prev_tv_index);
- }
-}
-#endif
-
-class LongestCommonSubsequenceCalculator {
-public:
- // A pair of an object key and an index in the table view
- struct Row {
- int64_t key;
- size_t tv_index;
- };
-
- struct Match {
- // The index in `a` at which this match begins
- size_t i;
- // The index in `b` at which this match begins
- size_t j;
- // The length of this match
- size_t size;
- // The number of rows in this block which were modified
- size_t modified;
- };
- std::vector<Match> m_longest_matches;
-
- LongestCommonSubsequenceCalculator(std::vector<Row>& a, std::vector<Row>& b,
- size_t start_index,
- IndexSet const& modifications)
- : m_modified(modifications)
- , a(a), b(b)
- {
- find_longest_matches(start_index, a.size(),
- start_index, b.size());
- m_longest_matches.push_back({a.size(), b.size(), 0});
- }
-
-private:
- IndexSet const& m_modified;
-
- // The two arrays of rows being diffed
- // a is sorted by tv_index, b is sorted by key
- std::vector<Row> &a, &b;
-
- // Find the longest matching range in (a + begin1, a + end1) and (b + begin2, b + end2)
- // "Matching" is defined as "has the same row index"; the TV index is just
- // there to let us turn an index in a/b into an index which can be reported
- // in the output changeset.
- //
- // This is done with the O(N) space variant of the dynamic programming
- // algorithm for longest common subsequence, where N is the maximum number
- // of the most common row index (which for everything but linkview-derived
- // TVs will be 1).
- Match find_longest_match(size_t begin1, size_t end1, size_t begin2, size_t end2)
- {
- struct Length {
- size_t j, len;
- };
- // The length of the matching block for each `j` for the previously checked row
- std::vector<Length> prev;
- // The length of the matching block for each `j` for the row currently being checked
- std::vector<Length> cur;
-
- // Calculate the length of the matching block *ending* at b[j], which
- // is 1 if b[j - 1] did not match, and b[j - 1] + 1 otherwise.
- auto length = [&](size_t j) -> size_t {
- for (auto const& pair : prev) {
- if (pair.j + 1 == j)
- return pair.len + 1;
- }
- return 1;
- };
-
- // Iterate over each `j` which has the same row index as a[i] and falls
- // within the range begin2 <= j < end2
- auto for_each_b_match = [&](size_t i, auto&& f) {
- auto ai = a[i].key;
- // Find the TV indicies at which this row appears in the new results
- // There should always be at least one (or it would have been
- // filtered out earlier), but there can be multiple if there are dupes
- auto it = lower_bound(begin(b), end(b), ai,
- [](auto lft, auto rgt) { return lft.key < rgt; });
- REALM_ASSERT(it != end(b) && it->key == ai);
- for (; it != end(b) && it->key == ai; ++it) {
- size_t j = it->tv_index;
- if (j < begin2)
- continue;
- if (j >= end2)
- break; // b is sorted by tv_index so this can't transition from false to true
- f(j);
- }
- };
-
- Match best = {begin1, begin2, 0, 0};
- for (size_t i = begin1; i < end1; ++i) {
- // prev = std::move(cur), but avoids discarding prev's heap allocation
- cur.swap(prev);
- cur.clear();
-
- for_each_b_match(i, [&](size_t j) {
- size_t size = length(j);
-
- cur.push_back({j, size});
-
- // If the matching block ending at a[i] and b[j] is longer than
- // the previous one, select it as the best
- if (size > best.size)
- best = {i - size + 1, j - size + 1, size, IndexSet::npos};
- // Given two equal-length matches, prefer the one with fewer modified rows
- else if (size == best.size) {
- if (best.modified == IndexSet::npos)
- best.modified = m_modified.count(best.j - size + 1, best.j + 1);
- auto count = m_modified.count(j - size + 1, j + 1);
- if (count < best.modified)
- best = {i - size + 1, j - size + 1, size, count};
- }
-
- // The best block should always fall within the range being searched
- REALM_ASSERT(best.i >= begin1 && best.i + best.size <= end1);
- REALM_ASSERT(best.j >= begin2 && best.j + best.size <= end2);
- });
- }
- return best;
- }
-
- void find_longest_matches(size_t begin1, size_t end1, size_t begin2, size_t end2)
- {
- // FIXME: recursion could get too deep here
- // recursion depth worst case is currently O(N) and each recursion uses 320 bytes of stack
- // could reduce worst case to O(sqrt(N)) (and typical case to O(log N))
- // biasing equal selections towards the middle, but that's still
- // insufficient for Android's 8 KB stacks
- auto m = find_longest_match(begin1, end1, begin2, end2);
- if (!m.size)
- return;
- if (m.i > begin1 && m.j > begin2)
- find_longest_matches(begin1, m.i, begin2, m.j);
- m_longest_matches.push_back(m);
- if (m.i + m.size < end2 && m.j + m.size < end2)
- find_longest_matches(m.i + m.size, end1, m.j + m.size, end2);
- }
-};
-
-void calculate_moves_sorted(std::vector<RowInfo>& rows, CollectionChangeSet& changeset)
-{
- // The RowInfo array contains information about the old and new TV indices of
- // each row, which we need to turn into two sequences of rows, which we'll
- // then find matches in
- std::vector<LongestCommonSubsequenceCalculator::Row> a, b;
-
- a.reserve(rows.size());
- for (auto& row : rows)
- a.push_back({row.key, row.prev_tv_index});
- std::sort(begin(a), end(a), [](auto lft, auto rgt) {
- return std::tie(lft.tv_index, lft.key) < std::tie(rgt.tv_index, rgt.key);
- });
-
- // Before constructing `b`, first find the first index in `a` which will
- // actually differ in `b`, and skip everything else if there aren't any
- size_t first_difference = IndexSet::npos;
- for (size_t i = 0; i < a.size(); ++i) {
- if (a[i].key != rows[i].key) {
- first_difference = i;
- break;
- }
- }
- if (first_difference == IndexSet::npos)
- return;
-
- // Note that `b` is sorted by key, while `a` is sorted by tv_index
- b.reserve(rows.size());
- for (size_t i = 0; i < rows.size(); ++i)
- b.push_back({rows[i].key, i});
- std::sort(begin(b), end(b), [](auto lft, auto rgt) {
- return std::tie(lft.key, lft.tv_index) < std::tie(rgt.key, rgt.tv_index);
- });
-
- // Calculate the LCS of the two sequences
- auto matches = LongestCommonSubsequenceCalculator(a, b, first_difference,
- changeset.modifications).m_longest_matches;
-
- // And then insert and delete rows as needed to align them
- size_t i = first_difference, j = first_difference;
- for (auto match : matches) {
- for (; i < match.i; ++i)
- changeset.deletions.add(a[i].tv_index);
- for (; j < match.j; ++j)
- changeset.insertions.add(rows[j].tv_index);
- i += match.size;
- j += match.size;
- }
-}
-
-template<typename T>
-void verify_changeset(std::vector<T> const& prev_rows,
- std::vector<T> const& next_rows,
- CollectionChangeBuilder const& changeset)
-{
-#ifdef REALM_DEBUG
- { // Verify that applying the calculated change to prev_rows actually produces next_rows
- auto rows = prev_rows;
- auto it = util::make_reverse_iterator(changeset.deletions.end());
- auto end = util::make_reverse_iterator(changeset.deletions.begin());
- for (; it != end; ++it) {
- rows.erase(rows.begin() + it->first, rows.begin() + it->second);
- }
-
- for (auto i : changeset.insertions.as_indexes()) {
- rows.insert(rows.begin() + i, next_rows[i]);
- }
-
- REALM_ASSERT(rows == next_rows);
- }
-#else
- static_cast<void>(prev_rows);
- static_cast<void>(next_rows);
- static_cast<void>(changeset);
-#endif
-}
-
-void calculate(CollectionChangeBuilder& ret,
- std::vector<RowInfo> old_rows, std::vector<RowInfo> new_rows,
- std::function<bool (int64_t)> key_did_change, bool in_table_order)
-{
- // Now that our old and new sets of rows are sorted by key, we can
- // iterate over them and either record old+new TV indices for rows present
- // in both, or mark them as inserted/deleted if they appear only in one
- size_t i = 0, j = 0;
- while (i < old_rows.size() && j < new_rows.size()) {
- auto old_index = old_rows[i];
- auto& new_index = new_rows[j];
- if (old_index.key == new_index.key) {
- new_index.prev_tv_index = old_rows[i].tv_index;
- ++i;
- ++j;
- }
- else if (old_index.key < new_index.key) {
- ret.deletions.add(old_index.tv_index);
- ++i;
- }
- else {
- ret.insertions.add(new_index.tv_index);
- ++j;
- }
- }
-
- for (; i < old_rows.size(); ++i)
- ret.deletions.add(old_rows[i].tv_index);
- for (; j < new_rows.size(); ++j)
- ret.insertions.add(new_rows[j].tv_index);
-
- // Filter out the new insertions since we don't need them for any of the
- // further calculations
- new_rows.erase(std::remove_if(begin(new_rows), end(new_rows),
- [](auto& row) { return row.prev_tv_index == IndexSet::npos; }),
- end(new_rows));
- std::sort(begin(new_rows), end(new_rows),
- [](auto& lft, auto& rgt) { return lft.tv_index < rgt.tv_index; });
-
- for (auto& row : new_rows) {
- if (key_did_change(row.key)) {
- ret.modifications.add(row.tv_index);
- }
- }
-
- if (!in_table_order)
- calculate_moves_sorted(new_rows, ret);
-}
-
-} // Anonymous namespace
-
-CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector<int64_t> const& prev_rows,
- std::vector<int64_t> const& next_rows,
- std::function<bool (int64_t)> key_did_change,
- bool in_table_order)
-{
-
- auto build_row_info = [](auto& rows) {
- std::vector<RowInfo> info;
- info.reserve(rows.size());
- for (size_t i = 0; i < rows.size(); ++i)
- info.push_back({rows[i], IndexSet::npos, i});
- std::sort(begin(info), end(info), [](auto& lft, auto& rgt) { return lft.key < rgt.key; });
- return info;
- };
-
- CollectionChangeBuilder ret;
- ::calculate(ret, build_row_info(prev_rows), build_row_info(next_rows), std::move(key_did_change), in_table_order);
- ret.verify();
- verify_changeset(prev_rows, next_rows, ret);
- return ret;
-}
-
-CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector<size_t> const& prev_rows,
- std::vector<size_t> const& next_rows,
- std::function<bool (int64_t)> key_did_change)
-{
-
- auto build_row_info = [](auto& rows) {
- std::vector<RowInfo> info;
- info.reserve(rows.size());
- for (size_t i = 0; i < rows.size(); ++i)
- info.push_back({static_cast<int64_t>(rows[i]), IndexSet::npos, i});
- std::sort(begin(info), end(info), [](auto& lft, auto& rgt) { return lft.key < rgt.key; });
- return info;
- };
-
- CollectionChangeBuilder ret;
- ::calculate(ret, build_row_info(prev_rows), build_row_info(next_rows), std::move(key_did_change), false);
- ret.verify();
- verify_changeset(prev_rows, next_rows, ret);
- return ret;
-}
-
-CollectionChangeSet CollectionChangeBuilder::finalize() &&
-{
- // Calculate which indices in the old collection were modified
- auto modifications_in_old = modifications;
- modifications_in_old.erase_at(insertions);
- modifications_in_old.shift_for_insert_at(deletions);
-
- // During changeset calculation we allow marking a row as both inserted and
- // modified in case changeset merging results in it no longer being an insert,
- // but we don't want inserts in the final modification set
- modifications.remove(insertions);
-
- return {
- std::move(deletions),
- std::move(insertions),
- std::move(modifications_in_old),
- std::move(modifications),
- std::move(moves),
- std::move(columns)
- };
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp
deleted file mode 100644
index 93575e107..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp
+++ /dev/null
@@ -1,81 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_COLLECTION_CHANGE_BUILDER_HPP
-#define REALM_COLLECTION_CHANGE_BUILDER_HPP
-
-#include "collection_notifications.hpp"
-
-#include <realm/keys.hpp>
-
-#include <functional>
-#include <unordered_set>
-#include <vector>
-
-namespace realm {
-namespace _impl {
-
-class CollectionChangeBuilder : public CollectionChangeSet {
-public:
- CollectionChangeBuilder(CollectionChangeBuilder const&) = default;
- CollectionChangeBuilder(CollectionChangeBuilder&&) = default;
- CollectionChangeBuilder& operator=(CollectionChangeBuilder const&) = default;
- CollectionChangeBuilder& operator=(CollectionChangeBuilder&&) = default;
-
- CollectionChangeBuilder(IndexSet deletions = {},
- IndexSet insertions = {},
- IndexSet modification = {},
- std::vector<Move> moves = {});
-
- // Calculate where rows need to be inserted or deleted from old_rows to turn
- // it into new_rows, and check all matching rows for modifications
- static CollectionChangeBuilder calculate(std::vector<int64_t> const& old_rows,
- std::vector<int64_t> const& new_rows,
- std::function<bool (int64_t)> key_did_change,
- bool in_table_order);
- static CollectionChangeBuilder calculate(std::vector<size_t> const& old_rows,
- std::vector<size_t> const& new_rows,
- std::function<bool (int64_t)> key_did_change);
-
- // generic operations {
- CollectionChangeSet finalize() &&;
- void merge(CollectionChangeBuilder&&);
-
- void insert(size_t ndx, size_t count=1, bool track_moves=true);
- void modify(size_t ndx, size_t col=-1);
- void erase(size_t ndx);
- void clear(size_t old_size);
- // }
-
- // operations only implemented for LinkList semantics {
- void clean_up_stale_moves();
- void move(size_t from, size_t to);
- // }
-
-private:
- bool m_track_columns = true;
-
- template<typename Func>
- void for_each_col(Func&& f);
-
- void verify();
-};
-} // namespace _impl
-} // namespace realm
-
-#endif // REALM_COLLECTION_CHANGE_BUILDER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp
deleted file mode 100644
index 4da4aaf99..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "impl/collection_notifier.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "shared_realm.hpp"
-
-#include <realm/db.hpp>
-
-using namespace realm;
-using namespace realm::_impl;
-
-bool CollectionNotifier::all_related_tables_covered(const TableVersions& versions)
-{
- if (m_related_tables.size() > versions.size()) {
- return false;
- }
- auto first = versions.begin();
- auto last = versions.end();
- for (auto& it : m_related_tables) {
- TableKey tk{it.table_key};
- auto match = std::find_if(first, last, [tk](auto& elem) {
- return elem.first == tk;
- });
- if (match == last) {
- // tk not found in versions
- return false;
- }
- }
- return true;
-}
-
-std::function<bool (ObjectChangeSet::ObjectKeyType)>
-CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info,
- ConstTableRef root_table)
-{
- if (info.schema_changed)
- set_table(root_table);
-
- // First check if any of the tables accessible from the root table were
- // actually modified. This can be false if there were only insertions, or
- // deletions which were not linked to by any row in the linking table
- auto table_modified = [&](auto& tbl) {
- auto it = info.tables.find(tbl.table_key.value);
- return it != info.tables.end() && !it->second.modifications_empty();
- };
- if (!any_of(begin(m_related_tables), end(m_related_tables), table_modified)) {
- return [](ObjectChangeSet::ObjectKeyType) { return false; };
- }
- if (m_related_tables.size() == 1) {
- auto& object_set = info.tables.find(m_related_tables[0].table_key.value)->second;
- return [&](ObjectChangeSet::ObjectKeyType object_key) { return object_set.modifications_contains(object_key); };
- }
-
- return DeepChangeChecker(info, *root_table, m_related_tables);
-}
-
-void DeepChangeChecker::find_related_tables(std::vector<RelatedTable>& out, Table const& table)
-{
- auto table_key = table.get_key();
- if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_key == table_key; }))
- return;
-
- // We need to add this table to `out` before recurring so that the check
- // above works, but we can't store a pointer to the thing being populated
- // because the recursive calls may resize `out`, so instead look it up by
- // index every time
- size_t out_index = out.size();
- out.push_back({table_key, {}});
-
- for (auto col_key : table.get_column_keys()) {
- auto type = table.get_column_type(col_key);
- if (type == type_Link || type == type_LinkList) {
- out[out_index].links.push_back({col_key.value, type == type_LinkList});
- find_related_tables(out, *table.get_link_target(col_key));
- }
- }
-}
-
-DeepChangeChecker::DeepChangeChecker(TransactionChangeInfo const& info,
- Table const& root_table,
- std::vector<RelatedTable> const& related_tables)
-: m_info(info)
-, m_root_table(root_table)
-, m_root_table_key(root_table.get_key().value)
-, m_root_object_changes([&] {
- auto it = info.tables.find(m_root_table_key.value);
- return it != info.tables.end() ? &it->second : nullptr;
-}())
-, m_related_tables(related_tables)
-{
-}
-
-bool DeepChangeChecker::check_outgoing_links(TableKey table_key, Table const& table,
- int64_t obj_key, size_t depth)
-{
- auto it = find_if(begin(m_related_tables), end(m_related_tables),
- [&](auto&& tbl) { return tbl.table_key == table_key; });
- if (it == m_related_tables.end())
- return false;
- if (it->links.empty())
- return false;
-
- // Check if we're already checking if the destination of the link is
- // modified, and if not add it to the stack
- auto already_checking = [&](int64_t col) {
- auto end = m_current_path.begin() + depth;
- auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) {
- return p.obj_key == obj_key && p.col_key == col;
- });
- if (match != end) {
- for (; match < end; ++match) match->depth_exceeded = true;
- return true;
- }
- m_current_path[depth] = {obj_key, col, false};
- return false;
- };
-
- ConstObj obj = table.get_object(ObjKey(obj_key));
- auto linked_object_changed = [&](OutgoingLink const& link) {
- if (already_checking(link.col_key))
- return false;
- if (!link.is_list) {
- if (obj.is_null(ColKey(link.col_key)))
- return false;
- auto dst = obj.get<ObjKey>(ColKey(link.col_key)).value;
- return check_row(*table.get_link_target(ColKey(link.col_key)), dst, depth + 1);
- }
-
- auto& target = *table.get_link_target(ColKey(link.col_key));
- auto lvr = obj.get_linklist(ColKey(link.col_key));
- return std::any_of(lvr.begin(), lvr.end(),
- [&, this](auto key) { return this->check_row(target, key.value, depth + 1); });
- };
-
- return std::any_of(begin(it->links), end(it->links), linked_object_changed);
-}
-
-bool DeepChangeChecker::check_row(Table const& table, ObjKeyType key, size_t depth)
-{
- // Arbitrary upper limit on the maximum depth to search
- if (depth >= m_current_path.size()) {
- // Don't mark any of the intermediate rows checked along the path as
- // not modified, as a search starting from them might hit a modification
- for (size_t i = 0; i < m_current_path.size(); ++i)
- m_current_path[i].depth_exceeded = true;
- return false;
- }
-
- TableKey table_key = table.get_key();
- if (depth > 0) {
- auto it = m_info.tables.find(table_key.value);
- if (it != m_info.tables.end() && it->second.modifications_contains(key))
- return true;
- }
- auto& not_modified = m_not_modified[table_key.value];
- auto it = not_modified.find(key);
- if (it != not_modified.end())
- return false;
-
- bool ret = check_outgoing_links(table_key, table, key, depth);
- if (!ret && (depth == 0 || !m_current_path[depth - 1].depth_exceeded))
- not_modified.insert(key);
- return ret;
-}
-
-bool DeepChangeChecker::operator()(ObjKeyType key)
-{
- if (m_root_object_changes && m_root_object_changes->modifications_contains(key))
- return true;
- return check_row(m_root_table, key, 0);
-}
-
-CollectionNotifier::CollectionNotifier(std::shared_ptr<Realm> realm)
-: m_realm(std::move(realm))
-, m_sg_version(Realm::Internal::get_transaction(*m_realm).get_version_of_current_transaction())
-{
-}
-
-CollectionNotifier::~CollectionNotifier()
-{
- // Need to do this explicitly to ensure m_realm is destroyed with the mutex
- // held to avoid potential double-deletion
- unregister();
-}
-
-void CollectionNotifier::release_data() noexcept
-{
- m_sg = nullptr;
-}
-
-uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback)
-{
- m_realm->verify_thread();
-
- util::CheckedLockGuard lock(m_callback_mutex);
- auto token = m_next_token++;
- m_callbacks.push_back({std::move(callback), {}, {}, token, false, false});
- if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
- Realm::Internal::get_coordinator(*m_realm).wake_up_notifier_worker();
- m_have_callbacks = true;
- }
- return token;
-}
-
-void CollectionNotifier::remove_callback(uint64_t token)
-{
- // the callback needs to be destroyed after releasing the lock as destroying
- // it could cause user code to be called
- Callback old;
- {
- util::CheckedLockGuard lock(m_callback_mutex);
- auto it = find_callback(token);
- if (it == end(m_callbacks)) {
- return;
- }
-
- size_t idx = distance(begin(m_callbacks), it);
- if (m_callback_index != npos) {
- if (m_callback_index >= idx)
- --m_callback_index;
- }
- --m_callback_count;
-
- old = std::move(*it);
- m_callbacks.erase(it);
-
- m_have_callbacks = !m_callbacks.empty();
- }
-}
-
-void CollectionNotifier::suppress_next_notification(uint64_t token)
-{
- {
- std::lock_guard<std::mutex> lock(m_realm_mutex);
- REALM_ASSERT(m_realm);
- m_realm->verify_thread();
- m_realm->verify_in_write();
- }
-
- util::CheckedLockGuard lock(m_callback_mutex);
- auto it = find_callback(token);
- if (it != end(m_callbacks)) {
- it->skip_next = true;
- }
-}
-
-std::vector<CollectionNotifier::Callback>::iterator CollectionNotifier::find_callback(uint64_t token)
-{
- REALM_ASSERT(m_error || m_callbacks.size() > 0);
-
- auto it = find_if(begin(m_callbacks), end(m_callbacks),
- [=](const auto& c) { return c.token == token; });
- // We should only fail to find the callback if it was removed due to an error
- REALM_ASSERT(m_error || it != end(m_callbacks));
- return it;
-}
-
-void CollectionNotifier::unregister() noexcept
-{
- std::lock_guard<std::mutex> lock(m_realm_mutex);
- m_realm = nullptr;
-}
-
-bool CollectionNotifier::is_alive() const noexcept
-{
- std::lock_guard<std::mutex> lock(m_realm_mutex);
- return m_realm != nullptr;
-}
-
-std::unique_lock<std::mutex> CollectionNotifier::lock_target()
-{
- return std::unique_lock<std::mutex>{m_realm_mutex};
-}
-
-void CollectionNotifier::set_table(ConstTableRef table)
-{
- m_related_tables.clear();
- DeepChangeChecker::find_related_tables(m_related_tables, *table);
-}
-
-void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info)
-{
- if (!do_add_required_change_info(info) || m_related_tables.empty()) {
- return;
- }
-
- info.tables.reserve(m_related_tables.size());
- for (auto& tbl : m_related_tables)
- info.tables[tbl.table_key.value];
-}
-
-void CollectionNotifier::prepare_handover()
-{
- REALM_ASSERT(m_sg);
- m_sg_version = m_sg->get_version_of_current_transaction();
- do_prepare_handover(*m_sg);
- add_changes(std::move(m_change));
- REALM_ASSERT(m_change.empty());
- m_has_run = true;
-
-#ifdef REALM_DEBUG
- util::CheckedLockGuard lock(m_callback_mutex);
- for (auto& callback : m_callbacks)
- REALM_ASSERT(!callback.skip_next);
-#endif
-}
-
-void CollectionNotifier::before_advance()
-{
- for_each_callback([&](auto& lock, auto& callback) {
- if (callback.changes_to_deliver.empty()) {
- return;
- }
-
- auto changes = callback.changes_to_deliver;
- // acquire a local reference to the callback so that removing the
- // callback from within it can't result in a dangling pointer
- auto cb = callback.fn;
- lock.unlock_unchecked();
- cb.before(changes);
- });
-}
-
-void CollectionNotifier::after_advance()
-{
- for_each_callback([&](auto& lock, auto& callback) {
- if (callback.initial_delivered && callback.changes_to_deliver.empty()) {
- return;
- }
- callback.initial_delivered = true;
-
- auto changes = std::move(callback.changes_to_deliver);
- // acquire a local reference to the callback so that removing the
- // callback from within it can't result in a dangling pointer
- auto cb = callback.fn;
- lock.unlock_unchecked();
- cb.after(changes);
- });
-}
-
-void CollectionNotifier::deliver_error(std::exception_ptr error)
-{
- // Don't complain about double-unregistering callbacks
- m_error = true;
-
- m_callback_count = m_callbacks.size();
- for_each_callback([this, &error](auto& lock, auto& callback) {
- // acquire a local reference to the callback so that removing the
- // callback from within it can't result in a dangling pointer
- auto cb = std::move(callback.fn);
- auto token = callback.token;
- lock.unlock_unchecked();
- cb.error(error);
-
- // We never want to call the callback again after this, so just remove it
- this->remove_callback(token);
- });
-}
-
-bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept
-{
- std::lock_guard<std::mutex> lock(m_realm_mutex);
- return m_realm.get() == &realm;
-}
-
-bool CollectionNotifier::package_for_delivery()
-{
- if (!prepare_to_deliver())
- return false;
- util::CheckedLockGuard lock(m_callback_mutex);
- for (auto& callback : m_callbacks)
- callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize();
- m_callback_count = m_callbacks.size();
- return true;
-}
-
-template<typename Fn>
-void CollectionNotifier::for_each_callback(Fn&& fn)
-{
- util::CheckedUniqueLock callback_lock(m_callback_mutex);
- REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
- for (++m_callback_index; m_callback_index < m_callback_count; ++m_callback_index) {
- fn(callback_lock, m_callbacks[m_callback_index]);
- if (!callback_lock.owns_lock())
- callback_lock.lock_unchecked();
- }
-
- m_callback_index = npos;
-}
-
-void CollectionNotifier::attach_to(std::shared_ptr<Transaction> sg)
-{
- do_attach_to(*sg);
- m_sg = std::move(sg);
-}
-
-Transaction& CollectionNotifier::source_shared_group()
-{
- return Realm::Internal::get_transaction(*m_realm);
-}
-
-void CollectionNotifier::add_changes(CollectionChangeBuilder change)
-{
- util::CheckedLockGuard lock(m_callback_mutex);
- for (auto& callback : m_callbacks) {
- if (callback.skip_next) {
- REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
- callback.skip_next = false;
- }
- else {
- if (&callback == &m_callbacks.back())
- callback.accumulated_changes.merge(std::move(change));
- else
- callback.accumulated_changes.merge(CollectionChangeBuilder(change));
- }
- }
-}
-
-NotifierPackage::NotifierPackage(std::exception_ptr error,
- std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
- RealmCoordinator* coordinator)
-: m_notifiers(std::move(notifiers))
-, m_coordinator(coordinator)
-, m_error(std::move(error))
-{
-}
-
-// Clang TSE seems to not like returning a unique_lock from a function
-void NotifierPackage::package_and_wait(util::Optional<VersionID::version_type> target_version) NO_THREAD_SAFETY_ANALYSIS
-{
- if (!m_coordinator || m_error || !*this)
- return;
-
- auto lock = m_coordinator->wait_for_notifiers([&] {
- if (!target_version)
- return true;
- return std::all_of(begin(m_notifiers), end(m_notifiers), [&](auto const& n) {
- return !n->have_callbacks() || (n->has_run() && n->version().version >= *target_version);
- });
- });
-
- // Package the notifiers for delivery and remove any which don't have anything to deliver
- auto package = [&](auto& notifier) {
- if (notifier->has_run() && notifier->package_for_delivery()) {
- m_version = notifier->version();
- return false;
- }
- return true;
- };
- m_notifiers.erase(std::remove_if(begin(m_notifiers), end(m_notifiers), package), end(m_notifiers));
- if (m_version && target_version && m_version->version < *target_version) {
- m_notifiers.clear();
- m_version = util::none;
- }
- REALM_ASSERT(m_version || m_notifiers.empty());
-
- m_coordinator = nullptr;
-}
-
-void NotifierPackage::before_advance()
-{
- if (m_error)
- return;
- for (auto& notifier : m_notifiers)
- notifier->before_advance();
-}
-
-void NotifierPackage::after_advance()
-{
- if (m_error) {
- for (auto& notifier : m_notifiers)
- notifier->deliver_error(m_error);
- return;
- }
- for (auto& notifier : m_notifiers)
- notifier->after_advance();
-}
-
-void NotifierPackage::add_notifier(std::shared_ptr<CollectionNotifier> notifier)
-{
- m_notifiers.push_back(notifier);
- m_coordinator->register_notifier(notifier);
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp
deleted file mode 100644
index 6c9da6078..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp
+++ /dev/null
@@ -1,353 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_BACKGROUND_COLLECTION_HPP
-#define REALM_BACKGROUND_COLLECTION_HPP
-
-#include "object_changeset.hpp"
-#include "impl/collection_change_builder.hpp"
-#include "util/checked_mutex.hpp"
-
-#include <realm/util/assert.hpp>
-#include <realm/version_id.hpp>
-#include <realm/keys.hpp>
-#include <realm/table_ref.hpp>
-
-#include <array>
-#include <atomic>
-#include <exception>
-#include <functional>
-#include <mutex>
-#include <unordered_map>
-#include <unordered_set>
-
-namespace realm {
-class Realm;
-class Transaction;
-
-namespace _impl {
-class RealmCoordinator;
-
-struct ListChangeInfo {
- TableKey table_key;
- int64_t row_key;
- int64_t col_key;
- CollectionChangeBuilder* changes;
-};
-
-// FIXME: this should be in core
-using TableKeyType = decltype(TableKey::value);
-using ObjKeyType = decltype(ObjKey::value);
-
-struct TransactionChangeInfo {
- std::vector<ListChangeInfo> lists;
- std::unordered_map<TableKeyType, ObjectChangeSet> tables;
- bool track_all;
- bool schema_changed;
-};
-
-class DeepChangeChecker {
-public:
- struct OutgoingLink {
- int64_t col_key;
- bool is_list;
- };
- struct RelatedTable {
- TableKey table_key;
- std::vector<OutgoingLink> links;
- };
-
- DeepChangeChecker(TransactionChangeInfo const& info, Table const& root_table,
- std::vector<RelatedTable> const& related_tables);
-
- bool operator()(int64_t obj_key);
-
- // Recursively add `table` and all tables it links to to `out`, along with
- // information about the links from them
- static void find_related_tables(std::vector<RelatedTable>& out, Table const& table);
-
-private:
- TransactionChangeInfo const& m_info;
- Table const& m_root_table;
- const TableKey m_root_table_key;
- ObjectChangeSet const* const m_root_object_changes;
- std::unordered_map<TableKeyType, std::unordered_set<ObjKeyType>> m_not_modified;
- std::vector<RelatedTable> const& m_related_tables;
-
- struct Path {
- int64_t obj_key;
- int64_t col_key;
- bool depth_exceeded;
- };
- std::array<Path, 4> m_current_path;
-
- bool check_row(Table const& table, ObjKeyType obj_key, size_t depth = 0);
- bool check_outgoing_links(TableKey table_key, Table const& table,
- int64_t obj_key, size_t depth = 0);
-};
-
-// A base class for a notifier that keeps a collection up to date and/or
-// generates detailed change notifications on a background thread. This manages
-// most of the lifetime-management issues related to sharing an object between
-// the worker thread and the collection on the target thread, along with the
-// thread-safe callback collection.
-class CollectionNotifier {
-public:
- CollectionNotifier(std::shared_ptr<Realm>);
- virtual ~CollectionNotifier();
-
- // ------------------------------------------------------------------------
- // Public API for the collections using this to get notifications:
-
- // Stop receiving notifications from this background worker
- // This must be called in the destructor of the collection
- void unregister() noexcept;
-
- // Add a callback to be called each time the collection changes
- // This can only be called from the target collection's thread
- // Returns a token which can be passed to remove_callback()
- uint64_t add_callback(CollectionChangeCallback callback) REQUIRES(!m_callback_mutex);
- // Remove a previously added token. The token is no longer valid after
- // calling this function and must not be used again. This function can be
- // called from any thread.
- void remove_callback(uint64_t token) REQUIRES(!m_callback_mutex);
-
- void suppress_next_notification(uint64_t token) REQUIRES(!m_callback_mutex);
-
- // ------------------------------------------------------------------------
- // API for RealmCoordinator to manage running things and calling callbacks
-
- bool is_for_realm(Realm&) const noexcept;
- Realm* get_realm() const noexcept { return m_realm.get(); }
-
- // Get the Transaction version which this collection can attach to (if it's
- // in handover mode), or can deliver to (if it's been handed over to the BG worker alredad)
- // precondition: RealmCoordinator::m_notifier_mutex is locked
- VersionID version() const noexcept { return m_sg_version; }
-
- // Release references to all core types
- // This is called on the worker thread to ensure that non-thread-safe things
- // can be destroyed on the correct thread, even if the last reference to the
- // CollectionNotifier is released on a different thread
- virtual void release_data() noexcept;
-
- // Prepare to deliver the new collection and call callbacks.
- // Returns whether or not it has anything to deliver.
- // precondition: RealmCoordinator::m_notifier_mutex is locked
- bool package_for_delivery() REQUIRES(!m_callback_mutex);
-
- // Pass the given error to all registered callbacks, then remove them
- // precondition: RealmCoordinator::m_notifier_mutex is unlocked
- void deliver_error(std::exception_ptr) REQUIRES(!m_callback_mutex);
-
- // Call each of the given callbacks with the changesets prepared by package_for_delivery()
- // precondition: RealmCoordinator::m_notifier_mutex is unlocked
- void before_advance() REQUIRES(!m_callback_mutex);
- void after_advance() REQUIRES(!m_callback_mutex);
-
- bool is_alive() const noexcept;
-
- // precondition: RealmCoordinator::m_notifier_mutex is locked *or* is called on worker thread
- bool has_run() const noexcept { return m_has_run; }
-
- // Attach the handed-over query to `sg`. Must not be already attached to a Transaction.
- // precondition: RealmCoordinator::m_notifier_mutex is locked
- void attach_to(std::shared_ptr<Transaction> sg);
-
- // Set `info` as the new ChangeInfo that will be populated by the next
- // transaction advance, and register all required information in it
- // precondition: RealmCoordinator::m_notifier_mutex is locked
- void add_required_change_info(TransactionChangeInfo& info);
-
- // precondition: RealmCoordinator::m_notifier_mutex is unlocked
- virtual void run() = 0;
-
- // precondition: RealmCoordinator::m_notifier_mutex is locked
- void prepare_handover() REQUIRES(!m_callback_mutex);
-
- template <typename T>
- class Handle;
-
- bool have_callbacks() const noexcept { return m_have_callbacks; }
-protected:
- void add_changes(CollectionChangeBuilder change) REQUIRES(!m_callback_mutex);
- void set_table(ConstTableRef table);
- std::unique_lock<std::mutex> lock_target();
- Transaction& source_shared_group();
-
- bool all_related_tables_covered(const TableVersions& versions);
- std::function<bool (ObjectChangeSet::ObjectKeyType)> get_modification_checker(TransactionChangeInfo const&, ConstTableRef);
-
- // The actual change, calculated in run() and delivered in prepare_handover()
- CollectionChangeBuilder m_change;
-
-private:
- virtual void do_attach_to(Transaction&) { }
- virtual void do_prepare_handover(Transaction&) { }
- virtual bool do_add_required_change_info(TransactionChangeInfo&) = 0;
- virtual bool prepare_to_deliver() { return true; }
-
- mutable std::mutex m_realm_mutex;
- std::shared_ptr<Realm> m_realm;
-
- VersionID m_sg_version;
- std::shared_ptr<Transaction> m_sg;
-
- bool m_has_run = false;
- bool m_error = false;
- std::vector<DeepChangeChecker::RelatedTable> m_related_tables;
-
- struct Callback {
- CollectionChangeCallback fn;
- CollectionChangeBuilder accumulated_changes;
- CollectionChangeSet changes_to_deliver;
- uint64_t token;
- bool initial_delivered;
- bool skip_next;
- };
-
- // Currently registered callbacks and a mutex which must always be held
- // while doing anything with them or m_callback_index
- util::CheckedMutex m_callback_mutex;
- std::vector<Callback> m_callbacks;
-
- // Cached value for if m_callbacks is empty, needed to avoid deadlocks in
- // run() due to lock-order inversion between m_callback_mutex and m_target_mutex
- // It's okay if this value is stale as at worst it'll result in us doing
- // some extra work.
- std::atomic<bool> m_have_callbacks = {false};
-
- // Iteration variable for looping over callbacks
- // remove_callback() updates this when needed
- size_t m_callback_index = -1;
- // The number of callbacks which were present when the notifier was packaged
- // for delivery which are still present.
- // Updated by packaged_for_delivery and removd_callback(), and used in
- // for_each_callback() to avoid calling callbacks registered during delivery.
- size_t m_callback_count = -1;
-
- uint64_t m_next_token = 0;
-
- template<typename Fn>
- void for_each_callback(Fn&& fn) REQUIRES(!m_callback_mutex);
-
- std::vector<Callback>::iterator find_callback(uint64_t token);
-};
-
-// 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>::operator=(std::move(other));
- return *this;
- }
-
- template<typename U>
- Handle& operator=(std::shared_ptr<U>&& other)
- {
- reset();
- std::shared_ptr<T>::operator=(std::move(other));
- return *this;
- }
-
- void reset()
- {
- if (*this) {
- this->get()->unregister();
- std::shared_ptr<T>::reset();
- }
- }
-};
-
-// A package of CollectionNotifiers for a single Realm instance which is passed
-// around to the various places which need to actually trigger the notifications
-class NotifierPackage {
-public:
- NotifierPackage() = default;
- NotifierPackage(std::exception_ptr error,
- std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
- RealmCoordinator* coordinator);
-
- explicit operator bool() { return !m_notifiers.empty(); }
-
- // Get the version which this package can deliver into, or VersionID{} if
- // it has not yet been packaged
- util::Optional<VersionID> version() { return m_version; }
-
- // Package the notifiers for delivery, blocking if they aren't ready for
- // the given version.
- // No-op if called multiple times
- void package_and_wait(util::Optional<VersionID::version_type> target_version);
-
- // Send the before-change notifications
- void before_advance();
- // Deliver the payload associated with the contained notifiers and/or the error
- void deliver(Transaction& sg);
- // Send the after-change notifications
- void after_advance();
-
- void add_notifier(std::shared_ptr<CollectionNotifier> notifier);
-
-private:
- util::Optional<VersionID> m_version;
- std::vector<std::shared_ptr<CollectionNotifier>> m_notifiers;
-
- RealmCoordinator* m_coordinator = nullptr;
- std::exception_ptr m_error;
-};
-
-// Find which column of the row in the table contains the given container.
-//
-// LinkViews and Subtables know what row of their parent they're in, but not
-// what column, so we have to just check each one.
-template<typename Table, typename T, typename U>
-size_t find_container_column(Table& table, size_t row_ndx, T const& expected, int type, U (Table::*getter)(size_t, size_t))
-{
- for (size_t i = 0, count = table.get_column_count(); i != count; ++i) {
- if (table.get_column_type(i) == type && (table.*getter)(i, row_ndx) == expected) {
- return i;
- }
- }
- REALM_UNREACHABLE();
-}
-
-
-} // namespace _impl
-} // namespace realm
-
-#endif /* REALM_BACKGROUND_COLLECTION_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp
deleted file mode 100644
index c900a78b1..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "impl/external_commit_helper.hpp"
-#include "impl/realm_coordinator.hpp"
-#include <realm/util/fifo_helper.hpp>
-
-#include <realm/util/assert.hpp>
-#include <realm/db.hpp>
-
-#include <algorithm>
-#include <errno.h>
-#include <fcntl.h>
-#include <sstream>
-#include <stdlib.h>
-#include <sys/epoll.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#ifdef __ANDROID__
-#include <android/log.h>
-#define ANDROID_LOG __android_log_print
-#else
-#define ANDROID_LOG(...)
-#endif
-
-using namespace realm;
-using namespace realm::_impl;
-
-#define LOGE(...) do { \
- fprintf(stderr, __VA_ARGS__); \
- ANDROID_LOG(ANDROID_LOG_ERROR, "REALM", __VA_ARGS__); \
-} while (0)
-
-namespace {
-// Write a byte to a pipe to notify anyone waiting for data on the pipe
-void notify_fd(int fd)
-{
- while (true) {
- char c = 0;
- ssize_t ret = write(fd, &c, 1);
- if (ret == 1) {
- break;
- }
-
- // If the pipe's buffer is full, we need to read some of the old data in
- // it to make space. We don't just read in the code waiting for
- // notifications so that we can notify multiple waiters with a single
- // write.
- if (ret != 0) {
- int err = errno;
- if (err != EAGAIN) {
- throw std::system_error(err, std::system_category());
- }
- }
- std::vector<uint8_t> buff(1024);
- read(fd, buff.data(), buff.size());
- }
-}
-} // anonymous namespace
-
-class ExternalCommitHelper::DaemonThread {
-public:
- DaemonThread();
- ~DaemonThread();
-
- void add_commit_helper(ExternalCommitHelper* helper);
- // Return true if the m_helper_list is empty after removal.
- void remove_commit_helper(ExternalCommitHelper* helper);
-
- static DaemonThread& shared();
-private:
- void listen();
-
- // To protect the accessing m_helpers on the daemon thread.
- std::mutex m_mutex;
- std::vector<ExternalCommitHelper*> m_helpers;
- // The listener thread
- std::thread m_thread;
- // File descriptor for epoll
- FdHolder m_epoll_fd;
- // The two ends of an anonymous pipe used to notify the kqueue() thread that it should be shut down.
- FdHolder m_shutdown_read_fd;
- FdHolder m_shutdown_write_fd;
- // Daemon thread id. For checking unexpected dead locks.
- std::thread::id m_thread_id;
-};
-
-
-void ExternalCommitHelper::FdHolder::close()
-{
- if (m_fd != -1) {
- ::close(m_fd);
- }
- m_fd = -1;
-}
-
-ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
-: m_parent(parent)
-{
- std::string path;
- std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
- std::string sys_temp_dir = util::normalize_dir(DBOptions::get_sys_tmp_dir());
-
- // Object Store needs to create a named pipe in order to coordinate notifications.
- // This can be a problem on some file systems (e.g. FAT32) or due to security policies in SELinux. Most commonly
- // it is a problem when saving Realms on external storage: https://stackoverflow.com/questions/2740321/how-to-create-named-pipe-mkfifo-in-android
- //
- // For this reason we attempt to create this file in a temporary location known to be safe to write these files.
- //
- // In order of priority we attempt to write the file in the following locations:
- // 1) Next to the Realm file itself
- // 2) A location defined by `Realm::Config::fifo_files_fallback_path`
- // 3) A location defined by `DBOptions::set_sys_tmp_dir()`
- //
- // Core has a similar policy for its named pipes.
- //
- // Also see https://github.com/realm/realm-java/issues/3140
- // Note that hash collisions are okay here because they just result in doing extra work instead of resulting
- // in correctness problems.
-
- path = parent.get_path() + ".note";
- bool fifo_created = util::try_create_fifo(path);
- if (!fifo_created && !temp_dir.empty()) {
- path = util::format("%1realm_%2.note", temp_dir, std::hash<std::string>()(parent.get_path()));
- fifo_created = util::try_create_fifo(path);
- }
- if (!fifo_created && !sys_temp_dir.empty()) {
- path = util::format("%1realm_%2.note", sys_temp_dir, std::hash<std::string>()(parent.get_path()));
- util::create_fifo(path);
- }
-
- m_notify_fd = open(path.c_str(), O_RDWR);
- if (m_notify_fd == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- // Make writing to the pipe return -1 when the pipe's buffer is full
- // rather than blocking until there's space available
- int ret = fcntl(m_notify_fd, F_SETFL, O_NONBLOCK);
- if (ret == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- // Lock is inside add_commit_helper.
- DaemonThread::shared().add_commit_helper(this);
-}
-
-ExternalCommitHelper::~ExternalCommitHelper()
-{
- DaemonThread::shared().remove_commit_helper(this);
-}
-
-ExternalCommitHelper::DaemonThread::DaemonThread()
-{
- m_epoll_fd = epoll_create(1);
- if (m_epoll_fd == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- // Create the anonymous pipe
- int pipe_fd[2];
- int ret = pipe(pipe_fd);
- if (ret == -1) {
- throw std::system_error(errno, std::system_category());
- }
-
- m_shutdown_read_fd = pipe_fd[0];
- m_shutdown_write_fd = pipe_fd[1];
-
- epoll_event event{};
- event.events = EPOLLIN;
- event.data.fd = m_shutdown_read_fd;
- ret = epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, m_shutdown_read_fd, &event);
- if (ret != 0) {
- int err = errno;
- throw std::system_error(err, std::system_category());
- }
-
- m_thread = std::thread([=] {
- try {
- listen();
- }
- catch (std::exception const& e) {
- LOGE("uncaught exception in notifier thread: %s: %s\n", typeid(e).name(), e.what());
- throw;
- }
- catch (...) {
- LOGE("uncaught exception in notifier thread\n");
- throw;
- }
- });
- m_thread_id = m_thread.get_id();
-}
-
-ExternalCommitHelper::DaemonThread::~DaemonThread()
-{
- notify_fd(m_shutdown_write_fd);
- m_thread.join(); // Wait for the thread to exit
-}
-
-ExternalCommitHelper::DaemonThread& ExternalCommitHelper::DaemonThread::shared()
-{
- static DaemonThread daemon_thread;
- return daemon_thread;
-}
-
-void ExternalCommitHelper::DaemonThread::add_commit_helper(ExternalCommitHelper* helper)
-{
- // Called in the deamon thread loop, dead lock will happen.
- REALM_ASSERT(std::this_thread::get_id() != m_thread_id);
-
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_helpers.push_back(helper);
-
- epoll_event event{};
- event.events = EPOLLIN | EPOLLET;
- event.data.fd = helper->m_notify_fd;
- int ret = epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, helper->m_notify_fd, &event);
- if (ret != 0) {
- int err = errno;
- throw std::system_error(err, std::system_category());
- }
-}
-
-void ExternalCommitHelper::DaemonThread::remove_commit_helper(ExternalCommitHelper* helper)
-{
- // Called in the deamon thread loop, dead lock will happen.
- REALM_ASSERT(std::this_thread::get_id() != m_thread_id);
-
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_helpers.erase(std::remove(m_helpers.begin(), m_helpers.end(), helper), m_helpers.end());
-
- // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event, even
- // though this argument is ignored. See man page of epoll_ctl.
- epoll_event event{};
- epoll_ctl(m_epoll_fd, EPOLL_CTL_DEL, helper->m_notify_fd, &event);
-}
-
-void ExternalCommitHelper::DaemonThread::listen()
-{
- pthread_setname_np(pthread_self(), "Realm notification listener");
-
- int ret;
-
- while (true) {
- epoll_event ev{};
- ret = epoll_wait(m_epoll_fd, &ev, 1, -1);
-
- if (ret == -1 && errno == EINTR) {
- // Interrupted system call, try again.
- continue;
- }
-
- if (ret == -1) {
- int err = errno;
- throw std::system_error(err, std::system_category());
- }
- if (ret == 0) {
- // Spurious wakeup; just wait again
- continue;
- }
-
- if (ev.data.u32 == (uint32_t)m_shutdown_read_fd) {
- return;
- }
-
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- for (auto helper : m_helpers) {
- if (ev.data.u32 == (uint32_t)helper->m_notify_fd) {
- helper->m_parent.on_change();
- }
- }
- }
- }
-}
-
-void ExternalCommitHelper::notify_others()
-{
- notify_fd(m_notify_fd);
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.hpp
deleted file mode 100644
index 7f9991373..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.hpp
+++ /dev/null
@@ -1,70 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 <memory>
-#include <mutex>
-#include <thread>
-#include <vector>
-
-namespace realm {
-
-namespace _impl {
-class RealmCoordinator;
-
-class ExternalCommitHelper {
-public:
- ExternalCommitHelper(RealmCoordinator& parent);
- ~ExternalCommitHelper();
-
- void notify_others();
-
-private:
- // A RAII holder for a file descriptor which automatically closes the wrapped
- // fd when it's deallocated
- class FdHolder {
- public:
- FdHolder() = default;
- ~FdHolder() { close(); }
- operator int() const { return m_fd; }
-
- FdHolder& operator=(int new_fd) {
- close();
- m_fd = new_fd;
- return *this;
- }
-
- private:
- int m_fd = -1;
- void close();
-
- FdHolder& operator=(FdHolder const&) = delete;
- FdHolder(FdHolder const&) = delete;
- };
-
- class DaemonThread;
-
- RealmCoordinator& m_parent;
-
- // Read-write file descriptor for the named pipe which is waited on for
- // changes and written to when a commit is made
- FdHolder m_notify_fd;
-};
-
-} // namespace _impl
-} // namespace realm
-
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/external_commit_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/external_commit_helper.hpp
deleted file mode 100644
index 8c5f3a1bd..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/external_commit_helper.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_EXTERNAL_COMMIT_HELPER_HPP
-#define REALM_EXTERNAL_COMMIT_HELPER_HPP
-
-#include <realm/util/features.h>
-
-#if (defined(REALM_HAVE_EPOLL) && REALM_HAVE_EPOLL) || REALM_ANDROID || (defined(REALM_PLATFORM_NODE) && REALM_PLATFORM_NODE && !REALM_PLATFORM_APPLE && !defined(_WIN32))
-#define REALM_USE_EPOLL 1
-#else
-#define REALM_USE_EPOLL 0
-#endif
-
-#if REALM_PLATFORM_APPLE
-#include "impl/apple/external_commit_helper.hpp"
-#elif REALM_USE_EPOLL
-#include "impl/epoll/external_commit_helper.hpp"
-#elif defined(_WIN32)
-#include "impl/windows/external_commit_helper.hpp"
-#else
-#include "impl/generic/external_commit_helper.hpp"
-#endif
-
-#endif // REALM_EXTERNAL_COMMIT_HELPER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp
deleted file mode 100644
index dfebc6aed..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/external_commit_helper.hpp"
-
-#include "impl/realm_coordinator.hpp"
-
-#include <realm/history.hpp>
-#include <realm/replication.hpp>
-
-using namespace realm;
-using namespace realm::_impl;
-
-ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
-: m_parent(parent)
-, m_history(realm::make_in_realm_history(parent.get_path()))
-, m_sg(*m_history, TransactionOptions(parent.is_in_memory() ? TransactionOptions::Durability::MemOnly
- : TransactionOptions::Durability::Full,
- parent.get_encryption_key().data()))
-, m_thread(std::async(std::launch::async, [=] {
- m_sg.begin_read();
- while (m_sg.wait_for_change()) {
- m_sg.end_read();
- m_sg.begin_read();
- m_parent.on_change();
- }
-}))
-{
-}
-
-ExternalCommitHelper::~ExternalCommitHelper()
-{
- m_sg.wait_for_change_release();
- m_thread.wait(); // Wait for the thread to exit
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp
deleted file mode 100644
index f88e2e060..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include <realm/group_shared.hpp>
-
-#include <future>
-
-namespace realm {
-class Replication;
-
-namespace _impl {
-class RealmCoordinator;
-
-class ExternalCommitHelper {
-public:
- ExternalCommitHelper(RealmCoordinator& parent);
- ~ExternalCommitHelper();
-
- // A no-op in this version, but needed for the Apple version
- void notify_others() { }
-
-private:
- RealmCoordinator& m_parent;
-
- // A shared group used to listen for changes
- std::unique_ptr<Replication> m_history;
- Transaction m_sg;
-
- // The listener thread
- std::future<void> m_thread;
-};
-
-} // namespace _impl
-} // namespace realm
-
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp
deleted file mode 100644
index ef55e7ffd..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "impl/list_notifier.hpp"
-
-#include "list.hpp"
-
-#include <realm/db.hpp>
-#include <realm/group.hpp>
-
-using namespace realm;
-using namespace realm::_impl;
-
-ListNotifier::ListNotifier(std::shared_ptr<Realm> realm, LstBase const& list,
- PropertyType type)
-: CollectionNotifier(std::move(realm))
-, m_type(type)
-, m_table(list.get_table()->get_key())
-, m_col(list.get_col_key())
-, m_obj(list.get_key())
-, m_prev_size(list.size())
-{
- if (m_type == PropertyType::Object) {
- set_table(static_cast<const LnkLst&>(list).get_target_table());
- }
-}
-
-void ListNotifier::release_data() noexcept
-{
- m_list = {};
- CollectionNotifier::release_data();
-}
-
-void ListNotifier::do_attach_to(Transaction& sg)
-{
- try {
- auto obj = sg.get_table(m_table)->get_object(m_obj);
- m_list = obj.get_listbase_ptr(m_col);
- }
- catch (const InvalidKey&) {
- }
-}
-
-bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info)
-{
- if (!m_list->is_attached())
- return false; // origin row was deleted after the notification was added
-
- info.lists.push_back({m_table, m_obj.value, m_col.value, &m_change});
-
- m_info = &info;
- return true;
-}
-
-void ListNotifier::run()
-{
- if (!m_list->is_attached()) {
- // List was deleted, so report all of the rows being removed if this is
- // the first run after that
- if (m_prev_size) {
- m_change.deletions.set(m_prev_size);
- m_prev_size = 0;
- }
- else {
- m_change = {};
- }
- return;
- }
-
- m_prev_size = m_list->size();
-
- if (m_type == PropertyType::Object) {
- auto& list = static_cast<LnkLst&>(*m_list);
- auto object_did_change = get_modification_checker(*m_info, list.get_target_table());
- for (size_t i = 0; i < list.size(); ++i) {
- if (m_change.modifications.contains(i))
- continue;
- if (object_did_change(list.get(i).value))
- m_change.modifications.add(i);
- }
-
- for (auto const& move : m_change.moves) {
- if (m_change.modifications.contains(move.to))
- continue;
- if (object_did_change(list.get(move.to).value))
- m_change.modifications.add(move.to);
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp
deleted file mode 100644
index abd4120e0..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_LIST_NOTIFIER_HPP
-#define REALM_LIST_NOTIFIER_HPP
-
-#include "impl/collection_notifier.hpp"
-
-#include "property.hpp"
-
-#include <realm/list.hpp>
-
-namespace realm {
-namespace _impl {
-class ListNotifier : public CollectionNotifier {
-public:
- ListNotifier(std::shared_ptr<Realm> realm, LstBase const& list, PropertyType type);
-
-private:
- PropertyType m_type;
- std::unique_ptr<LstBase> m_list;
-
- TableKey m_table;
- ColKey m_col;
- ObjKey m_obj;
-
- // The last-seen size of the LinkView so that we can report row deletions
- // when the LinkView itself is deleted
- size_t m_prev_size;
-
- TransactionChangeInfo* m_info;
-
- void run() override;
-
- void do_attach_to(Transaction& sg) override;
-
- void release_data() noexcept override;
- bool do_add_required_change_info(TransactionChangeInfo& info) override;
-};
-}
-}
-
-#endif // REALM_LIST_NOTIFIER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/notification_wrapper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/notification_wrapper.hpp
deleted file mode 100644
index f2e5a3b8f..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/notification_wrapper.hpp
+++ /dev/null
@@ -1,51 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_NOTIFICATION_WRAPPER_HPP
-#define REALM_OS_NOTIFICATION_WRAPPER_HPP
-
-#include "collection_notifications.hpp"
-
-namespace realm {
-namespace _impl {
-
-// A wrapper that stores a value and an associated notification token.
-// The point of this type is to keep the notification token alive
-// until the value can be properly processed or handled.
-template<typename T>
-struct NotificationWrapper : public T {
- using T::T;
-
- NotificationWrapper(T&& object)
- : T(object)
- { }
-
- template <typename F>
- void add_notification_callback(F&& callback)
- {
- m_token = T::add_notification_callback(std::forward<F>(callback));
- }
-
-private:
- NotificationToken m_token;
-};
-
-} // namespace _impl
-} // namespace realm
-
-#endif // REALM_OS_NOTIFICATION_WRAPPER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp
deleted file mode 100644
index 7cc309e1c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp
+++ /dev/null
@@ -1,224 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_OBJECT_ACCESSOR_IMPL_HPP
-#define REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
-
-#include "object_accessor.hpp"
-
-#include <realm/util/any.hpp>
-
-namespace realm {
-using AnyDict = std::map<std::string, util::Any>;
-using AnyVector = std::vector<util::Any>;
-
-// An object accessor context which can be used to create and access objects
-// using util::Any as the type-erased value type. In addition, this serves as
-// the reference implementation of an accessor context that must be implemented
-// by each binding.
-class CppContext {
-public:
- // This constructor is the only one used by the object accessor code, and is
- // used when recurring into a link or array property during object creation
- // (i.e. prop.type will always be Object or Array).
- CppContext(CppContext& c, Property const& prop)
- : realm(c.realm)
- , object_schema(prop.type == PropertyType::Object ? &*realm->schema().find(prop.object_type) : c.object_schema)
- { }
-
- CppContext() = default;
- CppContext(std::shared_ptr<Realm> realm, const ObjectSchema* os=nullptr)
- : realm(std::move(realm)), object_schema(os) { }
-
- // The use of util::Optional for the following two functions is not a hard
- // requirement; only that it be some type which can be evaluated in a
- // boolean context to determine if it contains a value, and if it does
- // contain a value it must be dereferencable to obtain that value.
-
- // Get the value for a property in an input object, or `util::none` if no
- // value present. The property is identified both by the name of the
- // property and its index within the ObjectScehma's persisted_properties
- // array.
- util::Optional<util::Any> value_for_property(util::Any& dict,
- const Property& prop,
- size_t /* property_index */) const
- {
- auto const& v = any_cast<AnyDict&>(dict);
- auto it = v.find(prop.name);
- return it == v.end() ? util::none : util::make_optional(it->second);
- }
-
- // Get the default value for the given property in the given object schema,
- // or `util::none` if there is none (which is distinct from the default
- // being `null`).
- //
- // This implementation does not support default values; see the default
- // value tests for an example of one which does.
- util::Optional<util::Any>
- default_value_for_property(ObjectSchema const&, Property const&) const
- {
- return util::none;
- }
-
- // Invoke `fn` with each of the values from an enumerable type
- template<typename Func>
- void enumerate_list(util::Any& value, Func&& fn) {
- for (auto&& v : any_cast<AnyVector&>(value))
- fn(v);
- }
-
- // Determine if `value` boxes the same List as `list`
- bool is_same_list(List const& list, util::Any const& value)
- {
- if (auto list2 = any_cast<List>(&value))
- return list == *list2;
- return false;
- }
-
- // Convert from core types to the boxed type
- util::Any box(BinaryData v) const { return std::string(v); }
- util::Any box(List v) const { return v; }
- util::Any box(Object v) const { return v; }
- util::Any box(Results v) const { return v; }
- util::Any box(StringData v) const { return std::string(v); }
- util::Any box(Timestamp v) const { return v; }
- util::Any box(bool v) const { return v; }
- util::Any box(double v) const { return v; }
- util::Any box(float v) const { return v; }
- util::Any box(int64_t v) const { return v; }
- util::Any box(util::Optional<bool> v) const { return v; }
- util::Any box(util::Optional<double> v) const { return v; }
- util::Any box(util::Optional<float> v) const { return v; }
- util::Any box(util::Optional<int64_t> v) const { return v; }
- util::Any box(Obj) const;
-
- // Any properties are only supported by the Cocoa binding to enable reading
- // old Realm files that may have used them. Other bindings can safely not
- // implement this.
- util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
-
- // Convert from the boxed type to core types. This needs to be implemented
- // for all of the types which `box()` can take, plus `Obj` and optional
- // versions of the numeric types, minus `List` and `Results`.
- //
- // `create` and `update` are only applicable to `unbox<Obj>`. If
- // `create` is false then when given something which is not a managed Realm
- // object `unbox()` should simply return a detached obj, while if it's
- // true then `unbox()` should create a new object in the context's Realm
- // using the provided value. If `update` is true then upsert semantics
- // should be used for this.
- // If `update_only_diff` is true, only properties that are different from
- // already existing properties should be updated. If `create` and `update_only_diff`
- // is true, `current_row` may hold a reference to the object that should
- // be compared against.
- template<typename T>
- T unbox(util::Any& v, CreatePolicy = CreatePolicy::Skip, ObjKey /*current_row*/ = ObjKey()) const { return any_cast<T>(v); }
-
- bool is_null(util::Any const& v) const noexcept { return !v.has_value(); }
- util::Any null_value() const noexcept { return {}; }
- util::Optional<util::Any> no_value() const noexcept { return {}; }
-
- // KVO hooks which will be called before and after modying a property from
- // within Object::create().
- void will_change(Object const&, Property const&) {}
- void did_change() {}
-
- // Get a string representation of the given value for use in error messages.
- std::string print(util::Any const&) const { return "not implemented"; }
-
- // Cocoa allows supplying fewer values than there are properties when
- // creating objects using an array of values. Other bindings should not
- // mimick this behavior so just return false here.
- bool allow_missing(util::Any const&) const { return false; }
-
-private:
- std::shared_ptr<Realm> realm;
- const ObjectSchema* object_schema = nullptr;
-
-};
-
-inline util::Any CppContext::box(Obj obj) const
-{
- REALM_ASSERT(object_schema);
- return Object(realm, *object_schema, obj);
-}
-
-template<>
-inline StringData CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- if (!v.has_value())
- return StringData();
- auto& value = any_cast<std::string&>(v);
- return StringData(value.c_str(), value.size());
-}
-
-template<>
-inline BinaryData CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- if (!v.has_value())
- return BinaryData();
- auto& value = any_cast<std::string&>(v);
- return BinaryData(value.c_str(), value.size());
-}
-
-template<>
-inline Obj CppContext::unbox(util::Any& v, CreatePolicy policy, ObjKey current_obj) const
-{
- if (auto object = any_cast<Object>(&v))
- return object->obj();
- if (auto obj = any_cast<Obj>(&v))
- return *obj;
- if (policy == CreatePolicy::Skip)
- return Obj();
-
- REALM_ASSERT(object_schema);
- return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, policy, current_obj).obj();
-}
-
-template<>
-inline util::Optional<bool> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
-}
-
-template<>
-inline util::Optional<int64_t> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
-}
-
-template<>
-inline util::Optional<double> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
-}
-
-template<>
-inline util::Optional<float> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
-{
- return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
-}
-
-template<>
-inline Mixed CppContext::unbox(util::Any&, CreatePolicy, ObjKey) const
-{
- throw std::logic_error("'Any' type is unsupported");
-}
-}
-
-#endif // REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp
deleted file mode 100644
index f266199d9..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "impl/object_notifier.hpp"
-
-#include "shared_realm.hpp"
-
-using namespace realm;
-using namespace realm::_impl;
-
-ObjectNotifier::ObjectNotifier(std::shared_ptr<Realm> realm, TableKey table, ObjKey obj)
-: CollectionNotifier(std::move(realm))
-, m_table(table)
-, m_obj(obj)
-{
-}
-
-bool ObjectNotifier::do_add_required_change_info(TransactionChangeInfo& info)
-{
- m_info = &info;
- info.tables[m_table.value];
- return false;
-}
-
-void ObjectNotifier::run()
-{
- if (!m_table)
- return;
-
- auto it = m_info->tables.find(m_table.value);
- if (it == m_info->tables.end())
- return;
- auto& change = it->second;
-
- if (change.deletions_contains(m_obj.value)) {
- m_change.deletions.add(0);
- m_table = {};
- m_obj = {};
- return;
- }
-
- auto column_modifications = change.get_columns_modified(m_obj.value);
- if (!column_modifications)
- return;
- m_change.modifications.add(0);
- for (auto col : *column_modifications) {
- m_change.columns[col].add(0);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp
deleted file mode 100644
index 4f9411a60..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_OBJECT_NOTIFIER_HPP
-#define REALM_OS_OBJECT_NOTIFIER_HPP
-
-#include "impl/collection_notifier.hpp"
-
-#include <realm/keys.hpp>
-
-namespace realm {
-
-namespace _impl {
-class ObjectNotifier : public CollectionNotifier {
-public:
- ObjectNotifier(std::shared_ptr<Realm> realm, TableKey table, ObjKey obj);
-
-private:
- TableKey m_table;
- ObjKey m_obj;
- TransactionChangeInfo* m_info;
-
- void run() override;
-
- bool do_add_required_change_info(TransactionChangeInfo& info) override;
-};
-}
-}
-
-#endif // REALM_OS_OBJECT_NOTIFIER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp
deleted file mode 100644
index c9ee74b4d..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp
+++ /dev/null
@@ -1,1162 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/realm_coordinator.hpp"
-
-#include "impl/collection_notifier.hpp"
-#include "impl/external_commit_helper.hpp"
-#include "impl/transact_log_handler.hpp"
-#include "impl/weak_realm_notifier.hpp"
-#include "binding_context.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-#include "thread_safe_reference.hpp"
-#include "util/scheduler.hpp"
-
-#if REALM_ENABLE_SYNC
-#include "sync/impl/work_queue.hpp"
-#include "sync/impl/sync_file.hpp"
-#include "sync/async_open_task.hpp"
-#include "sync/partial_sync.hpp"
-#include "sync/sync_config.hpp"
-#include "sync/sync_manager.hpp"
-#include "sync/sync_session.hpp"
-#endif
-
-#include <realm/db.hpp>
-#include <realm/history.hpp>
-#include <realm/string_data.hpp>
-#include <realm/util/fifo_helper.hpp>
-
-#include <algorithm>
-#include <unordered_map>
-
-using namespace realm;
-using namespace realm::_impl;
-
-static auto& s_coordinator_mutex = *new std::mutex;
-static auto& s_coordinators_per_path = *new std::unordered_map<std::string, std::weak_ptr<RealmCoordinator>>;
-
-std::shared_ptr<RealmCoordinator> RealmCoordinator::get_coordinator(StringData path)
-{
- std::lock_guard<std::mutex> lock(s_coordinator_mutex);
-
- auto& weak_coordinator = s_coordinators_per_path[path];
- if (auto coordinator = weak_coordinator.lock()) {
- return coordinator;
- }
-
- auto coordinator = std::make_shared<RealmCoordinator>();
- weak_coordinator = coordinator;
- return coordinator;
-}
-
-std::shared_ptr<RealmCoordinator> RealmCoordinator::get_coordinator(const Realm::Config& config) NO_THREAD_SAFETY_ANALYSIS
-{
- auto coordinator = get_coordinator(config.path);
- util::CheckedLockGuard lock(coordinator->m_realm_mutex);
- coordinator->set_config(config);
- return coordinator;
-}
-
-std::shared_ptr<RealmCoordinator> RealmCoordinator::get_existing_coordinator(StringData path)
-{
- std::lock_guard<std::mutex> lock(s_coordinator_mutex);
-
- auto& weak_coordinator = s_coordinators_per_path[path];
- if (auto coordinator = weak_coordinator.lock()) {
- return coordinator;
- }
-
- return {};
-}
-
-void RealmCoordinator::create_sync_session(bool force_client_resync)
-{
-#if REALM_ENABLE_SYNC
- if (m_sync_session)
- return;
-
- if (!m_config.encryption_key.empty() && !m_config.sync_config->realm_encryption_key) {
- throw std::logic_error("A realm encryption key was specified in Realm::Config but not in SyncConfig");
- } else if (m_config.sync_config->realm_encryption_key && m_config.encryption_key.empty()) {
- throw std::logic_error("A realm encryption key was specified in SyncConfig but not in Realm::Config");
- } else if (m_config.sync_config->realm_encryption_key &&
- !std::equal(m_config.sync_config->realm_encryption_key->begin(), m_config.sync_config->realm_encryption_key->end(),
- m_config.encryption_key.begin(), m_config.encryption_key.end())) {
- throw std::logic_error("The realm encryption key specified in SyncConfig does not match the one in Realm::Config");
- }
-
- m_sync_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config, force_client_resync);
-
- std::weak_ptr<RealmCoordinator> weak_self = shared_from_this();
- SyncSession::Internal::set_sync_transact_callback(*m_sync_session,
- [weak_self](VersionID old_version, VersionID new_version) {
- if (auto self = weak_self.lock()) {
- util::CheckedUniqueLock lock(self->m_transaction_callback_mutex);
- if (auto transaction_callback = self->m_transaction_callback) {
- lock.unlock();
- transaction_callback(old_version, new_version);
- }
- if (self->m_notifier)
- self->m_notifier->notify_others();
- }
- });
-#else
- static_cast<void>(force_client_resync);
-#endif
-}
-
-void RealmCoordinator::set_config(const Realm::Config& config)
-{
- if (config.encryption_key.data() && config.encryption_key.size() != 64)
- throw InvalidEncryptionKeyException();
- if (config.schema_mode == SchemaMode::Immutable && config.sync_config)
- throw std::logic_error("Synchronized Realms cannot be opened in immutable mode");
- if (config.schema_mode == SchemaMode::Additive && config.migration_function)
- throw std::logic_error("Realms opened in Additive-only schema mode do not use a migration function");
- if (config.schema_mode == SchemaMode::Immutable && config.migration_function)
- throw std::logic_error("Realms opened in immutable mode do not use a migration function");
- if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.migration_function)
- throw std::logic_error("Realms opened in read-only mode do not use a migration function");
- if (config.schema_mode == SchemaMode::Immutable && config.initialization_function)
- throw std::logic_error("Realms opened in immutable mode do not use an initialization function");
- if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.initialization_function)
- throw std::logic_error("Realms opened in read-only mode do not use an initialization function");
- if (config.schema && config.schema_version == ObjectStore::NotVersioned)
- throw std::logic_error("A schema version must be specified when the schema is specified");
- if (!config.realm_data.is_null() && (!config.immutable() || !config.in_memory))
- throw std::logic_error("In-memory realms initialized from memory buffers can only be opened in read-only mode");
- if (!config.realm_data.is_null() && !config.path.empty())
- throw std::logic_error("Specifying both memory buffer and path is invalid");
- if (!config.realm_data.is_null() && !config.encryption_key.empty())
- throw std::logic_error("Memory buffers do not support encryption");
- // ResetFile also won't use the migration function, but specifying one is
- // allowed to simplify temporarily switching modes during development
-
- bool no_existing_realm = std::all_of(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers),
- [](auto& notifier) { return notifier.expired(); });
- if (no_existing_realm) {
- m_config = config;
- m_config.scheduler = nullptr;
- }
- else {
- if (m_config.immutable() != config.immutable()) {
- 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 '%1' already opened with different inMemory settings.", config.path);
- }
- if (m_config.encryption_key != config.encryption_key) {
- throw MismatchedConfigException("Realm at path '%1' already opened with a different encryption key.", config.path);
- }
- if (m_config.schema_mode != config.schema_mode) {
- throw MismatchedConfigException("Realm at path '%1' already opened with a different schema mode.", config.path);
- }
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- if (config.schema && m_schema_version != ObjectStore::NotVersioned && m_schema_version != config.schema_version) {
- throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path);
- }
-
-#if REALM_ENABLE_SYNC
- if (bool(m_config.sync_config) != bool(config.sync_config)) {
- throw MismatchedConfigException("Realm at path '%1' already opened with different sync configurations.", config.path);
- }
-
- if (config.sync_config) {
- if (m_config.sync_config->user != config.sync_config->user) {
- throw MismatchedConfigException("Realm at path '%1' already opened with different sync user.", config.path);
- }
- if (m_config.sync_config->realm_url() != config.sync_config->realm_url()) {
- throw MismatchedConfigException("Realm at path '%1' already opened with different sync server URL.", config.path);
- }
- if (m_config.sync_config->transformer != config.sync_config->transformer) {
- throw MismatchedConfigException("Realm at path '%1' already opened with different transformer.", config.path);
- }
- if (m_config.sync_config->realm_encryption_key != config.sync_config->realm_encryption_key) {
- throw MismatchedConfigException("Realm at path '%1' already opened with sync session encryption key.", config.path);
- }
- }
-#endif
-
- // Realm::update_schema() handles complaining about schema mismatches
- }
-}
-
-std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config, util::Optional<VersionID> version)
-{
- if (!config.scheduler)
- config.scheduler = version ? util::Scheduler::get_frozen() : util::Scheduler::make_default();
- // realm must be declared before lock so that the mutex is released before
- // we release the strong reference to realm, as Realm's destructor may want
- // to acquire the same lock
- std::shared_ptr<Realm> realm;
- util::CheckedUniqueLock lock(m_realm_mutex);
- set_config(config);
- do_get_realm(std::move(config), realm, version, lock);
- return realm;
-}
-
-std::shared_ptr<Realm> RealmCoordinator::get_realm(std::shared_ptr<util::Scheduler> scheduler)
-{
- std::shared_ptr<Realm> realm;
- util::CheckedUniqueLock lock(m_realm_mutex);
- auto config = m_config;
- config.scheduler = scheduler ? scheduler : util::Scheduler::make_default();
- do_get_realm(std::move(config), realm, none, lock);
- return realm;
-}
-
-ThreadSafeReference RealmCoordinator::get_unbound_realm()
-{
- std::shared_ptr<Realm> realm;
- util::CheckedUniqueLock lock(m_realm_mutex);
- do_get_realm(m_config, realm, none, lock);
- return ThreadSafeReference(realm);
-}
-
-void RealmCoordinator::do_get_realm(Realm::Config config, std::shared_ptr<Realm>& realm,
- util::Optional<VersionID> version,
- CheckedUniqueLock& realm_lock)
-{
- open_db();
-
- auto schema = std::move(config.schema);
- auto migration_function = std::move(config.migration_function);
- auto initialization_function = std::move(config.initialization_function);
- auto audit_factory = std::move(config.audit_factory);
- config.schema = {};
-
- realm = Realm::make_shared_realm(std::move(config), version, shared_from_this());
- if (!m_notifier && !m_config.immutable() && m_config.automatic_change_notifications) {
- try {
- m_notifier = std::make_unique<ExternalCommitHelper>(*this);
- }
- catch (std::system_error const& ex) {
- throw RealmFileException(RealmFileException::Kind::AccessError,
- get_path(), ex.code().message(), "");
- }
- }
- m_weak_realm_notifiers.emplace_back(realm);
-
- if (realm->config().sync_config)
- create_sync_session(false);
-
- if (!m_audit_context && audit_factory)
- m_audit_context = audit_factory();
-
- realm_lock.unlock_unchecked();
- if (schema) {
-#if REALM_ENABLE_SYNC && REALM_PLATFORM_JAVA
- // Workaround for https://github.com/realm/realm-java/issues/6619
- // Between Realm Java 5.10.0 and 5.13.0 created_at/updated_at was optional
- // when created from Java, even though the Object Store code specified them as
- // required. Due to how the Realm was initialized, this wasn't a problem before
- // 5.13.0, but after that the Object Store initializer code was changed causing
- // problems when Java clients upgraded. In order to prevent older clients from
- // breaking with a schema mismatch when upgrading we thus fix the schema in transit.
- // This means that schema reported back from Realm will be different than the one
- // specified in the Java model class, but this seemed like the approach with the
- // least amount of disadvantages.
- if (realm->is_partial()) {
- auto& new_schema = schema.value();
- auto current_schema = realm->schema();
- auto current_resultsets_schema_obj = current_schema.find("__ResultSets");
- if (current_resultsets_schema_obj != current_schema.end()) {
- Property* p = current_resultsets_schema_obj->property_for_public_name("created_at");
- if (is_nullable(p->type)) {
- auto it = new_schema.find("__ResultSets");
- if (it != new_schema.end()) {
- auto created_at_property = it->property_for_public_name("created_at");
- auto updated_at_property = it->property_for_public_name("updated_at");
- if (created_at_property && updated_at_property) {
- created_at_property->type = created_at_property->type | PropertyType::Nullable;
- updated_at_property->type = updated_at_property->type | PropertyType::Nullable;
- }
- }
- }
- }
- }
-#endif
- realm->update_schema(std::move(*schema), config.schema_version, std::move(migration_function),
- std::move(initialization_function));
- }
-#if REALM_ENABLE_SYNC
- else if (realm->is_partial())
- _impl::ensure_partial_sync_schema_initialized(*realm);
-#endif
-}
-
-void RealmCoordinator::bind_to_context(Realm& realm)
-{
- util::CheckedLockGuard lock(m_realm_mutex);
- for (auto& cached_realm : m_weak_realm_notifiers) {
- if (!cached_realm.is_for_realm(&realm))
- continue;
- cached_realm.bind_to_scheduler();
- return;
- }
- REALM_TERMINATE("Invalid Realm passed to bind_to_context()");
-}
-
-#if REALM_ENABLE_SYNC
-std::shared_ptr<AsyncOpenTask> RealmCoordinator::get_synchronized_realm(Realm::Config config)
-{
- if (!config.sync_config)
- throw std::logic_error("This method is only available for fully synchronized Realms.");
-
- util::CheckedLockGuard lock(m_realm_mutex);
- set_config(config);
- bool exists = File::exists(m_config.path);
- create_sync_session(!config.sync_config->is_partial && !exists);
- return std::make_shared<AsyncOpenTask>(shared_from_this(), m_sync_session);
-}
-
-void RealmCoordinator::create_session(const Realm::Config& config)
-{
- REALM_ASSERT(config.sync_config);
- util::CheckedLockGuard lock(m_realm_mutex);
- set_config(config);
- bool exists = File::exists(m_config.path);
- create_sync_session(!config.sync_config->is_partial && !exists);
-}
-
-void RealmCoordinator::open_with_config(Realm::Config config)
-{
- CheckedLockGuard lock(m_realm_mutex);
- set_config(config);
- open_db();
-}
-
-#endif
-
-namespace realm {
-namespace _impl {
-REALM_NOINLINE void translate_file_exception(StringData path, bool immutable)
-{
- 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(), immutable ? "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 exist.", ex.get_path()), 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());
- }
- 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();
- RealmFileException::Kind error_kind = RealmFileException::Kind::AccessError;
- // FIXME: Replace this with a proper specific exception type once Core adds support for it.
- if (underlying == "Bad or incompatible history type")
- error_kind = RealmFileException::Kind::BadHistoryError;
- 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(error_kind, 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 (UnsupportedFileFormatVersion const& ex) {
- throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, path,
- util::format("Opening Realm files of format version %1 is not supported by this version of Realm", ex.source_version),
- ex.what());
- }
-}
-} // namespace _impl
-} // namespace realm
-
-void RealmCoordinator::open_db()
-{
- if (m_db || m_read_only_group)
- return;
-
- bool server_synchronization_mode = m_config.sync_config || m_config.force_sync_history;
- try {
- if (m_config.immutable()) {
- if (m_config.realm_data.is_null()) {
- m_read_only_group = std::make_shared<Group>(m_config.path,
- m_config.encryption_key.data(),
- Group::mode_ReadOnly);
- }
- else {
- // Create in-memory read-only realm from existing buffer (without taking ownership of the buffer)
- m_read_only_group = std::make_unique<Group>(m_config.realm_data, false);
- }
- return;
- }
-
- if (server_synchronization_mode) {
-#if REALM_ENABLE_SYNC
- m_history = sync::make_client_replication(m_config.path);
-#else
- REALM_TERMINATE("Realm was not built with sync enabled");
-#endif
- }
- else {
- m_history = make_in_realm_history(m_config.path);
- }
-
- DBOptions options;
- options.durability = m_config.in_memory
- ? DBOptions::Durability::MemOnly
- : DBOptions::Durability::Full;
-
- if (!m_config.fifo_files_fallback_path.empty()) {
- options.temp_dir = util::normalize_dir(m_config.fifo_files_fallback_path);
- }
- options.encryption_key = m_config.encryption_key.data();
- options.allow_file_format_upgrade = !m_config.disable_format_upgrade
- && m_config.schema_mode != SchemaMode::ResetFile;
- m_db = DB::create(*m_history, options);
- }
- catch (realm::FileFormatUpgradeRequired const&) {
- if (m_config.schema_mode != SchemaMode::ResetFile) {
- translate_file_exception(m_config.path, m_config.immutable());
- }
- util::File::remove(m_config.path);
- return open_db();
- }
- catch (UnsupportedFileFormatVersion const&) {
- if (m_config.schema_mode != SchemaMode::ResetFile) {
- translate_file_exception(m_config.path, m_config.immutable());
- }
- util::File::remove(m_config.path);
- return open_db();
- }
-#if REALM_ENABLE_SYNC
- catch (IncompatibleHistories const& ex) {
- translate_file_exception(m_config.path, m_config.immutable()); // Throws
- }
-#endif // REALM_ENABLE_SYNC
- catch (...) {
- translate_file_exception(m_config.path, m_config.immutable());
- }
-
- if (!m_config.should_compact_on_launch_function)
- return;
-
- size_t free_space = 0;
- size_t used_space = 0;
- if (auto tr = m_db->start_write(true)) {
- tr->commit();
- m_db->get_stats(free_space, used_space);
- }
- if (free_space > 0 && m_config.should_compact_on_launch_function(free_space + used_space, used_space))
- m_db->compact();
-}
-
-void RealmCoordinator::close()
-{
- m_db->close();
- m_db = nullptr;
-}
-
-std::shared_ptr<Group> RealmCoordinator::begin_read(VersionID version, bool frozen_transaction)
-{
- open_db();
- if (m_read_only_group)
- return m_read_only_group;
- return (frozen_transaction) ? m_db->start_frozen(version) : m_db->start_read(version);
-}
-
-uint64_t RealmCoordinator::get_schema_version() const noexcept
-{
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- return m_schema_version;
-}
-
-bool RealmCoordinator::get_cached_schema(Schema& schema, uint64_t& schema_version,
- uint64_t& transaction) const noexcept
-{
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- if (!m_cached_schema)
- return false;
- schema = *m_cached_schema;
- schema_version = m_schema_version;
- transaction = m_schema_transaction_version_max;
- return true;
-}
-
-void RealmCoordinator::cache_schema(Schema const& new_schema, uint64_t new_schema_version,
- uint64_t transaction_version)
-{
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- if (transaction_version < m_schema_transaction_version_max)
- return;
- if (new_schema.empty() || new_schema_version == ObjectStore::NotVersioned)
- return;
-
- m_cached_schema = new_schema;
- m_schema_version = new_schema_version;
- m_schema_transaction_version_min = transaction_version;
- m_schema_transaction_version_max = transaction_version;
-}
-
-void RealmCoordinator::clear_schema_cache_and_set_schema_version(uint64_t new_schema_version)
-{
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- m_cached_schema = util::none;
- m_schema_version = new_schema_version;
-}
-
-void RealmCoordinator::advance_schema_cache(uint64_t previous, uint64_t next)
-{
- util::CheckedLockGuard lock(m_schema_cache_mutex);
- if (!m_cached_schema)
- return;
- REALM_ASSERT(previous <= m_schema_transaction_version_max);
- if (next < m_schema_transaction_version_min)
- return;
- m_schema_transaction_version_min = std::min(previous, m_schema_transaction_version_min);
- m_schema_transaction_version_max = std::max(next, m_schema_transaction_version_max);
-}
-
-RealmCoordinator::RealmCoordinator()
-#if REALM_ENABLE_SYNC
-: m_partial_sync_work_queue(std::make_unique<_impl::partial_sync::WorkQueue>())
-#endif
-{
-}
-
-RealmCoordinator::~RealmCoordinator()
-{
- {
- std::lock_guard<std::mutex> coordinator_lock(s_coordinator_mutex);
- for (auto it = s_coordinators_per_path.begin(); it != s_coordinators_per_path.end(); ) {
- if (it->second.expired()) {
- it = s_coordinators_per_path.erase(it);
- }
- else {
- ++it;
- }
- }
- }
- // Waits for the worker thread to join
- m_notifier = nullptr;
-
- // Ensure the notifiers aren't holding on to Transactions after we destroy
- // the History object the DB depends on
- // No locking needed here because the worker thread is gone
- for (auto& notifier : m_new_notifiers)
- notifier->release_data();
- for (auto& notifier : m_notifiers)
- notifier->release_data();
-}
-
-void RealmCoordinator::unregister_realm(Realm* realm)
-{
- // Normally results notifiers are cleaned up by the background worker thread
- // but if that's disabled we need to ensure that any notifiers from this
- // Realm get cleaned up
- if (!m_config.automatic_change_notifications) {
- util::CheckedLockGuard lock(m_notifier_mutex);
- clean_up_dead_notifiers();
- }
- {
- util::CheckedLockGuard lock(m_realm_mutex);
- auto new_end = remove_if(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers),
- [=](auto& notifier) { return notifier.expired() || notifier.is_for_realm(realm); });
- m_weak_realm_notifiers.erase(new_end, end(m_weak_realm_notifiers));
- }
-}
-
-// Thread-safety analsys doesn't reasonably handle calling functions on different
-// instances of this type
-void RealmCoordinator::clear_cache() NO_THREAD_SAFETY_ANALYSIS
-{
- std::vector<std::shared_ptr<Realm>> realms_to_close;
- std::vector<std::shared_ptr<RealmCoordinator>> coordinators;
- {
- std::lock_guard<std::mutex> lock(s_coordinator_mutex);
-
- for (auto& weak_coordinator : s_coordinators_per_path) {
- auto coordinator = weak_coordinator.second.lock();
- if (!coordinator) {
- continue;
- }
- coordinators.push_back(coordinator);
-
- coordinator->m_notifier = nullptr;
-
- // Gather a list of all of the realms which will be removed
- CheckedLockGuard lock(coordinator->m_realm_mutex);
- for (auto& weak_realm_notifier : coordinator->m_weak_realm_notifiers) {
- if (auto realm = weak_realm_notifier.realm()) {
- realms_to_close.push_back(realm);
- }
- }
- }
-
- s_coordinators_per_path.clear();
- }
- coordinators.clear();
-
- // Close all of the previously cached Realms. This can't be done while
- // s_coordinator_mutex is held as it may try to re-lock it.
- for (auto& realm : realms_to_close)
- realm->close();
-}
-
-void RealmCoordinator::clear_all_caches()
-{
- std::vector<std::weak_ptr<RealmCoordinator>> to_clear;
- {
- std::lock_guard<std::mutex> lock(s_coordinator_mutex);
- for (auto iter : s_coordinators_per_path) {
- to_clear.push_back(iter.second);
- }
- }
- for (auto weak_coordinator : to_clear) {
- if (auto coordinator = weak_coordinator.lock()) {
- coordinator->clear_cache();
- }
- }
-}
-
-void RealmCoordinator::assert_no_open_realms() noexcept
-{
-#ifdef REALM_DEBUG
- std::lock_guard<std::mutex> lock(s_coordinator_mutex);
- REALM_ASSERT(s_coordinators_per_path.empty());
-#endif
-}
-
-void RealmCoordinator::wake_up_notifier_worker()
-{
- if (m_notifier) {
- // FIXME: this wakes up the notification workers for all processes and
- // not just us. This might be worth optimizing in the future.
- m_notifier->notify_others();
- }
-}
-
-void RealmCoordinator::commit_write(Realm& realm)
-{
- REALM_ASSERT(!m_config.immutable());
- REALM_ASSERT(realm.is_in_transaction());
-
- Transaction& tr = Realm::Internal::get_transaction(realm);
- {
- // Need to acquire this lock before committing or another process could
- // perform a write and notify us before we get the chance to set the
- // skip version
- util::CheckedLockGuard l(m_notifier_mutex);
-
- tr.commit_and_continue_as_read();
-
- // Don't need to check m_new_notifiers because those don't skip versions
- bool have_notifiers = std::any_of(m_notifiers.begin(), m_notifiers.end(),
- [&](auto&& notifier) { return notifier->is_for_realm(realm); });
- if (have_notifiers) {
- m_notifier_skip_version = Realm::Internal::get_transaction(realm).get_version_of_current_transaction();
- }
- }
-
-#if REALM_ENABLE_SYNC
- // Realm could be closed in did_change. So send sync notification first before did_change.
- if (m_sync_session) {
- auto version = tr.get_version();
- SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version);
- }
-#endif
- if (realm.m_binding_context) {
- realm.m_binding_context->did_change({}, {});
- }
-
- if (m_notifier) {
- m_notifier->notify_others();
- }
-}
-
-void RealmCoordinator::enable_wait_for_change()
-{
- m_db->enable_wait_for_change();
-}
-
-bool RealmCoordinator::wait_for_change(std::shared_ptr<Transaction> tr)
-{
- return m_db->wait_for_change(tr);
-}
-
-void RealmCoordinator::wait_for_change_release()
-{
- m_db->wait_for_change_release();
-}
-
-void RealmCoordinator::pin_version(VersionID versionid)
-{
- if (m_async_error)
- return;
- if (!m_advancer_sg || versionid < m_advancer_sg->get_version_of_current_transaction())
- m_advancer_sg = m_db->start_read(versionid);
-}
-
-// Thread-safety analsys doesn't reasonably handle calling functions on different
-// instances of this type
-void RealmCoordinator::register_notifier(std::shared_ptr<CollectionNotifier> notifier) NO_THREAD_SAFETY_ANALYSIS
-{
- auto version = notifier->version();
- auto& self = Realm::Internal::get_coordinator(*notifier->get_realm());
- {
- util::CheckedLockGuard lock(self.m_notifier_mutex);
- self.pin_version(version);
- self.m_new_notifiers.push_back(std::move(notifier));
- }
-}
-
-void RealmCoordinator::clean_up_dead_notifiers()
-{
- auto swap_remove = [&](auto& container) {
- bool did_remove = false;
- for (size_t i = 0; i < container.size(); ++i) {
- if (container[i]->is_alive())
- continue;
-
- // Ensure the notifier is destroyed here even if there's lingering refs
- // to the async notifier elsewhere
- container[i]->release_data();
-
- if (container.size() > i + 1)
- container[i] = std::move(container.back());
- container.pop_back();
- --i;
- did_remove = true;
- }
- return did_remove;
- };
-
- if (swap_remove(m_notifiers) && m_notifiers.empty()) {
- m_notifier_sg = nullptr;
- m_notifier_skip_version = {0, 0};
- }
- if (swap_remove(m_new_notifiers) && m_new_notifiers.empty()) {
- m_advancer_sg = nullptr;
- }
-}
-
-void RealmCoordinator::on_change()
-{
- run_async_notifiers();
-
- util::CheckedLockGuard lock(m_realm_mutex);
- for (auto& realm : m_weak_realm_notifiers) {
- realm.notify();
- }
-}
-
-namespace {
-class IncrementalChangeInfo {
-public:
- IncrementalChangeInfo(Transaction& sg,
- std::vector<std::shared_ptr<_impl::CollectionNotifier>>& notifiers)
- : m_sg(sg)
- {
- if (notifiers.empty())
- return;
-
- auto cmp = [&](auto&& lft, auto&& rgt) {
- return lft->version() < rgt->version();
- };
-
- // Sort the notifiers by their source version so that we can pull them
- // all forward to the latest version in a single pass over the transaction log
- std::sort(notifiers.begin(), notifiers.end(), cmp);
-
- // Preallocate the required amount of space in the vector so that we can
- // safely give out pointers to within the vector
- size_t count = 1;
- for (auto it = notifiers.begin(), next = it + 1; next != notifiers.end(); ++it, ++next) {
- if (cmp(*it, *next))
- ++count;
- }
- m_info.reserve(count);
- m_info.resize(1);
- m_current = &m_info[0];
- }
-
- TransactionChangeInfo& current() const { return *m_current; }
-
- bool advance_incremental(VersionID version)
- {
- if (version != m_sg.get_version_of_current_transaction()) {
- transaction::advance(m_sg, *m_current, version);
- m_info.push_back({std::move(m_current->lists)});
- auto next = &m_info.back();
- for (auto& table : m_current->tables)
- next->tables[table.first];
- m_current = next;
- return true;
- }
- return false;
- }
-
- void advance_to_final(VersionID version)
- {
- if (!m_current) {
- transaction::advance(m_sg, nullptr, version);
- return;
- }
-
- transaction::advance(m_sg, *m_current, version);
-
- // We now need to combine the transaction change info objects so that all of
- // the notifiers see the complete set of changes from their first version to
- // the most recent one
- for (size_t i = m_info.size() - 1; i > 0; --i) {
- auto& cur = m_info[i];
- if (cur.tables.empty())
- continue;
- auto& prev = m_info[i - 1];
- if (prev.tables.empty()) {
- prev.tables = cur.tables;
- continue;
- }
- for (auto& ct : cur.tables) {
- auto& pt = prev.tables[ct.first];
- if (pt.empty())
- pt = ct.second;
- else
- pt.merge(ObjectChangeSet{ct.second});
- }
- }
-
- // Copy the list change info if there are multiple LinkViews for the same LinkList
- auto id = [](auto const& list) { return std::tie(list.table_key, list.col_key, list.row_key); };
- for (size_t i = 1; i < m_current->lists.size(); ++i) {
- for (size_t j = i; j > 0; --j) {
- if (id(m_current->lists[i]) == id(m_current->lists[j - 1])) {
- m_current->lists[j - 1].changes->merge(CollectionChangeBuilder{*m_current->lists[i].changes});
- }
- }
- }
- }
-
-private:
- std::vector<TransactionChangeInfo> m_info;
- TransactionChangeInfo* m_current = nullptr;
- Transaction& m_sg;
-};
-} // anonymous namespace
-
-void RealmCoordinator::run_async_notifiers()
-{
- util::CheckedUniqueLock lock(m_notifier_mutex);
-
- clean_up_dead_notifiers();
-
- if (m_notifiers.empty() && m_new_notifiers.empty()) {
- m_notifier_cv.notify_all();
- return;
- }
-
- if (!m_notifier_sg) {
- m_notifier_sg = m_db->start_read();
- }
-
- if (m_async_error) {
- std::move(m_new_notifiers.begin(), m_new_notifiers.end(), std::back_inserter(m_notifiers));
- m_new_notifiers.clear();
- m_notifier_cv.notify_all();
- return;
- }
-
- VersionID version;
-
- // Advance all of the new notifiers to the most recent version, if any
- auto new_notifiers = std::move(m_new_notifiers);
- IncrementalChangeInfo new_notifier_change_info(*m_advancer_sg, new_notifiers);
- auto advancer_sg = std::move(m_advancer_sg);
-
- if (!new_notifiers.empty()) {
- REALM_ASSERT(advancer_sg);
- REALM_ASSERT_3(advancer_sg->get_version_of_current_transaction().version,
- <=, new_notifiers.front()->version().version);
-
- // The advancer SG can be at an older version than the oldest new notifier
- // if a notifier was added and then removed before it ever got the chance
- // to run, as we don't move the pin forward when removing dead notifiers
- transaction::advance(*advancer_sg, nullptr, new_notifiers.front()->version());
-
- // Advance each of the new notifiers to the latest version, attaching them
- // to the SG at their handover version. This requires a unique
- // TransactionChangeInfo for each source version, so that things don't
- // see changes from before the version they were handed over from.
- // Each Info has all of the changes between that source version and the
- // next source version, and they'll be merged together later after
- // releasing the lock
- for (auto& notifier : new_notifiers) {
- new_notifier_change_info.advance_incremental(notifier->version());
- notifier->attach_to(advancer_sg);
- notifier->add_required_change_info(new_notifier_change_info.current());
- }
- new_notifier_change_info.advance_to_final(VersionID{});
-
- // We want to advance the non-new notifiers to the same version as the
- // new notifiers to avoid having to merge changes from any new
- // transaction that happen immediately after this into the new notifier
- // changes
- version = advancer_sg->get_version_of_current_transaction();
- }
- else {
- // If we have no new notifiers we want to just advance to the latest
- // version, but we have to pick a "latest" version while holding the
- // notifier lock to avoid advancing over a transaction which should be
- // skipped
- // FIXME: this is comically slow
- version = m_db->start_read()->get_version_of_current_transaction();
- }
-
- auto skip_version = m_notifier_skip_version;
- m_notifier_skip_version = {0, 0};
-
- // Make a copy of the notifiers vector and then release the lock to avoid
- // blocking other threads trying to register or unregister notifiers while we run them
- auto notifiers = m_notifiers;
- m_notifiers.insert(m_notifiers.end(), new_notifiers.begin(), new_notifiers.end());
- lock.unlock();
-
- if (skip_version.version) {
- REALM_ASSERT(!notifiers.empty());
- REALM_ASSERT(version >= skip_version);
- IncrementalChangeInfo change_info(*m_notifier_sg, notifiers);
- for (auto& notifier : notifiers)
- notifier->add_required_change_info(change_info.current());
- change_info.advance_to_final(skip_version);
-
- for (auto& notifier : notifiers)
- notifier->run();
-
- util::CheckedLockGuard lock(m_notifier_mutex);
- for (auto& notifier : notifiers)
- notifier->prepare_handover();
- }
-
- // Advance the non-new notifiers to the same version as we advanced the new
- // ones to (or the latest if there were no new ones)
- IncrementalChangeInfo change_info(*m_notifier_sg, notifiers);
- for (auto& notifier : notifiers) {
- notifier->add_required_change_info(change_info.current());
- }
- change_info.advance_to_final(version);
-
- // Attach the new notifiers to the main SG and move them to the main list
- for (auto& notifier : new_notifiers) {
- notifier->attach_to(m_notifier_sg);
- notifier->run();
- }
-
- // Change info is now all ready, so the notifiers can now perform their
- // background work
- for (auto& notifier : notifiers) {
- notifier->run();
- }
-
- // Reacquire the lock while updating the fields that are actually read on
- // other threads
- util::CheckedLockGuard lock2(m_notifier_mutex);
- for (auto& notifier : new_notifiers) {
- notifier->prepare_handover();
- }
- for (auto& notifier : notifiers) {
- notifier->prepare_handover();
- }
- clean_up_dead_notifiers();
- m_notifier_cv.notify_all();
-}
-
-bool RealmCoordinator::can_advance(Realm& realm)
-{
- bool changes = realm.last_seen_transaction_version() != m_db->get_version_of_latest_snapshot();
- return changes;
-}
-
-void RealmCoordinator::advance_to_ready(Realm& realm)
-{
- util::CheckedUniqueLock lock(m_notifier_mutex);
- _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
- lock.unlock();
- notifiers.package_and_wait(util::none);
-
- // FIXME: we probably won't actually want a strong pointer here
- auto sg = Realm::Internal::get_transaction_ref(realm);
- if (notifiers) {
- auto version = notifiers.version();
- if (version) {
- auto current_version = sg->get_version_of_current_transaction();
- // Notifications are out of date, so just discard
- // This should only happen if begin_read() was used to change the
- // read version outside of our control
- if (*version < current_version)
- return;
- // While there is a newer version, notifications are for the current
- // version so just deliver them without advancing
- if (*version == current_version) {
- if (realm.m_binding_context)
- realm.m_binding_context->will_send_notifications();
- notifiers.after_advance();
- if (realm.m_binding_context)
- realm.m_binding_context->did_send_notifications();
- return;
- }
- }
- }
-
- transaction::advance(sg, realm.m_binding_context.get(), notifiers);
-}
-
-std::vector<std::shared_ptr<_impl::CollectionNotifier>> RealmCoordinator::notifiers_for_realm(Realm& realm)
-{
- std::vector<std::shared_ptr<_impl::CollectionNotifier>> ret;
- for (auto& notifier : m_new_notifiers) {
- if (notifier->is_for_realm(realm))
- ret.push_back(notifier);
- }
- for (auto& notifier : m_notifiers) {
- if (notifier->is_for_realm(realm))
- ret.push_back(notifier);
- }
- return ret;
-}
-
-bool RealmCoordinator::advance_to_latest(Realm& realm)
-{
- // FIXME: we probably won't actually want a strong pointer here
- auto self = shared_from_this();
- auto sg = Realm::Internal::get_transaction_ref(realm);
- util::CheckedUniqueLock lock(m_notifier_mutex);
- _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
- lock.unlock();
- notifiers.package_and_wait(sg->get_version_of_latest_snapshot());
-
- auto version = sg->get_version_of_current_transaction();
- transaction::advance(sg, realm.m_binding_context.get(), notifiers);
-
- // Realm could be closed in the callbacks.
- if (realm.is_closed())
- return false;
-
- return version != sg->get_version_of_current_transaction();
-}
-
-void RealmCoordinator::promote_to_write(Realm& realm)
-{
- REALM_ASSERT(!realm.is_in_transaction());
-
- util::CheckedUniqueLock lock(m_notifier_mutex);
- _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
- lock.unlock();
-
- // FIXME: we probably won't actually want a strong pointer here
- auto tr = Realm::Internal::get_transaction_ref(realm);
- transaction::begin(tr, realm.m_binding_context.get(), notifiers);
-}
-
-void RealmCoordinator::process_available_async(Realm& realm)
-{
- REALM_ASSERT(!realm.is_in_transaction());
-
- util::CheckedUniqueLock lock(m_notifier_mutex);
- auto notifiers = notifiers_for_realm(realm);
- if (notifiers.empty())
- return;
-
- if (auto error = m_async_error) {
- lock.unlock();
- if (realm.m_binding_context)
- realm.m_binding_context->will_send_notifications();
- for (auto& notifier : notifiers)
- notifier->deliver_error(m_async_error);
- if (realm.m_binding_context)
- realm.m_binding_context->did_send_notifications();
- return;
- }
-
- bool in_read = realm.is_in_read_transaction();
- auto& sg = Realm::Internal::get_transaction(realm);
- auto version = sg.get_version_of_current_transaction();
- auto package = [&](auto& notifier) {
- return !(notifier->has_run() && (!in_read || notifier->version() == version) && notifier->package_for_delivery());
- };
- notifiers.erase(std::remove_if(begin(notifiers), end(notifiers), package), end(notifiers));
- if (notifiers.empty())
- return;
- lock.unlock();
-
- // no before advance because the Realm is already at the given version,
- // because we're either sending initial notifications or the write was
- // done on this Realm instance
-
- if (realm.m_binding_context) {
- realm.m_binding_context->will_send_notifications();
- if (realm.is_closed()) // i.e. the Realm was closed in the callback above
- return;
- }
-
- for (auto& notifier : notifiers)
- notifier->after_advance();
-
- if (realm.m_binding_context)
- realm.m_binding_context->did_send_notifications();
-}
-
-void RealmCoordinator::set_transaction_callback(std::function<void(VersionID, VersionID)> fn)
-{
- create_sync_session(false);
- util::CheckedLockGuard lock(m_transaction_callback_mutex);
- m_transaction_callback = std::move(fn);
-}
-
-bool RealmCoordinator::compact()
-{
- return m_db->compact();
-}
-
-#if REALM_ENABLE_SYNC
-_impl::partial_sync::WorkQueue& RealmCoordinator::partial_sync_work_queue()
-{
- return *m_partial_sync_work_queue;
-}
-#endif
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp
deleted file mode 100644
index 23100b60a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp
+++ /dev/null
@@ -1,274 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_COORDINATOR_HPP
-#define REALM_COORDINATOR_HPP
-
-#include "shared_realm.hpp"
-
-#include "util/checked_mutex.hpp"
-
-#include <realm/version_id.hpp>
-
-#include <condition_variable>
-#include <mutex>
-
-namespace realm {
-class DB;
-class Replication;
-class Schema;
-class StringData;
-class SyncSession;
-class Transaction;
-
-namespace _impl {
-class CollectionNotifier;
-class ExternalCommitHelper;
-class WeakRealmNotifier;
-
-namespace partial_sync {
-class WorkQueue;
-}
-
-// RealmCoordinator manages the weak cache of Realm instances and communication
-// between per-thread Realm instances for a given file
-class RealmCoordinator : public std::enable_shared_from_this<RealmCoordinator> {
-public:
- // Get the coordinator for the given path, creating it if neccesary
- static std::shared_ptr<RealmCoordinator> get_coordinator(StringData path);
- // Get the coordinator for the given config, creating it if neccesary
- static std::shared_ptr<RealmCoordinator> get_coordinator(const Realm::Config&);
- // Get the coordinator for the given path, or null if there is none
- static std::shared_ptr<RealmCoordinator> get_existing_coordinator(StringData path);
-
- // Get a shared Realm with the given configuration
- // If the Realm is already opened on another thread, validate that the given
- // configuration is compatible with the existing one.
- // If no version is provided a live thread-confined Realm is returned.
- // Otherwise, a frozen Realm at the given version is returned. This
- // can be read from any thread.
- std::shared_ptr<Realm> get_realm(Realm::Config config, util::Optional<VersionID> version) REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
- std::shared_ptr<Realm> get_realm(std::shared_ptr<util::Scheduler> = nullptr) REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
-#if REALM_ENABLE_SYNC
- // Get a thread-local shared Realm with the given configuration
- // If the Realm is not already present, it will be fully downloaded before being returned.
- // If the Realm is already on disk, it will be fully synchronized before being returned.
- // Timeouts and interruptions are not handled by this method and must be handled by upper layers.
- std::shared_ptr<AsyncOpenTask> get_synchronized_realm(Realm::Config config)
- REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
- // Used by GlobalNotifier to bypass the normal initialization path
- void open_with_config(Realm::Config config) REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
-
- // Creates the underlying sync session if it doesn't already exists.
- // This is also created as part of opening a Realm, so only use this
- // method if the session needs to exist before the Realm does.
- void create_session(const Realm::Config& config) REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
-#endif
-
- // Get a Realm which is not bound to the current execution context
- ThreadSafeReference get_unbound_realm() REQUIRES(!m_realm_mutex);
-
- // Bind an unbound Realm to a specific execution context. The Realm must
- // be managed by this coordinator.
- void bind_to_context(Realm& realm) REQUIRES(!m_realm_mutex);
-
- Realm::Config get_config() const { return m_config; }
-
- uint64_t get_schema_version() const noexcept REQUIRES(!m_schema_cache_mutex);
- const std::string& get_path() const noexcept { return m_config.path; }
- const std::vector<char>& get_encryption_key() const noexcept { return m_config.encryption_key; }
- bool is_in_memory() const noexcept { return m_config.in_memory; }
- // Returns the number of versions in the Realm file.
- uint_fast64_t get_number_of_versions() const { return m_db->get_number_of_versions(); };
-
- // To avoid having to re-read and validate the file's schema every time a
- // new read transaction is begun, RealmCoordinator maintains a cache of the
- // most recently seen file schema and the range of transaction versions
- // which it applies to. Note that this schema may not be identical to that
- // of any Realm instances managed by this coordinator, as individual Realms
- // may only be using a subset of it.
-
- // Get the latest cached schema and the transaction version which it applies
- // to. Returns false if there is no cached schema.
- bool get_cached_schema(Schema& schema, uint64_t& schema_version, uint64_t& transaction) const noexcept REQUIRES(!m_schema_cache_mutex);
-
- // Cache the state of the schema at the given transaction version
- void cache_schema(Schema const& new_schema, uint64_t new_schema_version,
- uint64_t transaction_version) REQUIRES(!m_schema_cache_mutex);
- // If there is a schema cached for transaction version `previous`, report
- // that it is still valid at transaction version `next`
- void advance_schema_cache(uint64_t previous, uint64_t next) REQUIRES(!m_schema_cache_mutex);
- void clear_schema_cache_and_set_schema_version(uint64_t new_schema_version) REQUIRES(!m_schema_cache_mutex);
-
-
- // Asynchronously call notify() on every Realm instance for this coordinator's
- // path, including those in other processes
- void send_commit_notifications(Realm&);
- void wake_up_notifier_worker();
-
- // Clear the weak Realm cache for all paths
- // Should only be called in test code, as continuing to use the previously
- // cached instances will have odd results
- static void clear_cache();
-
- // Clears all caches on existing coordinators
- static void clear_all_caches();
-
- // Verify that there are no Realms open for any paths
- static void assert_no_open_realms() noexcept;
-
- // Explicit constructor/destructor needed for the unique_ptrs to forward-declared types
- RealmCoordinator();
- ~RealmCoordinator();
-
- // Called by Realm's destructor to ensure the cache is cleaned up promptly
- // Do not call directly
- void unregister_realm(Realm* realm) REQUIRES(!m_realm_mutex, !m_notifier_mutex);
-
- // Called by m_notifier when there's a new commit to send notifications for
- void on_change() REQUIRES(!m_realm_mutex, !m_notifier_mutex);
-
- static void register_notifier(std::shared_ptr<CollectionNotifier> notifier);
-
- std::shared_ptr<Group> begin_read(VersionID version={}, bool frozen_transaction = false);
-
- // Check if advance_to_ready() would actually advance the Realm's read version
- bool can_advance(Realm& realm);
-
- // Advance the Realm to the most recent transaction version which all async
- // work is complete for
- void advance_to_ready(Realm& realm) REQUIRES(!m_notifier_mutex);
-
- // Advance the Realm to the most recent transaction version, blocking if
- // async notifiers are not yet ready for that version
- // returns whether it actually changed the version
- bool advance_to_latest(Realm& realm) REQUIRES(!m_notifier_mutex);
-
- // Deliver any notifications which are ready for the Realm's version
- void process_available_async(Realm& realm) REQUIRES(!m_notifier_mutex);
-
- // Register a function which is called whenever sync makes a write to the Realm
- void set_transaction_callback(std::function<void(VersionID, VersionID)>) REQUIRES(!m_transaction_callback_mutex);
-
- // Deliver notifications for the Realm, blocking if some aren't ready yet
- // The calling Realm must be in a write transaction
- void promote_to_write(Realm& realm) REQUIRES(!m_notifier_mutex);
-
- // Commit a Realm's current write transaction and send notifications to all
- // other Realm instances for that path, including in other processes
- void commit_write(Realm& realm) REQUIRES(!m_notifier_mutex);
-
- void enable_wait_for_change();
- bool wait_for_change(std::shared_ptr<Transaction> tr);
- void wait_for_change_release();
-
- void close();
- bool compact();
-
- template<typename Pred>
- util::CheckedUniqueLock wait_for_notifiers(Pred&& wait_predicate) REQUIRES(!m_notifier_mutex);
-
-#if REALM_ENABLE_SYNC
- // A work queue that can be used to perform background work related to partial sync.
- _impl::partial_sync::WorkQueue& partial_sync_work_queue();
-#endif
-
- AuditInterface* audit_context() const noexcept { return m_audit_context.get(); }
-
-private:
- friend Realm::Internal;
- Realm::Config m_config;
- std::unique_ptr<Replication> m_history;
- std::shared_ptr<DB> m_db;
- std::shared_ptr<Group> m_read_only_group;
-
- mutable util::CheckedMutex m_schema_cache_mutex;
- util::Optional<Schema> m_cached_schema GUARDED_BY(m_schema_cache_mutex);
- uint64_t m_schema_version GUARDED_BY(m_schema_cache_mutex) = -1;
- uint64_t m_schema_transaction_version_min GUARDED_BY(m_schema_cache_mutex) = 0;
- uint64_t m_schema_transaction_version_max GUARDED_BY(m_schema_cache_mutex) = 0;
-
- util::CheckedMutex m_realm_mutex;
- std::vector<WeakRealmNotifier> m_weak_realm_notifiers GUARDED_BY(m_realm_mutex);
-
- util::CheckedMutex m_notifier_mutex;
- std::condition_variable m_notifier_cv;
- std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_new_notifiers GUARDED_BY(m_notifier_mutex);
- std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_notifiers GUARDED_BY(m_notifier_mutex);
- VersionID m_notifier_skip_version GUARDED_BY(m_notifier_mutex) = {0, 0};
-
- // Transaction used for actually running async notifiers
- // Will have a read transaction iff m_notifiers is non-empty
- std::shared_ptr<Transaction> m_notifier_sg;
-
- // Transaction used to advance notifiers in m_new_notifiers to the main shared
- // group's transaction version
- // Will have a read transaction iff m_new_notifiers is non-empty
- std::shared_ptr<Transaction> m_advancer_sg;
- std::exception_ptr m_async_error;
-
- std::unique_ptr<_impl::ExternalCommitHelper> m_notifier;
- util::CheckedMutex m_transaction_callback_mutex;
- std::function<void(VersionID, VersionID)> m_transaction_callback GUARDED_BY(m_transaction_callback_mutex);
-
-#if REALM_ENABLE_SYNC
- std::shared_ptr<SyncSession> m_sync_session;
- std::unique_ptr<partial_sync::WorkQueue> m_partial_sync_work_queue;
-#endif
-
- std::shared_ptr<AuditInterface> m_audit_context;
-
- void open_db();
-
- void pin_version(VersionID version) REQUIRES(m_notifier_mutex);
-
- void set_config(const Realm::Config&) REQUIRES(m_realm_mutex, !m_schema_cache_mutex);
- void create_sync_session(bool force_client_resync);
- void do_get_realm(Realm::Config config, std::shared_ptr<Realm>& realm,
- util::Optional<VersionID> version,
- util::CheckedUniqueLock& realm_lock) REQUIRES(m_realm_mutex);
- void run_async_notifiers() REQUIRES(!m_notifier_mutex);
- void advance_helper_shared_group_to_latest();
- void clean_up_dead_notifiers() REQUIRES(m_notifier_mutex);
-
- std::vector<std::shared_ptr<_impl::CollectionNotifier>> notifiers_for_realm(Realm&) REQUIRES(m_notifier_mutex);
-};
-
-void translate_file_exception(StringData path, bool immutable=false);
-
-template<typename Pred>
-util::CheckedUniqueLock RealmCoordinator::wait_for_notifiers(Pred&& wait_predicate)
-{
- util::CheckedUniqueLock lock(m_notifier_mutex);
- bool first = true;
- m_notifier_cv.wait(lock.native_handle(), [&] {
- if (wait_predicate())
- return true;
- if (first) {
- wake_up_notifier_worker();
- first = false;
- }
- return false;
- });
- return lock;
-}
-
-} // namespace _impl
-} // namespace realm
-
-#endif /* REALM_COORDINATOR_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp
deleted file mode 100644
index 633ded68d..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/results_notifier.hpp"
-
-#include "shared_realm.hpp"
-
-#include <numeric>
-
-using namespace realm;
-using namespace realm::_impl;
-
-// Some of the inter-thread synchronization for this class is handled externally
-// by RealmCoordinator using the "notifier lock" which also guards registering
-// and unregistering notifiers. This can make it somewhat difficult to tell what
-// can safely be accessed where.
-//
-// The data flow is:
-// - ResultsNotifier is created on target thread.
-// - On background worker thread:
-// * do_attach_to() called with notifier lock held
-// - Writes to m_query
-// * do_add_required_change_info() called with notifier lock held
-// - Writes to m_info
-// * run() called with no locks held
-// - Reads m_query
-// - Reads m_info
-// - Reads m_need_to_run <-- FIXME: data race?
-// - Writes m_run_tv
-// * do_prepare_handover() called with notifier lock held
-// - Reads m_run_tv
-// - Writes m_handover_transaction
-// - Writes m_handover_tv
-// - On target thread:
-// * prepare_to_deliver() called with notifier lock held
-// - Reads m_handover_transaction
-// - Reads m_handover_tv
-// - Writes m_deliver_transaction
-// - Writes m_deliver_handover
-// * get_tableview() called with no locks held
-// - Reads m_deliver_transaction
-// - Reads m_deliver_handover
-// - Reads m_results_were_used
-
-ResultsNotifier::ResultsNotifier(Results& target)
-: ResultsNotifierBase(target.get_realm())
-, m_query(std::make_unique<Query>(target.get_query()))
-, m_descriptor_ordering(target.get_descriptor_ordering())
-, m_target_is_in_table_order(target.is_in_table_order())
-{
- auto table = m_query->get_table();
- if (table) {
- set_table(table);
- }
-}
-
-void ResultsNotifier::release_data() noexcept
-{
- m_query = {};
- m_run_tv = {};
- m_handover_tv = {};
- m_handover_transaction = {};
- m_delivered_tv = {};
- m_delivered_transaction = {};
- CollectionNotifier::release_data();
-}
-
-bool ResultsNotifier::get_tableview(TableView& out)
-{
- if (!m_delivered_tv)
- return false;
- auto& transaction = source_shared_group();
- if (m_delivered_transaction->get_version_of_current_transaction() != transaction.get_version_of_current_transaction())
- return false;
-
- out = std::move(*transaction.import_copy_of(*m_delivered_tv, PayloadPolicy::Move));
- m_delivered_tv.reset();
- return true;
-}
-
-bool ResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info)
-{
- m_info = &info;
- return m_query->get_table() && has_run() && have_callbacks();
-}
-
-bool ResultsNotifier::need_to_run()
-{
- REALM_ASSERT(m_info);
-
- {
- auto lock = lock_target();
- // Don't run the query if the results aren't actually going to be used
- if (!get_realm() || (!have_callbacks() && !m_results_were_used))
- return false;
- }
-
- // If we've run previously, check if we need to rerun
- if (has_run() && m_query->sync_view_if_needed() == m_last_seen_version) {
- // Does m_last_seen_version match m_related_tables
- if (all_related_tables_covered(m_last_seen_version)) {
- return false;
- }
- }
- return true;
-}
-
-void ResultsNotifier::calculate_changes()
-{
- if (has_run() && have_callbacks()) {
- std::vector<int64_t> next_rows;
- next_rows.reserve(m_run_tv.size());
- for (size_t i = 0; i < m_run_tv.size(); ++i)
- next_rows.push_back(m_run_tv.get_key(i).value);
-
- m_change = CollectionChangeBuilder::calculate(m_previous_rows, next_rows,
- get_modification_checker(*m_info, m_query->get_table()),
- m_target_is_in_table_order);
-
- m_previous_rows = std::move(next_rows);
- }
- else {
- m_previous_rows.resize(m_run_tv.size());
- for (size_t i = 0; i < m_run_tv.size(); ++i)
- m_previous_rows[i] = m_run_tv.get_key(i).value;
- }
-}
-
-void ResultsNotifier::run()
-{
- // Table's been deleted, so report all rows as deleted
- if (!m_query->get_table()) {
- m_change = {};
- m_change.deletions.set(m_previous_rows.size());
- m_previous_rows.clear();
- return;
- }
-
- if (!need_to_run())
- return;
-
- m_query->sync_view_if_needed();
- m_run_tv = m_query->find_all();
- m_run_tv.apply_descriptor_ordering(m_descriptor_ordering);
- m_run_tv.sync_if_needed();
- m_last_seen_version = m_run_tv.ObjList::get_dependency_versions();
-
- calculate_changes();
-}
-
-void ResultsNotifier::do_prepare_handover(Transaction& sg)
-{
- m_handover_tv.reset();
- if (m_handover_transaction)
- m_handover_transaction->advance_read(sg.get_version_of_current_transaction());
-
- if (m_run_tv.is_attached()) {
- REALM_ASSERT(m_run_tv.is_in_sync());
- if (!m_handover_transaction)
- m_handover_transaction = sg.duplicate();
- m_handover_tv = m_run_tv.clone_for_handover(m_handover_transaction.get(), PayloadPolicy::Move);
- m_run_tv = {};
- }
-}
-
-bool ResultsNotifier::prepare_to_deliver()
-{
- auto lock = lock_target();
- auto realm = get_realm();
- if (!realm) {
- m_handover_tv.reset();
- m_delivered_tv.reset();
- return false;
- }
- if (!m_handover_tv) {
- bool transaction_is_stale = m_delivered_transaction &&
- (!realm->is_in_read_transaction() || realm->read_transaction_version() > m_delivered_transaction->get_version_of_current_transaction());
- if (transaction_is_stale) {
- m_delivered_tv.reset();
- m_delivered_transaction.reset();
- }
- return true;
- }
-
- m_results_were_used = !m_delivered_tv;
- m_delivered_tv.reset();
- if (m_delivered_transaction)
- m_delivered_transaction->advance_read(m_handover_transaction->get_version_of_current_transaction());
- else
- m_delivered_transaction = m_handover_transaction->duplicate();
- m_delivered_tv = m_delivered_transaction->import_copy_of(*m_handover_tv, PayloadPolicy::Move);
- m_handover_tv.reset();
-
- return true;
-}
-
-void ResultsNotifier::do_attach_to(Transaction& sg)
-{
- if (m_query->get_table())
- m_query = sg.import_copy_of(*m_query, PayloadPolicy::Move);
-}
-
-ListResultsNotifier::ListResultsNotifier(Results& target)
-: ResultsNotifierBase(target.get_realm())
-, m_list(target.get_list())
-{
- auto& ordering = target.get_descriptor_ordering();
- for (size_t i = 0, sz = ordering.size(); i < sz; i++) {
- auto descr = ordering[i];
- if (descr->get_type() == DescriptorType::Sort)
- m_sort_order = static_cast<const SortDescriptor*>(descr)->is_ascending(0);
- if (descr->get_type() == DescriptorType::Distinct)
- m_distinct = true;
- }
-
-}
-
-void ListResultsNotifier::release_data() noexcept
-{
- m_list = {};
- CollectionNotifier::release_data();
-}
-
-bool ListResultsNotifier::get_list_indices(ListIndices& out)
-{
- if (!m_delivered_indices)
- return false;
- auto& transaction = source_shared_group();
- if (m_delivered_transaction_version != transaction.get_version_of_current_transaction())
- return false;
-
- out = std::move(m_delivered_indices);
- m_delivered_indices = util::none;
- return true;
-}
-
-bool ListResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info)
-{
- if (!m_list->is_attached())
- return false; // origin row was deleted after the notification was added
-
- info.lists.push_back({m_list->get_table()->get_key(), m_list->get_key().value,
- m_list->get_col_key().value, &m_change});
-
- m_info = &info;
- return true;
-}
-
-bool ListResultsNotifier::need_to_run()
-{
- REALM_ASSERT(m_info);
-
- {
- auto lock = lock_target();
- // Don't run the query if the results aren't actually going to be used
- if (!get_realm() || (!have_callbacks() && !m_results_were_used))
- return false;
- }
-
- return !has_run() || m_list->has_changed();
-}
-
-void ListResultsNotifier::calculate_changes()
-{
- // Unsorted lists can just forward the changeset directly from the
- // transaction log parsing, but sorted lists need to perform diffing
- if (has_run() && have_callbacks() && (m_sort_order || m_distinct)) {
- // Update each of the row indices in m_previous_indices to the equivalent
- // new index in the new list
- if (!m_change.insertions.empty() || !m_change.deletions.empty()) {
- for (auto& row : m_previous_indices) {
- if (m_change.deletions.contains(row))
- row = npos;
- else
- row = m_change.insertions.shift(m_change.deletions.unshift(row));
- }
- }
-
- m_change = CollectionChangeBuilder::calculate(m_previous_indices, *m_run_indices,
- [=](int64_t key) {
- return m_change.modifications_new.contains(static_cast<size_t>(key));
- });
- }
-
- m_previous_indices = *m_run_indices;
-}
-
-void ListResultsNotifier::run()
-{
- if (!m_list->is_attached()) {
- // List was deleted, so report all of the rows being removed
- m_change = {};
- m_change.deletions.set(m_previous_indices.size());
- m_previous_indices.clear();
- return;
- }
-
- if (!need_to_run())
- return;
-
- m_run_indices = std::vector<size_t>();
- if (m_distinct)
- m_list->distinct(*m_run_indices, m_sort_order);
- else if (m_sort_order)
- m_list->sort(*m_run_indices, *m_sort_order);
- else {
- m_run_indices->resize(m_list->size());
- std::iota(m_run_indices->begin(), m_run_indices->end(), 0);
- }
-
- calculate_changes();
-}
-
-void ListResultsNotifier::do_prepare_handover(Transaction& sg)
-{
- if (m_run_indices) {
- m_handover_indices = std::move(m_run_indices);
- m_run_indices = {};
- }
- else {
- m_handover_indices = {};
- }
- m_handover_transaction_version = sg.get_version_of_current_transaction();
-}
-
-bool ListResultsNotifier::prepare_to_deliver()
-{
- auto lock = lock_target();
- if (!get_realm()) {
- return false;
- }
- if (!m_handover_indices)
- return true;
-
- m_results_were_used = !m_delivered_indices;
- m_delivered_indices = std::move(m_handover_indices);
- m_delivered_transaction_version = m_handover_transaction_version;
- m_handover_indices = {};
-
- return true;
-}
-
-void ListResultsNotifier::do_attach_to(Transaction& sg)
-{
- if (m_list->is_attached())
- m_list = sg.import_copy_of(*m_list);
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp
deleted file mode 100644
index e48d92850..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp
+++ /dev/null
@@ -1,117 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_RESULTS_NOTIFIER_HPP
-#define REALM_RESULTS_NOTIFIER_HPP
-
-#include "collection_notifier.hpp"
-#include "results.hpp"
-
-#include <realm/db.hpp>
-
-namespace realm {
-namespace _impl {
-class ResultsNotifierBase : public CollectionNotifier {
-public:
- using ListIndices = util::Optional<std::vector<size_t>>;
- using CollectionNotifier::CollectionNotifier;
-
- virtual bool get_tableview(TableView&) { return false; }
- virtual bool get_list_indices(ListIndices&) { return false; }
-};
-
-class ResultsNotifier : public ResultsNotifierBase {
-public:
- ResultsNotifier(Results& target);
- bool get_tableview(TableView& out) override;
-
-private:
- std::unique_ptr<Query> m_query;
- DescriptorOrdering m_descriptor_ordering;
- bool m_target_is_in_table_order;
-
- // The TableView resulting from running the query. Will be detached unless
- // the query was (re)run since the last time the handover object was created
- TableView m_run_tv;
-
- TransactionRef m_handover_transaction;
- std::unique_ptr<TableView> m_handover_tv;
- TransactionRef m_delivered_transaction;
- std::unique_ptr<TableView> m_delivered_tv;
-
- // The table version from the last time the query was run. Used to avoid
- // rerunning the query when there's no chance of it changing.
- TableVersions m_last_seen_version;
-
- // The rows from the previous run of the query, for calculating diffs
- std::vector<int64_t> m_previous_rows;
-
- TransactionChangeInfo* m_info = nullptr;
- bool m_results_were_used = true;
-
- bool need_to_run();
- void calculate_changes();
-
- void run() override;
- void do_prepare_handover(Transaction&) override;
- bool do_add_required_change_info(TransactionChangeInfo& info) override;
- bool prepare_to_deliver() override;
-
- void release_data() noexcept override;
- void do_attach_to(Transaction& sg) override;
-};
-
-class ListResultsNotifier : public ResultsNotifierBase {
-public:
- ListResultsNotifier(Results& target);
- bool get_list_indices(ListIndices& out) override;
-
-private:
- std::shared_ptr<LstBase> m_list;
- util::Optional<bool> m_sort_order;
- bool m_distinct = false;
-
- ListIndices m_run_indices;
-
- VersionID m_handover_transaction_version;
- ListIndices m_handover_indices;
- VersionID m_delivered_transaction_version;
- ListIndices m_delivered_indices;
-
- // The rows from the previous run of the query, for calculating diffs
- std::vector<size_t> m_previous_indices;
-
- TransactionChangeInfo* m_info = nullptr;
- bool m_results_were_used = true;
-
- bool need_to_run();
- void calculate_changes();
-
- void run() override;
- void do_prepare_handover(Transaction&) override;
- bool do_add_required_change_info(TransactionChangeInfo& info) override;
- bool prepare_to_deliver() override;
-
- void release_data() noexcept override;
- void do_attach_to(Transaction& sg) override;
-};
-
-} // namespace _impl
-} // namespace realm
-
-#endif /* REALM_RESULTS_NOTIFIER_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp
deleted file mode 100644
index 918326fe8..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp
+++ /dev/null
@@ -1,564 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/transact_log_handler.hpp"
-
-#include "binding_context.hpp"
-#include "impl/collection_notifier.hpp"
-#include "index_set.hpp"
-#include "shared_realm.hpp"
-
-#include <realm/db.hpp>
-
-#include <algorithm>
-#include <numeric>
-
-using namespace realm;
-
-namespace {
-
-class KVOAdapter : public _impl::TransactionChangeInfo {
-public:
- KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context);
-
- void before(Transaction& sg);
- void after(Transaction& sg);
-
-private:
- BindingContext* m_context;
- std::vector<BindingContext::ObserverState>& m_observers;
- std::vector<void *> m_invalidated;
-
- struct ListInfo {
- BindingContext::ObserverState* observer;
- _impl::CollectionChangeBuilder builder;
- ColKey col;
- };
- std::vector<ListInfo> m_lists;
- VersionID m_version;
-};
-
-KVOAdapter::KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context)
-: _impl::TransactionChangeInfo{}
-, m_context(context)
-, m_observers(observers)
-{
- if (m_observers.empty())
- return;
-
- std::vector<TableKey> tables_needed;
- for (auto& observer : observers) {
- tables_needed.push_back(observer.table_key);
- }
- std::sort(begin(tables_needed), end(tables_needed));
- tables_needed.erase(std::unique(begin(tables_needed), end(tables_needed)),
- end(tables_needed));
-
- auto realm = context->realm.lock();
- auto& group = realm->read_group();
- for (auto& observer : observers) {
- auto table = group.get_table(TableKey(observer.table_key));
- for (auto key : table->get_column_keys()) {
- if (table->get_column_attr(key).test(col_attr_List))
- m_lists.push_back({&observer, {}, key});
- }
- }
-
- tables.reserve(tables_needed.size());
- for (auto& tbl : tables_needed)
- tables[tbl.value] = {};
- for (auto& list : m_lists)
- lists.push_back({list.observer->table_key,
- list.observer->obj_key, list.col.value, &list.builder});
-}
-
-void KVOAdapter::before(Transaction& sg)
-{
- if (!m_context)
- return;
-
- m_version = sg.get_version_of_current_transaction();
- if (tables.empty())
- return;
-
- for (auto& observer : m_observers) {
- auto it = tables.find(observer.table_key.value);
- if (it == tables.end())
- continue;
-
- auto const& table = it->second;
- auto key = observer.obj_key;
- if (table.deletions_contains(key)) {
- m_invalidated.push_back(observer.info);
- continue;
- }
- auto column_modifications = table.get_columns_modified(key);
- if (column_modifications) {
- for (auto col : *column_modifications) {
- observer.changes[col].kind = BindingContext::ColumnInfo::Kind::Set;
- }
- }
- }
-
- for (auto& list : m_lists) {
- if (list.builder.empty()) {
- // We may have pre-emptively marked the column as modified if the
- // LinkList was selected but the actual changes made ended up being
- // a no-op
- list.observer->changes.erase(list.col.value);
- continue;
- }
- // If the containing row was deleted then changes will be empty
- if (list.observer->changes.empty()) {
- REALM_ASSERT_DEBUG(tables[list.observer->table_key.value].deletions_contains(list.observer->obj_key));
- continue;
- }
- // otherwise the column should have been marked as modified
- auto it = list.observer->changes.find(list.col.value);
- REALM_ASSERT(it != list.observer->changes.end());
- auto& builder = list.builder;
- auto& changes = it->second;
-
- builder.modifications.remove(builder.insertions);
-
- // KVO can't express moves (becaue NSArray doesn't have them), so
- // transform them into a series of sets on each affected index when possible
- if (!builder.moves.empty() && builder.insertions.count() == builder.moves.size() && builder.deletions.count() == builder.moves.size()) {
- changes.kind = BindingContext::ColumnInfo::Kind::Set;
- changes.indices = builder.modifications;
- changes.indices.add(builder.deletions);
-
- // Iterate over each of the rows which may have been shifted by
- // the moves and check if it actually has been, or if it's ended
- // up in the same place as it started (either because the moves were
- // actually a swap that doesn't effect the rows in between, or the
- // combination of moves happen to leave some intermediate rows in
- // the same place)
- auto in_range = [](auto& it, auto end, size_t i) {
- if (it != end && i >= it->second)
- ++it;
- return it != end && i >= it->first && i < it->second;
- };
-
- auto del_it = builder.deletions.begin(), del_end = builder.deletions.end();
- auto ins_it = builder.insertions.begin(), ins_end = builder.insertions.end();
- size_t start = std::min(ins_it->first, del_it->first);
- size_t end = std::max(std::prev(ins_end)->second, std::prev(del_end)->second);
- ptrdiff_t shift = 0;
- for (size_t i = start; i < end; ++i) {
- if (in_range(del_it, del_end, i))
- --shift;
- else if (in_range(ins_it, ins_end, i + shift))
- ++shift;
- if (shift != 0)
- changes.indices.add(i);
- }
- }
- // KVO can't express multiple types of changes at once
- else if (builder.insertions.empty() + builder.modifications.empty() + builder.deletions.empty() < 2) {
- changes.kind = BindingContext::ColumnInfo::Kind::SetAll;
- }
- else if (!builder.insertions.empty()) {
- changes.kind = BindingContext::ColumnInfo::Kind::Insert;
- changes.indices = builder.insertions;
- }
- else if (!builder.modifications.empty()) {
- changes.kind = BindingContext::ColumnInfo::Kind::Set;
- changes.indices = builder.modifications;
- }
- else {
- REALM_ASSERT(!builder.deletions.empty());
- changes.kind = BindingContext::ColumnInfo::Kind::Remove;
- changes.indices = builder.deletions;
- }
- }
- m_context->will_change(m_observers, m_invalidated);
-}
-
-void KVOAdapter::after(Transaction& sg)
-{
- if (!m_context)
- return;
- m_context->did_change(m_observers, m_invalidated,
- m_version != VersionID{} &&
- m_version != sg.get_version_of_current_transaction());
-}
-
-class TransactLogValidationMixin {
- // The currently selected table
- TableKey m_current_table;
-
- REALM_NORETURN
- REALM_NOINLINE
- void schema_error()
- {
- throw _impl::UnsupportedSchemaChange();
- }
-
-protected:
- TableKey current_table() const noexcept { return m_current_table; }
-
-public:
-
- bool select_table(TableKey key) noexcept
- {
- m_current_table = key;
- return true;
- }
-
- // Removing or renaming things while a Realm is open is never supported
- bool erase_group_level_table(TableKey) { schema_error(); }
- bool rename_group_level_table(TableKey) { schema_error(); }
- bool erase_column(ColKey) { schema_error(); }
- bool rename_column(ColKey) { schema_error(); }
-
- // Additive changes and reorderings are supported
- bool insert_group_level_table(TableKey) { return true; }
- bool insert_column(ColKey) { return true; }
- bool set_link_type(ColKey) { return true; }
-
- // Non-schema changes are all allowed
- void parse_complete() { }
- bool create_object(ObjKey) { return true; }
- bool remove_object(ObjKey) { return true; }
- bool clear_table(size_t=0) noexcept { return true; }
- bool list_set(size_t) { return true; }
- bool list_insert(size_t) { return true; }
- bool list_erase(size_t) { return true; }
- bool list_clear(size_t) { return true; }
- bool list_move(size_t, size_t) { return true; }
- bool list_swap(size_t, size_t) { return true; }
-};
-
-
-// A transaction log handler that just validates that all operations made are
-// ones supported by the object store
-struct TransactLogValidator : public TransactLogValidationMixin {
- bool modify_object(ColKey, ObjKey) { return true; }
- bool select_list(ColKey, ObjKey) { return true; }
-};
-
-// Extends TransactLogValidator to track changes made to LinkViews
-class TransactLogObserver : public TransactLogValidationMixin {
- _impl::TransactionChangeInfo& m_info;
- _impl::CollectionChangeBuilder* m_active_list = nullptr;
- ObjectChangeSet* m_active_table = nullptr;
-
- _impl::CollectionChangeBuilder* find_list(ObjKey obj, ColKey col)
- {
- // When there are multiple source versions there could be multiple
- // change objects for a single LinkView, in which case we need to use
- // the last one
- auto table = current_table();
- for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) {
- if (it->table_key == table && it->row_key == obj.value && it->col_key == col.value)
- return it->changes;
- }
- return nullptr;
- }
-
-public:
- TransactLogObserver(_impl::TransactionChangeInfo& info)
- : m_info(info) { }
-
- void parse_complete()
- {
- for (auto& list : m_info.lists)
- list.changes->clean_up_stale_moves();
- for (auto it = m_info.tables.begin(); it != m_info.tables.end(); ) {
- if (it->second.empty())
- it = m_info.tables.erase(it);
- else
- ++it;
- }
- }
-
- bool select_table(TableKey key) noexcept
- {
- TransactLogValidationMixin::select_table(key);
-
- TableKey table_key = current_table();
- if (m_info.track_all)
- m_active_table = &m_info.tables[table_key.value];
- else {
- auto it = m_info.tables.find(table_key.value);
- if (it == m_info.tables.end())
- m_active_table = nullptr;
- else
- m_active_table = &it->second;
- }
- return true;
- }
-
- bool select_list(ColKey col, ObjKey obj)
- {
- modify_object(col, obj);
- m_active_list = find_list(obj, col);
- return true;
- }
-
- bool list_set(size_t index)
- {
- if (m_active_list)
- m_active_list->modify(index);
- return true;
- }
-
- bool list_insert(size_t index)
- {
- if (m_active_list)
- m_active_list->insert(index);
- return true;
- }
-
- bool list_erase(size_t index)
- {
- if (m_active_list)
- m_active_list->erase(index);
- return true;
- }
-
- bool list_swap(size_t index1, size_t index2)
- {
- if (m_active_list) {
- if (index1 > index2)
- std::swap(index1, index2);
- m_active_list->move(index1, index2);
- if (index1 + 1 != index2)
- m_active_list->move(index2 - 1, index1);
- }
- return true;
- }
-
- bool list_clear(size_t old_size)
- {
- if (m_active_list)
- m_active_list->clear(old_size);
- return true;
- }
-
- bool list_move(size_t from, size_t to)
- {
- if (m_active_list)
- m_active_list->move(from, to);
- return true;
- }
-
- bool create_object(ObjKey key)
- {
- if (m_active_table)
- m_active_table->insertions_add(key.value);
- return true;
- }
-
- bool remove_object(ObjKey key)
- {
- if (!m_active_table)
- return true;
- if (!m_active_table->insertions_remove(key.value))
- m_active_table->deletions_add(key.value);
- m_active_table->modifications_remove(key.value);
-
- for (size_t i = 0; i < m_info.lists.size(); ++i) {
- auto& list = m_info.lists[i];
- if (list.table_key != current_table())
- continue;
- if (list.row_key == key.value) {
- if (i + 1 < m_info.lists.size())
- m_info.lists[i] = std::move(m_info.lists.back());
- m_info.lists.pop_back();
- continue;
- }
- }
-
- return true;
- }
-
- bool modify_object(ColKey col, ObjKey key)
- {
- if (m_active_table)
- m_active_table->modifications_add(key.value, col.value);
- return true;
- }
-
- bool clear_table(size_t old_size)
- {
- auto cur_table = current_table();
- if (m_active_table)
- m_active_table->clear(old_size);
- auto it = remove_if(begin(m_info.lists), end(m_info.lists),
- [&](auto const& lv) { return lv.table_key == cur_table; });
- m_info.lists.erase(it, end(m_info.lists));
- return true;
- }
-
- bool insert_column(ColKey)
- {
- m_info.schema_changed = true;
- return true;
- }
-
- bool insert_group_level_table(TableKey)
- {
- m_info.schema_changed = true;
- return true;
- }
-};
-
-class KVOTransactLogObserver : public TransactLogObserver {
- KVOAdapter m_adapter;
- _impl::NotifierPackage& m_notifiers;
- Transaction& m_sg;
-
-public:
- KVOTransactLogObserver(std::vector<BindingContext::ObserverState>& observers,
- BindingContext* context,
- _impl::NotifierPackage& notifiers,
- Transaction& sg)
- : TransactLogObserver(m_adapter)
- , m_adapter(observers, context)
- , m_notifiers(notifiers)
- , m_sg(sg)
- {
- }
-
- ~KVOTransactLogObserver()
- {
- m_adapter.after(m_sg);
- }
-
- void parse_complete()
- {
- TransactLogObserver::parse_complete();
- m_adapter.before(m_sg);
-
- m_notifiers.package_and_wait(m_sg.get_version_of_latest_snapshot());
- m_notifiers.before_advance();
- }
-};
-
-template<typename Func>
-void advance_with_notifications(BindingContext* context,
- const std::shared_ptr<Transaction>& sg,
- Func&& func, _impl::NotifierPackage& notifiers)
-{
- auto old_version = sg->get_version_of_current_transaction();
- std::vector<BindingContext::ObserverState> observers;
- if (context) {
- observers = context->get_observed_rows();
- }
-
- // Advancing to the latest version with notifiers requires using the full
- // transaction log observer so that we have a point where we know what
- // version we're going to before we actually advance to that version
- if (observers.empty() && (!notifiers || notifiers.version())) {
- notifiers.before_advance();
- TransactLogValidator validator;
- func(&validator);
- auto new_version = sg->get_version_of_current_transaction();
- if (context && old_version != new_version)
- context->did_change({}, {});
- // did_change() could close the Realm. Just return if it does.
- if (sg->get_transact_stage() == DB::transact_Ready)
- return;
- if (context)
- context->will_send_notifications();
- // will_send_notifications() could close the Realm. Just return if it does.
- if (sg->get_transact_stage() == DB::transact_Ready)
- return;
- notifiers.after_advance();
- if (sg->get_transact_stage() == DB::transact_Ready)
- return;
- if (context)
- context->did_send_notifications();
- return;
- }
-
- if (context)
- context->will_send_notifications();
- {
- KVOTransactLogObserver observer(observers, context, notifiers, *sg);
- func(&observer);
- }
- notifiers.package_and_wait(sg->get_version_of_current_transaction().version); // is a no-op if parse_complete() was called
- notifiers.after_advance();
- if (context)
- context->did_send_notifications();
-}
-
-} // anonymous namespace
-
-namespace realm {
-namespace _impl {
-
-UnsupportedSchemaChange::UnsupportedSchemaChange()
-: std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way")
-{
-}
-
-namespace transaction {
-void advance(Transaction& tr, BindingContext*, VersionID version)
-{
- TransactLogValidator validator;
- tr.advance_read(&validator, version);
-}
-
-void advance(const std::shared_ptr<Transaction>& tr, BindingContext* context, NotifierPackage& notifiers)
-{
- advance_with_notifications(context, tr, [&](auto&&... args) {
- tr->advance_read(std::move(args)..., notifiers.version().value_or(VersionID{}));
- }, notifiers);
-}
-
-void begin(const std::shared_ptr<Transaction>& tr, BindingContext* context, NotifierPackage& notifiers)
-{
- advance_with_notifications(context, tr, [&](auto&&... args) {
- tr->promote_to_write(std::move(args)...);
- }, notifiers);
-}
-
-void cancel(Transaction& tr, BindingContext* context)
-{
- std::vector<BindingContext::ObserverState> observers;
- if (context) {
- observers = context->get_observed_rows();
- }
- if (observers.empty()) {
- tr.rollback_and_continue_as_read();
- return;
- }
-
- _impl::NotifierPackage notifiers;
- KVOTransactLogObserver o(observers, context, notifiers, tr);
- tr.rollback_and_continue_as_read(&o);
-}
-
-void advance(Transaction& tr, TransactionChangeInfo& info, VersionID version)
-{
- if (!info.track_all && info.tables.empty() && info.lists.empty()) {
- tr.advance_read(version);
- }
- else {
- TransactLogObserver o(info);
- tr.advance_read(&o, version);
- }
-}
-
-} // namespace transaction
-} // namespace _impl
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp
deleted file mode 100644
index 389ee58de..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_TRANSACT_LOG_HANDLER_HPP
-#define REALM_TRANSACT_LOG_HANDLER_HPP
-
-#include <cstdint>
-#include <stdexcept>
-#include <memory>
-
-#include <realm/version_id.hpp>
-
-namespace realm {
-class BindingContext;
-class Transaction;
-
-namespace _impl {
-class NotifierPackage;
-struct TransactionChangeInfo;
-
-struct UnsupportedSchemaChange : std::logic_error {
- UnsupportedSchemaChange();
-};
-
-namespace transaction {
-// Advance the read transaction version, with change notifications sent to delegate
-// Must not be called from within a write transaction.
-void advance(const std::shared_ptr<Transaction>& sg, BindingContext* binding_context, NotifierPackage&);
-void advance(Transaction& sg, BindingContext* binding_context, VersionID);
-
-// Begin a write transaction
-// If the read transaction version is not up to date, will first advance to the
-// most recent read transaction and sent notifications to delegate
-void begin(const std::shared_ptr<Transaction>& sg,
- BindingContext* binding_context, NotifierPackage&);
-
-// Cancel a write transaction and roll back all changes, with change notifications
-// for reverting to the old values sent to delegate
-void cancel(Transaction& sg, BindingContext* binding_context);
-
-// Advance the read transaction version, with change information gathered in info
-void advance(Transaction& sg, TransactionChangeInfo& info, VersionID version=VersionID{});
-} // namespace transaction
-} // namespace _impl
-} // namespace realm
-
-#endif /* REALM_TRANSACT_LOG_HANDLER_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp
deleted file mode 100644
index 013b22812..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "impl/weak_realm_notifier.hpp"
-
-#include "shared_realm.hpp"
-#include "util/scheduler.hpp"
-
-using namespace realm;
-using namespace realm::_impl;
-
-
-WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr<Realm>& realm)
-: m_realm(realm)
-, m_realm_key(realm.get())
-{
- bind_to_scheduler();
-}
-
-WeakRealmNotifier::~WeakRealmNotifier() = default;
-
-void WeakRealmNotifier::notify()
-{
- if (m_scheduler)
- m_scheduler->notify();
-}
-
-void WeakRealmNotifier::bind_to_scheduler()
-{
- REALM_ASSERT(!m_scheduler);
- m_scheduler = realm()->scheduler();
- if (m_scheduler) {
- m_scheduler->set_notify_callback([weak_realm = m_realm] {
- if (auto realm = weak_realm.lock()) {
- realm->notify();
- }
- });
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp
deleted file mode 100644
index 1cd0c266a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_WEAK_REALM_NOTIFIER_HPP
-#define REALM_WEAK_REALM_NOTIFIER_HPP
-
-#include <memory>
-#include <thread>
-
-namespace realm {
-class Realm;
-
-namespace util {
-class Scheduler;
-}
-
-namespace _impl {
-// WeakRealmNotifier stores a weak reference to a Realm instance, along with all of
-// the information about a Realm that needs to be accessed from other threads.
-// This is needed to avoid forming strong references to the Realm instances on
-// other threads, which can produce deadlocks when the last strong reference to
-// a Realm instance is released from within a function holding the cache lock.
-class WeakRealmNotifier {
-public:
- WeakRealmNotifier(const std::shared_ptr<Realm>& realm);
- ~WeakRealmNotifier();
-
- // Get a strong reference to the cached realm
- std::shared_ptr<Realm> realm() const { return m_realm.lock(); }
-
- // Has the Realm instance been destroyed?
- bool expired() const { return m_realm.expired(); }
-
- // Is this a WeakRealmNotifier for the given Realm instance?
- bool is_for_realm(Realm* realm) const { return realm == m_realm_key; }
-
- // Invoke m_realm.notify() on the Realm's thread via the scheduler.
- void notify();
-
- // Bind this notifier to the Realm's scheduler.
- void bind_to_scheduler();
-
-private:
- std::weak_ptr<Realm> m_realm;
- void* m_realm_key;
- std::shared_ptr<util::Scheduler> m_scheduler;
-};
-
-} // namespace _impl
-} // namespace realm
-
-#endif // REALM_WEAK_REALM_NOTIFIER_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.cpp
deleted file mode 100644
index 8f3de75f8..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "impl/external_commit_helper.hpp"
-#include "impl/realm_coordinator.hpp"
-
-#include <algorithm>
-
-using namespace realm;
-using namespace realm::_impl;
-
-static std::wstring create_condvar_sharedmemory_name(std::string realm_path) {
- std::replace(realm_path.begin(), realm_path.end(), '\\', '/');
- return L"Local\\Realm_ObjectStore_ExternalCommitHelper_SharedCondVar_" + std::wstring(realm_path.begin(), realm_path.end());
-}
-
-ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
-: m_parent(parent)
-, m_condvar_shared(create_condvar_sharedmemory_name(parent.get_path()).c_str())
-{
- m_mutex.set_shared_part(InterprocessMutex::SharedPart(), parent.get_path(), "ExternalCommitHelper_ControlMutex");
- m_commit_available.set_shared_part(m_condvar_shared.get(), parent.get_path(),
- "ExternalCommitHelper_CommitCondVar",
- std::filesystem::temp_directory_path().u8string());
- m_thread = std::async(std::launch::async, [this]() { listen(); });
-}
-
-ExternalCommitHelper::~ExternalCommitHelper()
-{
- {
- std::lock_guard<InterprocessMutex> lock(m_mutex);
- m_keep_listening = false;
- m_commit_available.notify_all();
- }
- m_thread.wait();
-
- m_commit_available.release_shared_part();
-}
-
-void ExternalCommitHelper::notify_others()
-{
- m_commit_available.notify_all();
-}
-
-void ExternalCommitHelper::listen()
-{
- std::lock_guard<InterprocessMutex> lock(m_mutex);
- while (m_keep_listening) {
- m_commit_available.wait(m_mutex, nullptr);
- if (m_keep_listening) {
- m_parent.on_change();
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp
deleted file mode 100644
index 3d4f3c6e3..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp
+++ /dev/null
@@ -1,97 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 <realm/db.hpp>
-
-#include <future>
-#include <windows.h>
-
-namespace realm {
-namespace _impl {
-class RealmCoordinator;
-
-namespace win32 {
- template <class T, void (*Initializer)(T&)>
- class SharedMemory {
- public:
- SharedMemory(LPCWSTR name) {
-#if REALM_WINDOWS
- HANDLE mapping = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(T), name);
- auto error = GetLastError();
- if (mapping != NULL)
- m_memory = reinterpret_cast<T*>(MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(T)));
-#elif REALM_UWP
- HANDLE mapping = CreateFileMappingFromApp(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, sizeof(T), name);
- auto error = GetLastError();
- if (mapping != NULL)
- m_memory = reinterpret_cast<T*>(MapViewOfFileFromApp(mapping, FILE_MAP_ALL_ACCESS, 0, sizeof(T)));
-#endif
-
- if (mapping) {
- // we can close the handle we own because the view has now referenced it
- CloseHandle(mapping);
- }
-
- try {
- if (error == 0) {
- Initializer(get());
- }
- else if (error != ERROR_ALREADY_EXISTS) {
- throw std::system_error(error, std::system_category());
- }
- }
- catch (...) {
- UnmapViewOfFile(m_memory);
- throw;
- }
- }
-
- T& get() const noexcept { return *m_memory; }
-
- ~SharedMemory() {
- UnmapViewOfFile(m_memory);
- }
- private:
- T* m_memory = nullptr;
- };
-}
-
-class ExternalCommitHelper {
-public:
- ExternalCommitHelper(RealmCoordinator& parent);
- ~ExternalCommitHelper();
-
- void notify_others();
-
-private:
- void listen();
-
- RealmCoordinator& m_parent;
-
- // The listener thread
- std::future<void> m_thread;
-
- win32::SharedMemory<InterprocessCondVar::SharedPart, InterprocessCondVar::init_shared_part> m_condvar_shared;
-
- InterprocessCondVar m_commit_available;
- InterprocessMutex m_mutex;
- bool m_keep_listening = true;
-};
-
-} // namespace _impl
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.cpp
deleted file mode 100644
index e1a3b1071..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.cpp
+++ /dev/null
@@ -1,707 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "index_set.hpp"
-
-#include <realm/util/assert.hpp>
-
-#include <algorithm>
-
-using namespace realm;
-using namespace realm::_impl;
-
-const size_t IndexSet::npos;
-
-template<typename T>
-void MutableChunkedRangeVectorIterator<T>::set(size_t front, size_t back)
-{
- this->m_outer->count -= this->m_inner->second - this->m_inner->first;
- if (this->offset() == 0) {
- this->m_outer->begin = front;
- }
- if (this->m_inner == &this->m_outer->data.back()) {
- this->m_outer->end = back;
- }
- this->m_outer->count += back - front;
- this->m_inner->first = front;
- this->m_inner->second = back;
-}
-
-template<typename T>
-void MutableChunkedRangeVectorIterator<T>::adjust(ptrdiff_t front, ptrdiff_t back)
-{
- if (this->offset() == 0) {
- this->m_outer->begin += front;
- }
- if (this->m_inner == &this->m_outer->data.back()) {
- this->m_outer->end += back;
- }
- this->m_outer->count += -front + back;
- this->m_inner->first += front;
- this->m_inner->second += back;
-}
-
-template<typename T>
-void MutableChunkedRangeVectorIterator<T>::shift(ptrdiff_t distance)
-{
- if (this->offset() == 0) {
- this->m_outer->begin += distance;
- }
- if (this->m_inner == &this->m_outer->data.back()) {
- this->m_outer->end += distance;
- }
- this->m_inner->first += distance;
- this->m_inner->second += distance;
-}
-
-void ChunkedRangeVector::push_back(value_type value)
-{
- if (!empty() && m_data.back().data.size() < max_size) {
- auto& range = m_data.back();
- REALM_ASSERT(range.end <= value.first);
-
- range.data.push_back(value);
- range.count += value.second - value.first;
- range.end = value.second;
- }
- else {
- m_data.push_back({{value}, value.first, value.second, value.second - value.first});
- }
- verify();
-}
-
-ChunkedRangeVector::iterator ChunkedRangeVector::insert(iterator pos, value_type value)
-{
- if (pos.m_outer == m_data.end()) {
- push_back(std::move(value));
- return std::prev(end());
- }
-
- pos = ensure_space(pos);
- auto& chunk = *pos.m_outer;
- pos.m_inner = &*chunk.data.insert(pos.m_outer->data.begin() + pos.offset(), value);
- chunk.count += value.second - value.first;
- chunk.begin = std::min(chunk.begin, value.first);
- chunk.end = std::max(chunk.end, value.second);
-
- verify();
- return pos;
-}
-
-ChunkedRangeVector::iterator ChunkedRangeVector::ensure_space(iterator pos)
-{
- if (pos.m_outer->data.size() + 1 <= max_size)
- return pos;
-
- auto offset = pos.offset();
-
- // Split the chunk in half to make space for the new insertion
- auto new_pos = m_data.insert(pos.m_outer + 1, Chunk{});
- auto prev = new_pos - 1;
- auto to_move = max_size / 2;
- new_pos->data.reserve(to_move);
- new_pos->data.assign(prev->data.end() - to_move, prev->data.end());
- prev->data.resize(prev->data.size() - to_move);
-
- size_t moved_count = 0;
- for (auto range : new_pos->data)
- moved_count += range.second - range.first;
-
- prev->end = prev->data.back().second;
- prev->count -= moved_count;
- new_pos->begin = new_pos->data.front().first;
- new_pos->end = new_pos->data.back().second;
- new_pos->count = moved_count;
-
- if (offset >= to_move) {
- pos.m_outer = new_pos;
- offset -= to_move;
- }
- else {
- pos.m_outer = prev;
- }
- pos.m_end = m_data.end();
- pos.m_inner = &pos.m_outer->data[offset];
- verify();
- return pos;
-}
-
-ChunkedRangeVector::iterator ChunkedRangeVector::erase(iterator pos) noexcept
-{
- auto offset = pos.offset();
- auto& chunk = *pos.m_outer;
- chunk.count -= pos->second - pos->first;
- chunk.data.erase(chunk.data.begin() + offset);
-
- if (chunk.data.size() == 0) {
- pos.m_outer = m_data.erase(pos.m_outer);
- pos.m_end = m_data.end();
- pos.m_inner = pos.m_outer == m_data.end() ? nullptr : &pos.m_outer->data.front();
- verify();
- return pos;
- }
-
- chunk.begin = chunk.data.front().first;
- chunk.end = chunk.data.back().second;
- if (offset < chunk.data.size())
- pos.m_inner = &chunk.data[offset];
- else {
- ++pos.m_outer;
- pos.m_inner = pos.m_outer == pos.m_end ? nullptr : &pos.m_outer->data.front();
- }
-
- verify();
- return pos;
-}
-
-void ChunkedRangeVector::verify() const noexcept
-{
-#ifdef REALM_DEBUG
- size_t prev_end = -1;
- for (auto range : *this) {
- REALM_ASSERT(range.first < range.second);
- REALM_ASSERT(prev_end == size_t(-1) || range.first > prev_end);
- prev_end = range.second;
- }
-
- for (auto& chunk : m_data) {
- REALM_ASSERT(!chunk.data.empty());
- REALM_ASSERT(chunk.data.front().first == chunk.begin);
- REALM_ASSERT(chunk.data.back().second == chunk.end);
- REALM_ASSERT(chunk.count <= chunk.end - chunk.begin);
- size_t count = 0;
- for (auto range : chunk.data)
- count += range.second - range.first;
- REALM_ASSERT(count == chunk.count);
- }
-#endif
-}
-
-namespace {
-class ChunkedRangeVectorBuilder {
-public:
- using value_type = std::pair<size_t, size_t>;
-
- ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected);
- void push_back(size_t index);
- void push_back(std::pair<size_t, size_t> range);
- std::vector<ChunkedRangeVector::Chunk> finalize();
-private:
- std::vector<ChunkedRangeVector::Chunk> m_data;
- size_t m_outer_pos = 0;
-};
-
-ChunkedRangeVectorBuilder::ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected)
-{
- size_t size = 0;
- for (auto const& chunk : expected.m_data)
- size += chunk.data.size();
- m_data.resize(size / ChunkedRangeVector::max_size + 1);
- for (size_t i = 0; i < m_data.size() - 1; ++i)
- m_data[i].data.reserve(ChunkedRangeVector::max_size);
-}
-
-void ChunkedRangeVectorBuilder::push_back(size_t index)
-{
- push_back({index, index + 1});
-}
-
-void ChunkedRangeVectorBuilder::push_back(std::pair<size_t, size_t> range)
-{
- auto& chunk = m_data[m_outer_pos];
- if (chunk.data.empty()) {
- chunk.data.push_back(range);
- chunk.count = range.second - range.first;
- chunk.begin = range.first;
- }
- else if (range.first == chunk.data.back().second) {
- chunk.data.back().second = range.second;
- chunk.count += range.second - range.first;
- }
- else if (chunk.data.size() < ChunkedRangeVector::max_size) {
- chunk.data.push_back(range);
- chunk.count += range.second - range.first;
- }
- else {
- chunk.end = chunk.data.back().second;
- ++m_outer_pos;
- if (m_outer_pos >= m_data.size())
- m_data.push_back({{range}, range.first, 0, 1});
- else {
- auto& chunk = m_data[m_outer_pos];
- chunk.data.push_back(range);
- chunk.begin = range.first;
- chunk.count = range.second - range.first;
- }
- }
-}
-
-std::vector<ChunkedRangeVector::Chunk> ChunkedRangeVectorBuilder::finalize()
-{
- if (!m_data.empty()) {
- m_data.resize(m_outer_pos + 1);
- if (m_data.back().data.empty())
- m_data.pop_back();
- else
- m_data.back().end = m_data.back().data.back().second;
- }
- return std::move(m_data);
-}
-}
-
-IndexSet::IndexSet(std::initializer_list<size_t> values)
-{
- for (size_t v : values)
- add(v);
-}
-
-bool IndexSet::contains(size_t index) const noexcept
-{
- auto it = const_cast<IndexSet*>(this)->find(index);
- return it != end() && it->first <= index;
-}
-
-size_t IndexSet::count(size_t start_index, size_t end_index) const noexcept
-{
- auto it = const_cast<IndexSet*>(this)->find(start_index);
- const auto end = this->end();
- if (it == end || it->first >= end_index) {
- return 0;
- }
- if (it->second >= end_index)
- return std::min(it->second, end_index) - std::max(it->first, start_index);
-
- size_t ret = 0;
-
- if (start_index > it->first || it.offset() != 0) {
- // Start index is in the middle of a chunk, so start by counting the
- // rest of that chunk
- ret = it->second - std::max(it->first, start_index);
- for (++it; it != end && it->second < end_index && it.offset() != 0; ++it) {
- ret += it->second - it->first;
- }
- if (it != end && it->first < end_index && it.offset() != 0)
- ret += end_index - it->first;
- if (it == end || it->second >= end_index)
- return ret;
- }
-
- // Now count all complete chunks that fall within the range
- while (it != end && it.outer()->end <= end_index) {
- REALM_ASSERT_DEBUG(it.offset() == 0);
- ret += it.outer()->count;
- it.next_chunk();
- }
-
- // Cound all complete ranges within the last chunk
- while (it != end && it->second <= end_index) {
- ret += it->second - it->first;
- ++it;
- }
-
- // And finally add in the partial last range
- if (it != end && it->first < end_index)
- ret += end_index - it->first;
- return ret;
-}
-
-IndexSet::iterator IndexSet::find(size_t index) noexcept
-{
- return find(index, begin());
-}
-
-IndexSet::iterator IndexSet::find(size_t index, iterator begin) noexcept
-{
- auto it = std::find_if(begin.outer(), m_data.end(),
- [&](auto const& lft) { return lft.end > index; });
- if (it == m_data.end())
- return end();
- if (index < it->begin)
- return iterator(it, m_data.end(), &it->data[0]);
- auto inner_begin = it->data.begin();
- if (it == begin.outer())
- inner_begin += begin.offset();
- auto inner = std::lower_bound(inner_begin, it->data.end(), index,
- [&](auto const& lft, auto) { return lft.second <= index; });
- REALM_ASSERT_DEBUG(inner != it->data.end());
-
- return iterator(it, m_data.end(), &*inner);
-}
-
-void IndexSet::add(size_t index)
-{
- do_add(find(index), index);
-}
-
-void IndexSet::add(IndexSet const& other)
-{
- auto it = begin();
- for (size_t index : other.as_indexes()) {
- it = do_add(find(index, it), index);
- }
-}
-
-size_t IndexSet::add_shifted(size_t index)
-{
- iterator it = begin(), end = this->end();
-
- // Shift for any complete chunks before the target
- for (; it != end && it.outer()->end <= index; it.next_chunk())
- index += it.outer()->count;
-
- // And any ranges within the last partial chunk
- for (; it != end && it->first <= index; ++it)
- index += it->second - it->first;
-
- do_add(it, index);
- return index;
-}
-
-void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values)
-{
- if (values.empty())
- return;
-
-#ifdef REALM_DEBUG
- size_t expected = std::distance(as_indexes().begin(), as_indexes().end());
- for (auto index : values.as_indexes()) {
- if (!shifted_by.contains(index))
- ++expected;
- }
-#endif
-
- ChunkedRangeVectorBuilder builder(*this);
-
- auto old_it = cbegin(), old_end = cend();
- auto shift_it = shifted_by.cbegin(), shift_end = shifted_by.cend();
-
- size_t skip_until = 0;
- size_t old_shift = 0;
- size_t new_shift = 0;
- for (size_t index : values.as_indexes()) {
- for (; shift_it != shift_end && shift_it->first <= index; ++shift_it) {
- new_shift += shift_it->second - shift_it->first;
- skip_until = shift_it->second;
- }
- if (index < skip_until)
- continue;
-
- for (; old_it != old_end && old_it->first <= index - new_shift + old_shift; ++old_it) {
- for (size_t i = old_it->first; i < old_it->second; ++i)
- builder.push_back(i);
- old_shift += old_it->second - old_it->first;
- }
-
- REALM_ASSERT(index >= new_shift);
- builder.push_back(index - new_shift + old_shift);
- }
-
- copy(old_it, old_end, std::back_inserter(builder));
- m_data = builder.finalize();
-
-#ifdef REALM_DEBUG
- REALM_ASSERT((size_t)std::distance(as_indexes().begin(), as_indexes().end()) == expected);
-#endif
-}
-
-void IndexSet::set(size_t len)
-{
- clear();
- if (len) {
- push_back({0, len});
- }
-}
-
-void IndexSet::insert_at(size_t index, size_t count)
-{
- REALM_ASSERT(count > 0);
-
- auto pos = find(index);
- auto end = this->end();
- bool in_existing = false;
- if (pos != end) {
- if (pos->first <= index) {
- in_existing = true;
- pos.adjust(0, count);
- }
- else {
- pos.shift(count);
- }
- for (auto it = std::next(pos); it != end; ++it)
- it.shift(count);
- }
- if (!in_existing) {
- for (size_t i = 0; i < count; ++i)
- pos = std::next(do_add(pos, index + i));
- }
-
- verify();
-}
-
-void IndexSet::insert_at(IndexSet const& positions)
-{
- if (positions.empty())
- return;
- if (empty()) {
- *this = positions;
- return;
- }
-
- IndexIterator begin1 = cbegin(), begin2 = positions.cbegin();
- IndexIterator end1 = cend(), end2 = positions.cend();
-
- ChunkedRangeVectorBuilder builder(*this);
- size_t shift = 0;
- while (begin1 != end1 && begin2 != end2) {
- if (*begin1 + shift < *begin2) {
- builder.push_back(*begin1++ + shift);
- }
- else {
- ++shift;
- builder.push_back(*begin2++);
- }
- }
- for (; begin1 != end1; ++begin1)
- builder.push_back(*begin1 + shift);
- for (; begin2 != end2; ++begin2)
- builder.push_back(*begin2);
-
- m_data = builder.finalize();
-}
-
-void IndexSet::shift_for_insert_at(size_t index, size_t count)
-{
- REALM_ASSERT(count > 0);
-
- auto it = find(index);
- if (it == end())
- return;
-
- for (auto pos = it, end = this->end(); pos != end; ++pos)
- pos.shift(count);
-
- // If the range contained the insertion point, split the range and move
- // the part of it before the insertion point back
- if (it->first < index + count) {
- auto old_second = it->second;
- it.set(it->first - count, index);
- insert(std::next(it), {index + count, old_second});
- }
- verify();
-}
-
-void IndexSet::shift_for_insert_at(realm::IndexSet const& values)
-{
- if (empty() || values.empty())
- return;
- if (values.m_data.front().begin >= m_data.back().end)
- return;
-
- IndexIterator begin1 = cbegin(), begin2 = values.cbegin();
- IndexIterator end1 = cend(), end2 = values.cend();
-
- ChunkedRangeVectorBuilder builder(*this);
- size_t shift = 0;
- while (begin1 != end1 && begin2 != end2) {
- if (*begin1 + shift < *begin2) {
- builder.push_back(*begin1++ + shift);
- }
- else {
- ++shift;
- begin2++;
- }
- }
- for (; begin1 != end1; ++begin1)
- builder.push_back(*begin1 + shift);
-
- m_data = builder.finalize();
-}
-
-void IndexSet::erase_at(size_t index)
-{
- auto it = find(index);
- if (it != end())
- do_erase(it, index);
-}
-
-void IndexSet::erase_at(IndexSet const& positions)
-{
- if (empty() || positions.empty())
- return;
-
- ChunkedRangeVectorBuilder builder(*this);
-
- IndexIterator begin1 = cbegin(), begin2 = positions.cbegin();
- IndexIterator end1 = cend(), end2 = positions.cend();
-
- size_t shift = 0;
- while (begin1 != end1 && begin2 != end2) {
- if (*begin1 < *begin2) {
- builder.push_back(*begin1++ - shift);
- }
- else if (*begin1 == *begin2) {
- ++shift;
- ++begin1;
- ++begin2;
- }
- else {
- ++shift;
- ++begin2;
- }
- }
- for (; begin1 != end1; ++begin1)
- builder.push_back(*begin1 - shift);
-
- m_data = builder.finalize();
-}
-
-size_t IndexSet::erase_or_unshift(size_t index)
-{
- auto shifted = index;
- iterator it = begin(), end = this->end();
-
- // Shift for any complete chunks before the target
- for (; it != end && it.outer()->end <= index; it.next_chunk())
- shifted -= it.outer()->count;
-
- // And any ranges within the last partial chunk
- for (; it != end && it->second <= index; ++it)
- shifted -= it->second - it->first;
-
- if (it == end)
- return shifted;
-
- if (it->first <= index)
- shifted = npos;
-
- do_erase(it, index);
-
- return shifted;
-}
-
-void IndexSet::do_erase(iterator it, size_t index)
-{
- if (it->first <= index) {
- if (it->first + 1 == it->second) {
- it = erase(it);
- }
- else {
- it.adjust(0, -1);
- ++it;
- }
- }
- else if (it != begin() && std::prev(it)->second + 1 == it->first) {
- std::prev(it).adjust(0, it->second - it->first);
- it = erase(it);
- }
-
- for (; it != end(); ++it)
- it.shift(-1);
-}
-
-IndexSet::iterator IndexSet::do_remove(iterator it, size_t begin, size_t end)
-{
- for (it = find(begin, it); it != this->end() && it->first < end; it = find(begin, it)) {
- // Trim off any part of the range to remove that's before the matching range
- begin = std::max(it->first, begin);
-
- // If the matching range extends to both sides of the range to remove,
- // split it on the range to remove
- if (it->first < begin && it->second > end) {
- auto old_second = it->second;
- it.set(it->first, begin);
- it = std::prev(insert(std::next(it), {end, old_second}));
- }
- // Range to delete now coverages (at least) one end of the matching range
- else if (begin == it->first && end >= it->second)
- it = erase(it);
- else if (begin == it->first)
- it.set(end, it->second);
- else
- it.set(it->first, begin);
- }
- return it;
-}
-
-void IndexSet::remove(size_t index, size_t count)
-{
- do_remove(find(index), index, index + count);
-}
-
-void IndexSet::remove(realm::IndexSet const& values)
-{
- auto it = begin();
- for (auto range : values) {
- it = do_remove(it, range.first, range.second);
- if (it == end())
- return;
- }
-}
-
-size_t IndexSet::shift(size_t index) const noexcept
-{
- // FIXME: optimize
- for (auto range : *this) {
- if (range.first > index)
- break;
- index += range.second - range.first;
- }
- return index;
-}
-
-size_t IndexSet::unshift(size_t index) const noexcept
-{
- REALM_ASSERT_DEBUG(!contains(index));
- return index - count(0, index);
-}
-
-void IndexSet::clear() noexcept
-{
- m_data.clear();
-}
-
-IndexSet::iterator IndexSet::do_add(iterator it, size_t index)
-{
- verify();
- bool more_before = it != begin(), valid = it != end();
- REALM_ASSERT(!more_before || index >= std::prev(it)->second);
- if (valid && it->first <= index && it->second > index) {
- // index is already in set
- return it;
- }
- if (more_before && std::prev(it)->second == index) {
- auto prev = std::prev(it);
- // index is immediately after an existing range
- prev.adjust(0, 1);
-
- if (valid && prev->second == it->first) {
- // index joins two existing ranges
- prev.adjust(0, it->second - it->first);
- return std::prev(erase(it));
- }
- return prev;
- }
- if (valid && it->first == index + 1) {
- // index is immediately before an existing range
- it.adjust(-1, 0);
- return it;
- }
-
- // index is not next to an existing range
- return insert(it, {index, index + 1});
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.hpp
deleted file mode 100644
index 55bedcc15..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/index_set.hpp
+++ /dev/null
@@ -1,325 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_INDEX_SET_HPP
-#define REALM_INDEX_SET_HPP
-
-#include <cstddef>
-#include <initializer_list>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-namespace realm {
-namespace _impl {
-template<typename OuterIterator>
-class MutableChunkedRangeVectorIterator;
-
-// An iterator for ChunkedRangeVector, templated on the vector iterator/const_iterator
-template<typename OuterIterator>
-class ChunkedRangeVectorIterator {
-public:
- using iterator_category = std::bidirectional_iterator_tag;
- using value_type = typename std::remove_reference<decltype(*OuterIterator()->data.begin())>::type;
- using difference_type = ptrdiff_t;
- using pointer = const value_type*;
- using reference = const value_type&;
-
- ChunkedRangeVectorIterator(OuterIterator outer, OuterIterator end, value_type* inner)
- : m_outer(outer), m_end(end), m_inner(inner) { }
-
- reference operator*() const noexcept { return *m_inner; }
- pointer operator->() const noexcept { return m_inner; }
-
- template<typename Other> bool operator==(Other const& it) const noexcept;
- template<typename Other> bool operator!=(Other const& it) const noexcept;
-
- ChunkedRangeVectorIterator& operator++() noexcept;
- ChunkedRangeVectorIterator operator++(int) noexcept;
-
- ChunkedRangeVectorIterator& operator--() noexcept;
- ChunkedRangeVectorIterator operator--(int) noexcept;
-
- // Advance directly to the next outer block
- void next_chunk() noexcept;
-
- OuterIterator outer() const noexcept { return m_outer; }
- size_t offset() const noexcept { return m_inner - &m_outer->data[0]; }
-
-private:
- OuterIterator m_outer;
- OuterIterator m_end;
- value_type* m_inner;
- friend struct ChunkedRangeVector;
- friend class MutableChunkedRangeVectorIterator<OuterIterator>;
-};
-
-// A mutable iterator that adds some invariant-preserving mutation methods
-template<typename OuterIterator>
-class MutableChunkedRangeVectorIterator : public ChunkedRangeVectorIterator<OuterIterator> {
-public:
- using ChunkedRangeVectorIterator<OuterIterator>::ChunkedRangeVectorIterator;
-
- // Set this iterator to the given range and update the parent if needed
- void set(size_t begin, size_t end);
- // Adjust the begin and end of this iterator by the given amounts and
- // update the parent if needed
- void adjust(ptrdiff_t front, ptrdiff_t back);
- // Shift this iterator by the given amount and update the parent if needed
- void shift(ptrdiff_t distance);
-};
-
-// A vector which stores ranges in chunks with a maximum size
-struct ChunkedRangeVector {
- struct Chunk {
- std::vector<std::pair<size_t, size_t>> data;
- size_t begin;
- size_t end;
- size_t count;
- };
- std::vector<Chunk> m_data;
-
- using value_type = std::pair<size_t, size_t>;
- using iterator = MutableChunkedRangeVectorIterator<typename decltype(m_data)::iterator>;
- using const_iterator = ChunkedRangeVectorIterator<typename decltype(m_data)::const_iterator>;
-
-#ifdef REALM_DEBUG
- static const size_t max_size = 4;
-#else
- static const size_t max_size = 4096 / sizeof(std::pair<size_t, size_t>);
-#endif
-
- iterator begin() noexcept { return empty() ? end() : iterator(m_data.begin(), m_data.end(), &m_data[0].data[0]); }
- iterator end() noexcept { return iterator(m_data.end(), m_data.end(), nullptr); }
- const_iterator begin() const noexcept { return cbegin(); }
- const_iterator end() const noexcept { return cend(); }
- const_iterator cbegin() const noexcept { return empty() ? cend() : const_iterator(m_data.cbegin(), m_data.end(), &m_data[0].data[0]); }
- const_iterator cend() const noexcept { return const_iterator(m_data.end(), m_data.end(), nullptr); }
-
- bool empty() const noexcept { return m_data.empty(); }
-
- iterator insert(iterator pos, value_type value);
- iterator erase(iterator pos) noexcept;
- void push_back(value_type value);
- iterator ensure_space(iterator pos);
-
- void verify() const noexcept;
-};
-} // namespace _impl
-
-class IndexSet : private _impl::ChunkedRangeVector {
-public:
- static const size_t npos = -1;
-
- using ChunkedRangeVector::value_type;
- using ChunkedRangeVector::iterator;
- using ChunkedRangeVector::const_iterator;
- using ChunkedRangeVector::begin;
- using ChunkedRangeVector::end;
- using ChunkedRangeVector::empty;
- using ChunkedRangeVector::verify;
-
- IndexSet() = default;
- IndexSet(std::initializer_list<size_t>);
-
- // Check if the index set contains the given index
- bool contains(size_t index) const noexcept;
-
- // Counts the number of indices in the set in the given range
- size_t count(size_t start_index=0, size_t end_index=-1) const noexcept;
-
- // Add an index to the set, doing nothing if it's already present
- void add(size_t index);
- void add(IndexSet const& is);
-
- // Add an index which has had all of the ranges in the set before it removed
- // Returns the unshifted index
- size_t add_shifted(size_t index);
- // Add indexes which have had the ranges in `shifted_by` added and the ranges
- // in the current set removed
- void add_shifted_by(IndexSet const& shifted_by, IndexSet const& values);
-
- // Remove all indexes from the set and then add a single range starting from
- // zero with the given length
- void set(size_t len);
-
- // Insert an index at the given position, shifting existing indexes at or
- // after that point back by one
- void insert_at(size_t index, size_t count=1);
- void insert_at(IndexSet const&);
-
- // Shift indexes at or after the given point back by one
- void shift_for_insert_at(size_t index, size_t count=1);
- void shift_for_insert_at(IndexSet const&);
-
- // Delete an index at the given position, shifting indexes after that point
- // forward by one
- void erase_at(size_t index);
- void erase_at(IndexSet const&);
-
- // If the given index is in the set remove it and return npos; otherwise unshift() it
- size_t erase_or_unshift(size_t index);
-
- // Remove the indexes at the given index from the set, without shifting
- void remove(size_t index, size_t count=1);
- void remove(IndexSet const&);
-
- // Shift an index by inserting each of the indexes in this set
- size_t shift(size_t index) const noexcept;
- // Shift an index by deleting each of the indexes in this set
- size_t unshift(size_t index) const noexcept;
-
- // Remove all indexes from the set
- void clear() noexcept;
-
- // An iterator over the individual indices in the set rather than the ranges
- class IndexIterator : public std::iterator<std::forward_iterator_tag, size_t> {
- public:
- IndexIterator(IndexSet::const_iterator it) : m_iterator(it) { }
- size_t operator*() const noexcept { return m_iterator->first + m_offset; }
- bool operator==(IndexIterator const& it) const noexcept { return m_iterator == it.m_iterator; }
- bool operator!=(IndexIterator const& it) const noexcept { return m_iterator != it.m_iterator; }
-
- IndexIterator& operator++() noexcept
- {
- ++m_offset;
- if (m_iterator->first + m_offset == m_iterator->second) {
- ++m_iterator;
- m_offset = 0;
- }
- return *this;
- }
-
- IndexIterator operator++(int) noexcept
- {
- auto value = *this;
- ++*this;
- return value;
- }
-
- private:
- IndexSet::const_iterator m_iterator;
- size_t m_offset = 0;
- };
-
- class IndexIteratableAdaptor {
- public:
- using value_type = size_t;
- using iterator = IndexIterator;
- using const_iterator = iterator;
-
- const_iterator begin() const noexcept { return m_index_set.begin(); }
- const_iterator end() const noexcept { return m_index_set.end(); }
-
- IndexIteratableAdaptor(IndexSet const& is) : m_index_set(is) { }
- private:
- IndexSet const& m_index_set;
- };
-
- IndexIteratableAdaptor as_indexes() const noexcept { return *this; }
-
-private:
- // Find the range which contains the index, or the first one after it if
- // none do
- iterator find(size_t index) noexcept;
- iterator find(size_t index, iterator it) noexcept;
- // Insert the index before the given position, combining existing ranges as
- // applicable
- // returns inserted position
- iterator do_add(iterator pos, size_t index);
- void do_erase(iterator it, size_t index);
- iterator do_remove(iterator it, size_t index, size_t count);
-
- void shift_until_end_by(iterator begin, ptrdiff_t shift);
-};
-
-namespace util {
-// This was added in C++14 but is missing from libstdc++ 4.9
-template<typename Iterator>
-std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) noexcept
-{
- return std::reverse_iterator<Iterator>(it);
-}
-} // namespace util
-
-
-namespace _impl {
-template<typename T>
-template<typename OtherIterator>
-inline bool ChunkedRangeVectorIterator<T>::operator==(OtherIterator const& it) const noexcept
-{
- return m_outer == it.outer() && m_inner == it.operator->();
-}
-
-template<typename T>
-template<typename OtherIterator>
-inline bool ChunkedRangeVectorIterator<T>::operator!=(OtherIterator const& it) const noexcept
-{
- return !(*this == it);
-}
-
-template<typename T>
-inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator++() noexcept
-{
- ++m_inner;
- if (offset() == m_outer->data.size())
- next_chunk();
- return *this;
-}
-
-template<typename T>
-inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator++(int) noexcept
-{
- auto value = *this;
- ++*this;
- return value;
-}
-
-template<typename T>
-inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator--() noexcept
-{
- if (!m_inner || m_inner == &m_outer->data.front()) {
- --m_outer;
- m_inner = &m_outer->data.back();
- }
- else {
- --m_inner;
- }
- return *this;
-}
-
-template<typename T>
-inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator--(int) noexcept
-{
- auto value = *this;
- --*this;
- return value;
-}
-
-template<typename T>
-inline void ChunkedRangeVectorIterator<T>::next_chunk() noexcept
-{
- ++m_outer;
- m_inner = m_outer != m_end ? &m_outer->data[0] : nullptr;
-}
-} // namespace _impl
-
-} // namespace realm
-
-#endif // REALM_INDEX_SET_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp
deleted file mode 100644
index b6382bbd4..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp
+++ /dev/null
@@ -1,507 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "list.hpp"
-
-#include "impl/list_notifier.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "shared_realm.hpp"
-
-namespace {
-using namespace realm;
-
-template<typename T>
-struct ListType {
- using type = Lst<T>;
-};
-
-template<>
-struct ListType<Obj> {
- using type = LnkLst;
-};
-
-}
-
-namespace realm {
-using namespace _impl;
-
-List::List() noexcept = default;
-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 Obj& parent_obj, ColKey col)
-: m_realm(std::move(r))
-, m_type(ObjectSchema::from_core_type(*parent_obj.get_table(), col) & ~PropertyType::Array)
-, m_list_base(parent_obj.get_listbase_ptr(col))
-{
-}
-
-List::List(std::shared_ptr<Realm> r, const LstBase& list)
-: m_realm(std::move(r))
-, m_type(ObjectSchema::from_core_type(*list.get_table(), list.get_col_key()) & ~PropertyType::Array)
-, m_list_base(list.clone())
-{
-}
-
-static StringData object_name(Table const& table)
-{
- return ObjectStore::object_type_for_table_name(table.get_name());
-}
-
-ObjectSchema const& List::get_object_schema() const
-{
- verify_attached();
-
- REALM_ASSERT(get_type() == PropertyType::Object);
- auto object_schema = m_object_schema.load();
- if (!object_schema) {
- auto object_type = object_name(*static_cast<LnkLst&>(*m_list_base).get_target_table());
- auto it = m_realm->schema().find(object_type);
- REALM_ASSERT(it != m_realm->schema().end());
- m_object_schema = object_schema = &*it;
- }
- return *object_schema;
-}
-
-Query List::get_query() const
-{
- verify_attached();
- if (m_type == PropertyType::Object)
- return static_cast<LnkLst&>(*m_list_base).get_target_table()->where(as<Obj>());
- throw std::runtime_error("not implemented");
-}
-
-ObjKey List::get_parent_object_key() const
-{
- verify_attached();
- return m_list_base->get_key();
-}
-
-ColKey List::get_parent_column_key() const
-{
- verify_attached();
- return m_list_base->get_col_key();
-}
-
-TableKey List::get_parent_table_key() const
-{
- verify_attached();
- return m_list_base->get_table()->get_key();
-}
-
-void List::verify_valid_row(size_t row_ndx, bool insertion) const
-{
- size_t s = size();
- if (row_ndx > s || (!insertion && row_ndx == s)) {
- throw OutOfBoundsIndexException{row_ndx, s + insertion};
- }
-}
-
-void List::validate(const Obj& obj) const
-{
- if (!obj.is_valid())
- throw std::invalid_argument("Object has been deleted or invalidated");
- auto target = static_cast<LnkLst&>(*m_list_base).get_target_table();
- if (obj.get_table() != target)
- throw std::invalid_argument(util::format("Object of type (%1) does not match List type (%2)",
- object_name(*obj.get_table()),
- object_name(*target)));
-}
-
-bool List::is_valid() const
-{
- if (!m_realm)
- return false;
- m_realm->verify_thread();
- if (!m_realm->is_in_read_transaction())
- return false;
- return m_list_base->is_attached();
-}
-
-void List::verify_attached() const
-{
- if (!is_valid()) {
- throw InvalidatedException();
- }
-}
-
-void List::verify_in_transaction() const
-{
- verify_attached();
- m_realm->verify_in_write();
-}
-
-size_t List::size() const
-{
- verify_attached();
- return m_list_base->size();
-}
-
-template<typename T>
-T List::get(size_t row_ndx) const
-{
- verify_valid_row(row_ndx);
- return as<T>().get(row_ndx);
-}
-
-template<>
-Obj List::get(size_t row_ndx) const
-{
- verify_valid_row(row_ndx);
- auto& list = as<Obj>();
- return list.get_target_table()->get_object(list.get(row_ndx));
-}
-
-template<typename T>
-size_t List::find(T const& value) const
-{
- verify_attached();
- return as<T>().find_first(value);
-}
-
-template<>
-size_t List::find(Obj const& o) const
-{
- verify_attached();
- if (!o.is_valid())
- return not_found;
- validate(o);
-
- return as<Obj>().ConstLstIf<ObjKey>::find_first(o.get_key());
-}
-
-size_t List::find(Query&& q) const
-{
- verify_attached();
- if (m_type == PropertyType::Object) {
- ObjKey key = get_query().and_query(std::move(q)).find();
- return key ? as<Obj>().ConstLstIf<ObjKey>::find_first(key) : not_found;
- }
- throw std::runtime_error("not implemented");
-}
-
-template<typename T>
-void List::add(T value)
-{
- verify_in_transaction();
- as<T>().add(value);
-}
-
-template<>
-void List::add(Obj o)
-{
- verify_in_transaction();
- validate(o);
- as<Obj>().add(o.get_key());
-}
-
-template<typename T>
-void List::insert(size_t row_ndx, T value)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx, true);
- as<T>().insert(row_ndx, value);
-}
-
-template<>
-void List::insert(size_t row_ndx, Obj o)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx, true);
- validate(o);
- as<Obj>().insert(row_ndx, o.get_key());
-}
-
-void List::move(size_t source_ndx, size_t dest_ndx)
-{
- verify_in_transaction();
- verify_valid_row(source_ndx);
- verify_valid_row(dest_ndx); // Can't be one past end due to removing one earlier
- if (source_ndx == dest_ndx)
- return;
-
- m_list_base->move(source_ndx, dest_ndx);
-}
-
-void List::remove(size_t row_ndx)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx);
- m_list_base->remove(row_ndx, row_ndx + 1);
-}
-
-void List::remove_all()
-{
- verify_in_transaction();
- m_list_base->clear();
-}
-
-template<typename T>
-void List::set(size_t row_ndx, T value)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx);
-// validate(row);
- as<T>().set(row_ndx, value);
-}
-
-template<>
-void List::set(size_t row_ndx, Obj o)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx);
- validate(o);
- as<Obj>().set(row_ndx, o.get_key());
-}
-
-void List::swap(size_t ndx1, size_t ndx2)
-{
- verify_in_transaction();
- verify_valid_row(ndx1);
- verify_valid_row(ndx2);
- m_list_base->swap(ndx1, ndx2);
-}
-
-void List::delete_at(size_t row_ndx)
-{
- verify_in_transaction();
- verify_valid_row(row_ndx);
- if (m_type == PropertyType::Object)
- as<Obj>().remove_target_row(row_ndx);
- else
- m_list_base->remove(row_ndx, row_ndx + 1);
-}
-
-void List::delete_all()
-{
- verify_in_transaction();
- if (m_type == PropertyType::Object)
- as<Obj>().remove_all_target_rows();
- else
- m_list_base->clear();
-}
-
-Results List::sort(SortDescriptor order) const
-{
- verify_attached();
- if ((m_type == PropertyType::Object)) {
- return Results(m_realm, std::dynamic_pointer_cast<LnkLst>(m_list_base), util::none, std::move(order));
- }
- else {
- DescriptorOrdering o;
- o.append_sort(order);
- return Results(m_realm, m_list_base, std::move(o));
- }
-}
-
-Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
-{
- return as_results().sort(keypaths);
-}
-
-Results List::filter(Query q) const
-{
- verify_attached();
- return Results(m_realm, std::dynamic_pointer_cast<LnkLst>(m_list_base), get_query().and_query(std::move(q)));
-}
-
-Results List::as_results() const
-{
- verify_attached();
- return m_type == PropertyType::Object
- ? Results(m_realm, std::static_pointer_cast<LnkLst>(m_list_base))
- : Results(m_realm, m_list_base);
-}
-
-Results List::snapshot() const
-{
- return as_results().snapshot();
-}
-
-// The simpler definition of void_t below does not work in gcc 4.9 due to a bug
-// in that version of gcc (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64395)
-
-// template<class...> using VoidT = void;
-namespace _impl {
- template<class... > struct MakeVoid { using type = void; };
-}
-template<class... T> using VoidT = typename _impl::MakeVoid<T...>::type;
-
-template<class, class = VoidT<>>
-struct HasMinmaxType : std::false_type { };
-template<class T>
-struct HasMinmaxType<T, VoidT<typename ColumnTypeTraits<T>::minmax_type>> : std::true_type { };
-
-template<class, class = VoidT<>>
-struct HasSumType : std::false_type { };
-template<class T>
-struct HasSumType<T, VoidT<typename ColumnTypeTraits<T>::sum_type>> : std::true_type { };
-
-template<bool cond>
-struct If;
-
-template<>
-struct If<true> {
- template<typename T, typename Then, typename Else>
- static auto call(T self, Then&& fn, Else&&) { return fn(self); }
-};
-template<>
-struct If<false> {
- template<typename T, typename Then, typename Else>
- static auto call(T, Then&&, Else&& fn) { return fn(); }
-};
-
-util::Optional<Mixed> List::max(ColKey col) const
-{
- if (get_type() == PropertyType::Object)
- return as_results().max(col);
- size_t out_ndx = not_found;
- auto result = m_list_base->max(&out_ndx);
- if (result.is_null()) {
- throw realm::Results::UnsupportedColumnTypeException(m_list_base->get_col_key(), m_list_base->get_table(), "max");
- }
- return out_ndx == not_found ? none : make_optional(result);
-}
-
-util::Optional<Mixed> List::min(ColKey col) const
-{
- if (get_type() == PropertyType::Object)
- return as_results().min(col);
-
- size_t out_ndx = not_found;
- auto result = m_list_base->min(&out_ndx);
- if (result.is_null()) {
- throw realm::Results::UnsupportedColumnTypeException(m_list_base->get_col_key(), m_list_base->get_table(), "min");
- }
- return out_ndx == not_found ? none : make_optional(result);
-}
-
-Mixed List::sum(ColKey col) const
-{
- if (get_type() == PropertyType::Object)
- return *as_results().sum(col);
-
- auto result = m_list_base->sum();
- if (result.is_null()) {
- throw realm::Results::UnsupportedColumnTypeException(m_list_base->get_col_key(), m_list_base->get_table(), "sum");
- }
- return result;
-}
-
-util::Optional<double> List::average(ColKey col) const
-{
- if (get_type() == PropertyType::Object)
- return as_results().average(col);
- size_t count = 0;
- auto result = m_list_base->avg(&count);
- if (result.is_null()) {
- throw realm::Results::UnsupportedColumnTypeException(m_list_base->get_col_key(), m_list_base->get_table(), "average");
- }
- return count == 0 ? none : make_optional(result.get_double());
-}
-
-bool List::operator==(List const& rgt) const noexcept
-{
- return m_list_base->get_table() == rgt.m_list_base->get_table()
- && m_list_base->get_key() == rgt.m_list_base->get_key()
- && m_list_base->get_col_key() == rgt.m_list_base->get_col_key();
-}
-
-NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
-{
- verify_attached();
- m_realm->verify_notifications_available();
- // Adding a new callback to a notifier which had all of its callbacks
- // removed does not properly reinitialize the notifier. Work around this by
- // recreating it instead.
- // FIXME: The notifier lifecycle here is dumb (when all callbacks are removed
- // from a notifier a zombie is left sitting around uselessly) and should be
- // cleaned up.
- if (m_notifier && !m_notifier->have_callbacks())
- m_notifier.reset();
- if (!m_notifier) {
- m_notifier = std::make_shared<ListNotifier>(m_realm, *m_list_base, m_type);
- RealmCoordinator::register_notifier(m_notifier);
- }
- return {m_notifier, m_notifier->add_callback(std::move(cb))};
-}
-
-List List::freeze(std::shared_ptr<Realm> const& frozen_realm) const
-{
- return List(frozen_realm, *frozen_realm->import_copy_of(*m_list_base));
-}
-
-bool List::is_frozen() const noexcept
-{
- return m_realm->is_frozen();
-}
-
-List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
-: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1))
-, requested(r), valid_count(c) {}
-
-#define REALM_PRIMITIVE_LIST_TYPE(T) \
- template T List::get<T>(size_t) const; \
- template size_t List::find<T>(T const&) const; \
- template void List::add<T>(T); \
- template void List::insert<T>(size_t, T); \
- template void List::set<T>(size_t, T);
-
-REALM_PRIMITIVE_LIST_TYPE(bool)
-REALM_PRIMITIVE_LIST_TYPE(int64_t)
-REALM_PRIMITIVE_LIST_TYPE(float)
-REALM_PRIMITIVE_LIST_TYPE(double)
-REALM_PRIMITIVE_LIST_TYPE(StringData)
-REALM_PRIMITIVE_LIST_TYPE(BinaryData)
-REALM_PRIMITIVE_LIST_TYPE(Timestamp)
-REALM_PRIMITIVE_LIST_TYPE(ObjKey)
-REALM_PRIMITIVE_LIST_TYPE(util::Optional<bool>)
-REALM_PRIMITIVE_LIST_TYPE(util::Optional<int64_t>)
-REALM_PRIMITIVE_LIST_TYPE(util::Optional<float>)
-REALM_PRIMITIVE_LIST_TYPE(util::Optional<double>)
-
-#undef REALM_PRIMITIVE_LIST_TYPE
-} // namespace realm
-
-namespace {
-size_t hash_combine() { return 0; }
-template<typename T, typename... Rest>
-size_t hash_combine(const T& v, Rest... rest)
-{
- size_t h = hash_combine(rest...);
- h ^= std::hash<T>()(v) + 0x9e3779b9 + (h<<6) + (h>>2);
- return h;
-}
-}
-
-namespace std {
-size_t hash<List>::operator()(List const& list) const
-{
- auto& impl = *list.m_list_base;
- return hash_combine(impl.get_key().value, impl.get_table()->get_key().value,
- impl.get_col_key().value);
-}
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp
deleted file mode 100644
index fb7a751c2..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp
+++ /dev/null
@@ -1,319 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OS_LIST_HPP
-#define REALM_OS_LIST_HPP
-
-#include "collection_notifications.hpp"
-#include "impl/collection_notifier.hpp"
-#include "object.hpp"
-#include "property.hpp"
-#include "util/copyable_atomic.hpp"
-
-#include <realm/mixed.hpp>
-#include <realm/list.hpp>
-
-#include <functional>
-#include <memory>
-
-namespace realm {
-class Obj;
-class ObjectSchema;
-class Query;
-class Realm;
-class Results;
-class SortDescriptor;
-class ThreadSafeReference;
-struct ColKey;
-struct ObjKey;
-
-namespace _impl {
-class ListNotifier;
-}
-
-class List {
-public:
- List() noexcept;
- List(std::shared_ptr<Realm> r, const Obj& parent_obj, ColKey col);
- List(std::shared_ptr<Realm> r, const LstBase& list);
- ~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;
-
- ColKey get_parent_column_key() const;
- ObjKey get_parent_object_key() const;
- TableKey get_parent_table_key() const;
-
- // Get the type of the values contained in this List
- PropertyType get_type() const { return m_type; }
-
- // Get the ObjectSchema of the values in this List
- // Only valid if get_type() returns PropertyType::Object
- ObjectSchema const& get_object_schema() const;
-
- bool is_valid() const;
- void verify_attached() const;
- void verify_in_transaction() const;
-
- size_t size() const;
-
- void move(size_t source_ndx, size_t dest_ndx);
- void remove(size_t list_ndx);
- void remove_all();
- void swap(size_t ndx1, size_t ndx2);
- void delete_at(size_t list_ndx);
- void delete_all();
-
- template<typename T = Obj>
- T get(size_t row_ndx) const;
- template<typename T>
- size_t find(T const& value) const;
-
- // Find the index in the List of the first row matching the query
- size_t find(Query&& query) const;
-
- template<typename T>
- void add(T value);
- template<typename T>
- void insert(size_t list_ndx, T value);
- template<typename T>
- void set(size_t row_ndx, T value);
-
- Results sort(SortDescriptor order) const;
- Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const;
- Results filter(Query q) const;
-
- // Return a Results representing a live view of this List.
- Results as_results() const;
-
- // Return a Results representing a snapshot of this List.
- Results snapshot() const;
-
- // Returns a frozen copy of this result
- List freeze(std::shared_ptr<Realm> const& realm) const;
-
- // Returns whether or not this List is frozen.
- bool is_frozen() const noexcept;
-
- // Get the min/max/average/sum of the given column
- // All but sum() returns none when there are zero matching rows
- // sum() returns 0,
- // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
- // Throws OutOfBoundsIndexException for an out-of-bounds column
- util::Optional<Mixed> max(ColKey column={}) const;
- util::Optional<Mixed> min(ColKey column={}) const;
- util::Optional<double> average(ColKey column={}) const;
- Mixed sum(ColKey column={}) const;
-
- bool operator==(List const& rgt) const noexcept;
-
- NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
-
- template<typename Context>
- auto get(Context&, size_t row_ndx) const;
- template<typename T, typename Context>
- size_t find(Context&, T&& value) const;
-
- template<typename T, typename Context>
- void add(Context&, T&& value, CreatePolicy=CreatePolicy::ForceCreate);
- template<typename T, typename Context>
- void insert(Context&, size_t list_ndx, T&& value, CreatePolicy=CreatePolicy::ForceCreate);
- template<typename T, typename Context>
- void set(Context&, size_t row_ndx, T&& value, CreatePolicy=CreatePolicy::ForceCreate);
-
- // Replace the values in this list with the values from an enumerable object
- template<typename T, typename Context>
- void assign(Context&, T&& value, CreatePolicy=CreatePolicy::ForceCreate);
-
- // 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::logic_error {
- InvalidatedException() : std::logic_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;
- PropertyType m_type;
- mutable util::CopyableAtomic<const ObjectSchema*> m_object_schema = nullptr;
- _impl::CollectionNotifier::Handle<_impl::ListNotifier> m_notifier;
- std::shared_ptr<LstBase> m_list_base;
-
- void verify_valid_row(size_t row_ndx, bool insertion = false) const;
- void validate(const Obj&) const;
-
- template<typename Fn>
- auto dispatch(Fn&&) const;
- template<typename T>
- auto& as() const;
-
- template<typename T, typename Context>
- void set_if_different(Context&, size_t row_ndx, T&& value, CreatePolicy);
-
- friend struct std::hash<List>;
-};
-
-template<typename T>
-auto& List::as() const
-{
- return static_cast<Lst<T>&>(*m_list_base);
-}
-
-template<>
-inline auto& List::as<Obj>() const
-{
- return static_cast<LnkLst&>(*m_list_base);
-}
-
-template<typename Fn>
-auto List::dispatch(Fn&& fn) const
-{
- verify_attached();
- return switch_on_type(get_type(), std::forward<Fn>(fn));
-}
-
-template<typename Context>
-auto List::get(Context& ctx, size_t row_ndx) const
-{
- return dispatch([&](auto t) { return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx)); });
-}
-
-template<typename T, typename Context>
-size_t List::find(Context& ctx, T&& value) const
-{
- return dispatch([&](auto t) { return this->find(ctx.template unbox<std::decay_t<decltype(*t)>>(value, CreatePolicy::Skip)); });
-}
-
-template<typename T, typename Context>
-void List::add(Context& ctx, T&& value, CreatePolicy policy)
-{
- dispatch([&](auto t) { this->add(ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy)); });
-}
-
-template<typename T, typename Context>
-void List::insert(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
-{
- dispatch([&](auto t) { this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy)); });
-}
-
-template<typename T, typename Context>
-void List::set(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
-{
- dispatch([&](auto t) { this->set(row_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy)); });
-}
-
-namespace _impl {
-template <class T>
-inline ObjKey help_get_current_row(const T&)
-{
- return ObjKey();
-}
-
-template <>
-inline ObjKey help_get_current_row(const ConstObj& v)
-{
- return v.get_key();
-}
-
-template <>
-inline ObjKey help_get_current_row(const Obj& v)
-{
- return v.get_key();
-}
-
-template <class T>
-inline bool help_compare_values(const T& v1, const T& v2)
-{
- return v1 != v2;
-}
-template <>
-inline bool help_compare_values(const Obj& v1, const Obj& v2)
-{
- return v1.get_table() != v2.get_table() || v1.get_key() != v2.get_key();
-}
-}
-
-template<typename T, typename Context>
-void List::set_if_different(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
-{
- dispatch([&](auto t) {
- using U = std::decay_t<decltype(*t)>;
- auto old_value = this->get<U>(row_ndx);
- auto new_value = ctx.template unbox<U>(value, policy, _impl::help_get_current_row(old_value));
- if (_impl::help_compare_values(old_value, new_value))
- this->set(row_ndx, new_value);
- });
-}
-
-
-template<typename T, typename Context>
-void List::assign(Context& ctx, T&& values, CreatePolicy policy)
-{
- if (ctx.is_same_list(*this, values))
- return;
-
- if (ctx.is_null(values)) {
- remove_all();
- return;
- }
-
- if (policy == CreatePolicy::UpdateModified) {
- size_t sz = size();
- size_t index = 0;
- ctx.enumerate_list(values, [&](auto&& element) {
- if (index < sz) {
- this->set_if_different(ctx, index, element, policy);
- }
- else {
- this->add(ctx, element, policy);
- }
- index++;
- });
- while (index < sz) {
- remove(--sz);
- }
- }
- else {
- remove_all();
- ctx.enumerate_list(values, [&](auto&& element) {
- this->add(ctx, element, policy);
- });
- }
-}
-} // namespace realm
-
-namespace std {
-template<> struct hash<realm::List> {
- size_t operator()(realm::List const&) const;
-};
-}
-
-#endif // REALM_OS_LIST_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp
deleted file mode 100644
index 318da0c80..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "object.hpp"
-
-#include "impl/object_notifier.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-
-#include <realm/table.hpp>
-
-using namespace realm;
-
-Object Object::freeze(std::shared_ptr<Realm> frozen_realm) const
-{
- return Object(frozen_realm, frozen_realm->import_copy_of(m_obj));
-}
-
-bool Object::is_frozen() const noexcept
-{
- return m_realm->is_frozen();
-}
-
-InvalidatedObjectException::InvalidatedObjectException(const std::string& object_type)
-: std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted")
-, object_type(object_type)
-{}
-
-InvalidPropertyException::InvalidPropertyException(const std::string& object_type, const std::string& property_name)
-: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name))
-, object_type(object_type), property_name(property_name)
-{}
-
-MissingPropertyValueException::MissingPropertyValueException(const std::string& object_type, const std::string& property_name)
-: std::logic_error(util::format("Missing value for property '%1.%2'", object_type, property_name))
-, object_type(object_type), property_name(property_name)
-{}
-
-MissingPrimaryKeyException::MissingPrimaryKeyException(const std::string& object_type)
-: std::logic_error(util::format("'%1' does not have a primary key defined", object_type))
-, object_type(object_type)
-{}
-
-ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name)
-: std::logic_error(util::format("Cannot modify read-only property '%1.%2'", object_type, property_name))
-, object_type(object_type), property_name(property_name) {}
-
-ModifyPrimaryKeyException::ModifyPrimaryKeyException(const std::string& object_type, const std::string& property_name)
-: std::logic_error(util::format("Cannot modify primary key after creation: '%1.%2'", object_type, property_name))
-, object_type(object_type), property_name(property_name) {}
-
-Object::Object(SharedRealm r, ObjectSchema const& s, Obj const& o)
-: m_realm(std::move(r)), m_object_schema(&s), m_obj(o) { }
-
-Object::Object(SharedRealm r, Obj const& o)
-: m_realm(std::move(r))
-, m_object_schema(&*m_realm->schema().find(ObjectStore::object_type_for_table_name(o.get_table()->get_name())))
-, m_obj(o)
-{
-}
-
-Object::Object(SharedRealm r, StringData object_type, ObjKey key)
-: m_realm(std::move(r))
-, m_object_schema(&*m_realm->schema().find(object_type))
-, m_obj(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get_object(key))
-{
-}
-
-Object::Object(SharedRealm r, StringData object_type, size_t index)
-: m_realm(std::move(r))
-, m_object_schema(&*m_realm->schema().find(object_type))
-, m_obj(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get_object(index))
-{
-}
-
-Object::Object() = default;
-Object::~Object() = default;
-Object::Object(Object const&) = default;
-Object::Object(Object&&) = default;
-Object& Object::operator=(Object const&) = default;
-Object& Object::operator=(Object&&) = default;
-
-NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) &
-{
- verify_attached();
- m_realm->verify_notifications_available();
- if (!m_notifier) {
- m_notifier = std::make_shared<_impl::ObjectNotifier>(m_realm, m_obj.get_table()->get_key(), m_obj.get_key());
- _impl::RealmCoordinator::register_notifier(m_notifier);
- }
- return {m_notifier, m_notifier->add_callback(std::move(callback))};
-}
-
-void Object::verify_attached() const
-{
- m_realm->verify_thread();
- if (!m_obj.is_valid()) {
- throw InvalidatedObjectException(m_object_schema->name);
- }
-}
-
-Property const& Object::property_for_name(StringData prop_name) const
-{
- auto prop = m_object_schema->property_for_name(prop_name);
- if (!prop) {
- throw InvalidPropertyException(m_object_schema->name, prop_name);
- }
- return *prop;
-}
-
-void Object::validate_property_for_setter(Property const& property) const
-{
- verify_attached();
- m_realm->verify_in_write();
-
- // Modifying primary keys is allowed in migrations to make it possible to
- // add a new primary key to a type (or change the property type), but it
- // is otherwise considered the immutable identity of the row
- if (property.is_primary) {
- if (!m_realm->is_in_migration())
- throw ModifyPrimaryKeyException(m_object_schema->name, property.name);
- // Modifying the PK property while it's the PK will corrupt the table,
- // so remove it and then restore it at the end of the migration (which will rebuild the table)
- m_obj.get_table()->set_primary_key_column({});
- }
-}
-
-#if REALM_ENABLE_SYNC
-void Object::ensure_user_in_everyone_role()
-{
- if (auto role_table = m_realm->read_group().get_table("class___Role")) {
- if (ObjKey ndx = role_table->find_first_string(role_table->get_column_key("name"), "everyone")) {
- auto role = role_table->get_object(ndx);
- auto users = role.get_linklist(role_table->get_column_key("members"));
- if (users.find_first(m_obj.get_key()) == realm::npos) {
- users.add(m_obj.get_key());
- }
- }
- }
-}
-
-void Object::ensure_private_role_exists_for_user()
-{
- auto user_id = m_obj.get<StringData>("id");
- ObjectStore::ensure_private_role_exists_for_user(static_cast<Transaction&>(m_realm->read_group()), user_id);
-}
-#endif
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp
deleted file mode 100644
index 1f2751a2f..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp
+++ /dev/null
@@ -1,188 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_OBJECT_HPP
-#define REALM_OS_OBJECT_HPP
-
-#include "impl/collection_notifier.hpp"
-
-#include <realm/obj.hpp>
-
-namespace realm {
-class ObjectSchema;
-struct Property;
-
-namespace _impl {
- class ObjectNotifier;
-}
-
-enum class CreatePolicy : int8_t {
- // Do not create objects if given something that could be converted to a
- // Realm object but isn't. Used for things like find().
- Skip,
- // Throw an exception if an object with the same PK already exists.
- ForceCreate,
- // If an object with the same PK already exists, set all fields in the input.
- UpdateAll,
- // If an object with the same PK already exists, only set fields which have changed.
- UpdateModified
-};
-
-class Object {
-public:
- Object();
- Object(std::shared_ptr<Realm> r, Obj const& o);
- Object(std::shared_ptr<Realm> r, ObjectSchema const& s, Obj const& o);
- Object(std::shared_ptr<Realm> r, StringData object_type, ObjKey key);
- Object(std::shared_ptr<Realm> r, StringData object_type, size_t index);
-
- Object(Object const&);
- Object(Object&&);
- Object& operator=(Object const&);
- Object& operator=(Object&&);
-
- ~Object();
-
- std::shared_ptr<Realm> const& realm() const { return m_realm; }
- std::shared_ptr<Realm> const& get_realm() const { return m_realm; }
- ObjectSchema const& get_object_schema() const { return *m_object_schema; }
- Obj obj() const { return m_obj; }
-
- bool is_valid() const { return m_obj.is_valid(); }
-
- // Returns a frozen copy of this object.
- Object freeze(std::shared_ptr<Realm> frozen_realm) const;
-
- // Returns whether or not this Object is frozen.
- bool is_frozen() const noexcept;
-
- NotificationToken add_notification_callback(CollectionChangeCallback callback) &;
-
- void ensure_user_in_everyone_role();
- void ensure_private_role_exists_for_user();
-
- template<typename ValueType>
- void set_column_value(StringData prop_name, ValueType&& value) { m_obj.set(prop_name, value); }
-
- template<typename ValueType>
- ValueType get_column_value(StringData prop_name) const { return m_obj.get<ValueType>(prop_name); }
-
- // The following functions require an accessor context which converts from
- // the binding's native data types to the core data types. See CppContext
- // for a reference implementation of such a context.
- //
- // The actual definitions of these templated functions is in object_accessor.hpp
-
- // property getter/setter
- template<typename ValueType, typename ContextType>
- void set_property_value(ContextType& ctx, StringData prop_name,
- ValueType value, CreatePolicy policy = CreatePolicy::ForceCreate);
-
- template<typename ValueType, typename ContextType>
- ValueType get_property_value(ContextType& ctx, StringData prop_name) const;
-
- template<typename ValueType, typename ContextType>
- ValueType get_property_value(ContextType& ctx, const Property& property) const;
-
- // create an Object from a native representation
- template<typename ValueType, typename ContextType>
- static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- const ObjectSchema &object_schema, ValueType value,
- CreatePolicy policy = CreatePolicy::ForceCreate,
- ObjKey current_obj = ObjKey(), Obj* = nullptr);
-
- template<typename ValueType, typename ContextType>
- static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- StringData object_type, ValueType value,
- CreatePolicy policy = CreatePolicy::ForceCreate,
- ObjKey current_obj = ObjKey(), Obj* = nullptr);
-
- template<typename ValueType, typename ContextType>
- static Object get_for_primary_key(ContextType& ctx,
- std::shared_ptr<Realm> const& realm,
- const ObjectSchema &object_schema,
- ValueType primary_value);
-
- template<typename ValueType, typename ContextType>
- static Object get_for_primary_key(ContextType& ctx,
- std::shared_ptr<Realm> const& realm,
- StringData object_type,
- ValueType primary_value);
-
-private:
- friend class Results;
-
- std::shared_ptr<Realm> m_realm;
- const ObjectSchema *m_object_schema;
- Obj m_obj;
- _impl::CollectionNotifier::Handle<_impl::ObjectNotifier> m_notifier;
-
-
- template<typename ValueType, typename ContextType>
- void set_property_value_impl(ContextType& ctx, const Property &property,
- ValueType value, CreatePolicy policy, bool is_default);
- template<typename ValueType, typename ContextType>
- ValueType get_property_value_impl(ContextType& ctx, const Property &property) const;
-
- template<typename ValueType, typename ContextType>
- static ObjKey get_for_primary_key_impl(ContextType& ctx, Table const& table,
- const Property &primary_prop,
- ValueType primary_value);
-
- void verify_attached() const;
- Property const& property_for_name(StringData prop_name) const;
- void validate_property_for_setter(Property const&) const;
-};
-
-struct InvalidatedObjectException : public std::logic_error {
- InvalidatedObjectException(const std::string& object_type);
- const std::string object_type;
-};
-
-struct InvalidPropertyException : public std::logic_error {
- InvalidPropertyException(const std::string& object_type, const std::string& property_name);
- const std::string object_type;
- const std::string property_name;
-};
-
-struct MissingPropertyValueException : public std::logic_error {
- MissingPropertyValueException(const std::string& object_type, const std::string& property_name);
- const std::string object_type;
- const std::string property_name;
-};
-
-struct MissingPrimaryKeyException : public std::logic_error {
- MissingPrimaryKeyException(const std::string& object_type);
- const std::string object_type;
-};
-
-struct ReadOnlyPropertyException : public std::logic_error {
- ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name);
- const std::string object_type;
- const std::string property_name;
-};
-
-struct ModifyPrimaryKeyException : public std::logic_error {
- ModifyPrimaryKeyException(const std::string& object_type, const std::string& property_name);
- const std::string object_type;
- const std::string property_name;
-};
-
-} // namespace realm
-
-#endif // REALM_OS_OBJECT_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp
deleted file mode 100644
index f02b6f901..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp
+++ /dev/null
@@ -1,349 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OS_OBJECT_ACCESSOR_HPP
-#define REALM_OS_OBJECT_ACCESSOR_HPP
-
-#include "object.hpp"
-
-#include "feature_checks.hpp"
-#include "list.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "shared_realm.hpp"
-
-#include <realm/util/assert.hpp>
-#include <realm/table_view.hpp>
-
-#if REALM_ENABLE_SYNC
-#include <realm/sync/object.hpp>
-#endif // REALM_ENABLE_SYNC
-
-#include <string>
-
-namespace realm {
-template <typename ValueType, typename ContextType>
-void Object::set_property_value(ContextType& ctx, StringData prop_name,
- ValueType value, CreatePolicy policy)
-{
- auto& property = property_for_name(prop_name);
- validate_property_for_setter(property);
- set_property_value_impl(ctx, property, value, policy, false);
-}
-
-template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value(ContextType& ctx, const Property& property) const
-{
- return get_property_value_impl<ValueType>(ctx, property);
-}
-
-template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value(ContextType& ctx, StringData prop_name) const
-{
- return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
-}
-
-namespace {
-template <typename ValueType, typename ContextType>
-struct ValueUpdater {
- ContextType& ctx;
- Property const& property;
- ValueType& value;
- Obj& obj;
- ColKey col;
- CreatePolicy policy;
- bool is_default;
-
- void operator()(Obj*)
- {
- ContextType child_ctx(ctx, property);
- auto curr_link = obj.get<ObjKey>(col);
- auto link = child_ctx.template unbox<Obj>(value, policy, curr_link);
- if (policy != CreatePolicy::UpdateModified || curr_link != link.get_key()) {
- obj.set(col, link.get_key());
- }
- }
-
- template<typename T>
- void operator()(T*)
- {
- auto new_val = ctx.template unbox<T>(value);
- if (policy != CreatePolicy::UpdateModified || obj.get<T>(col) != new_val) {
- obj.set(col, new_val, is_default);
- }
- }
-};
-}
-
-template <typename ValueType, typename ContextType>
-void Object::set_property_value_impl(ContextType& ctx, const Property &property,
- ValueType value, CreatePolicy policy, bool is_default)
-{
- ctx.will_change(*this, property);
-
- ColKey col{property.column_key};
- if (is_nullable(property.type) && ctx.is_null(value)) {
- if (policy != CreatePolicy::UpdateModified || !m_obj.is_null(col)) {
- if (property.type == PropertyType::Object) {
- if (!is_default)
- m_obj.set_null(col);
- }
- else {
- m_obj.set_null(col, is_default);
- }
- }
-
- ctx.did_change();
- return;
- }
-
- if (is_array(property.type)) {
- if (property.type == PropertyType::LinkingObjects)
- throw ReadOnlyPropertyException(m_object_schema->name, property.name);
-
- ContextType child_ctx(ctx, property);
- List list(m_realm, m_obj, col);
- list.assign(child_ctx, value, policy);
- ctx.did_change();
- return;
- }
-
- ValueUpdater<ValueType, ContextType> updater{ctx, property, value,
- m_obj, col, policy, is_default};
- switch_on_type(property.type, updater);
- ctx.did_change();
-}
-
-template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property) const
-{
- verify_attached();
-
- ColKey column{property.column_key};
- if (is_nullable(property.type) && m_obj.is_null(column))
- return ctx.null_value();
- if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
- return ctx.box(List(m_realm, m_obj, column));
-
- switch (property.type & ~PropertyType::Flags) {
- case PropertyType::Bool: return ctx.box(m_obj.get<bool>(column));
- case PropertyType::Int: return is_nullable(property.type) ? ctx.box(*m_obj.get<util::Optional<int64_t>>(column)) : ctx.box(m_obj.get<int64_t>(column));
- case PropertyType::Float: return ctx.box(m_obj.get<float>(column));
- case PropertyType::Double: return ctx.box(m_obj.get<double>(column));
- case PropertyType::String: return ctx.box(m_obj.get<StringData>(column));
- case PropertyType::Data: return ctx.box(m_obj.get<BinaryData>(column));
- case PropertyType::Date: return ctx.box(m_obj.get<Timestamp>(column));
-// case PropertyType::Any: return ctx.box(m_obj.get<Mixed>(column));
- case PropertyType::Object: {
- auto linkObjectSchema = m_realm->schema().find(property.object_type);
- return ctx.box(Object(m_realm, *linkObjectSchema,
- const_cast<Obj&>(m_obj).get_linked_object(column)));
- }
- case PropertyType::LinkingObjects: {
- auto target_object_schema = m_realm->schema().find(property.object_type);
- auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
- auto table = m_realm->read_group().get_table(target_object_schema->table_key);
- auto tv = const_cast<Obj&>(m_obj).get_backlink_view(table, ColKey(link_property->column_key));
- return ctx.box(Results(m_realm, std::move(tv)));
- }
- default: REALM_UNREACHABLE();
- }
-}
-
-template<typename ValueType, typename ContextType>
-Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- StringData object_type, ValueType value,
- CreatePolicy policy, ObjKey current_obj, Obj* out_row)
-{
- auto object_schema = realm->schema().find(object_type);
- REALM_ASSERT(object_schema != realm->schema().end());
- return create(ctx, realm, *object_schema, value, policy, current_obj, out_row);
-}
-
-template<typename ValueType, typename ContextType>
-Mixed as_mixed(ContextType& ctx, ValueType& value, PropertyType type)
-{
- if (!value)
- return {};
- return switch_on_type(type, [&](auto* t) {
- return Mixed(ctx.template unbox<NonObjTypeT<decltype(*t)>>(*value));
- });
-}
-
-template<typename ValueType, typename ContextType>
-Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- ObjectSchema const& object_schema, ValueType value,
- CreatePolicy policy, ObjKey current_obj, Obj* out_row)
-{
- realm->verify_in_write();
-
- // When setting each property, we normally want to skip over the primary key
- // as that's set as part of object creation. However, during migrations the
- // property marked as the primary key in the schema may not currently be
- // considered a primary key by core, and so will need to be set.
- bool skip_primary = true;
- // If the input value is missing values for any of the properties we want to
- // set the propery to the default value for new objects, but leave it
- // untouched for existing objects.
- bool created = false;
-
- Obj obj;
- auto table = realm->read_group().get_table(object_schema.table_key);
-
- // If there's a primary key, we need to first check if an object with the
- // same primary key already exists. If it does, we either update that object
- // or throw an exception if updating is disabled.
- if (auto primary_prop = object_schema.primary_key_property()) {
- auto primary_value = ctx.value_for_property(value, *primary_prop,
- primary_prop - &object_schema.persisted_properties[0]);
- if (!primary_value)
- primary_value = ctx.default_value_for_property(object_schema, *primary_prop);
- if (!primary_value && !is_nullable(primary_prop->type))
- throw MissingPropertyValueException(object_schema.name, primary_prop->name);
-
- // When changing the primary key of a table, we remove the existing pk (if any), call
- // the migration function, then add the new pk (if any). This means that we can't call
- // create_object_with_primary_key(), and creating duplicate primary keys is allowed as
- // long as they're unique by the end of the migration.
- if (table->get_primary_key_column() == ColKey{}) {
- REALM_ASSERT(realm->is_in_migration());
- if (policy != CreatePolicy::ForceCreate) {
- if (auto key = get_for_primary_key_impl(ctx, *table, *primary_prop, *primary_value))
- obj = table->get_object(key);
- }
- if (!obj)
- skip_primary = false;
- }
- else {
- obj = table->create_object_with_primary_key(as_mixed(ctx, primary_value, primary_prop->type), &created);
- if (!created && policy == CreatePolicy::ForceCreate) {
- if (!realm->is_in_migration()) {
- throw std::logic_error(util::format("Attempting to create an object of type '%1' with an existing primary key value '%2'.",
- object_schema.name, ctx.print(*primary_value)));
- }
- table->set_primary_key_column(ColKey{});
- skip_primary = false;
- obj = {};
- }
- }
- }
-
- // No primary key (possibly temporarily due to migrations). If we're
- // currently performing a recursive update on an existing object tree then
- // an object key was passed in that we need to look up, and otherwise we
- // need to create the new object.
- if (!obj) {
- if (policy == CreatePolicy::UpdateModified && current_obj) {
- obj = table->get_object(current_obj);
- }
- else {
- obj = table->create_object();
- created = true;
- }
- }
-
- Object object(realm, object_schema, obj);
- // KVO in Cocoa requires that the obj ivar on the wrapper object be set
- // *before* we start setting the properties, so it passes in a pointer to
- // that.
- if (out_row)
- *out_row = obj;
- for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
- auto& prop = object_schema.persisted_properties[i];
- if (skip_primary && prop.is_primary)
- continue;
-
- auto v = ctx.value_for_property(value, prop, i);
- if (!created && !v)
- continue;
-
- bool is_default = false;
- if (!v) {
- v = ctx.default_value_for_property(object_schema, prop);
- is_default = true;
- }
- // We consider null or a missing value to be equivalent to an empty
- // array for historical reasons; the original implementation did this
- // accidentally and it's not worth changing.
- if ((!v || ctx.is_null(*v)) && !is_nullable(prop.type) && !is_array(prop.type)) {
- if (prop.is_primary || !ctx.allow_missing(value))
- throw MissingPropertyValueException(object_schema.name, prop.name);
- }
- if (v)
- object.set_property_value_impl(ctx, prop, *v, policy, is_default);
- }
-#if REALM_ENABLE_SYNC
- if (realm->is_partial() && object_schema.name == "__User") {
- object.ensure_user_in_everyone_role();
- object.ensure_private_role_exists_for_user();
- }
-#endif
- return object;
-}
-
-template<typename ValueType, typename ContextType>
-Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- StringData object_type, ValueType primary_value)
-{
- auto object_schema = realm->schema().find(object_type);
- REALM_ASSERT(object_schema != realm->schema().end());
- return get_for_primary_key(ctx, realm, *object_schema, primary_value);
-}
-
-template<typename ValueType, typename ContextType>
-Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
- const ObjectSchema &object_schema,
- ValueType primary_value)
-{
- auto primary_prop = object_schema.primary_key_property();
- if (!primary_prop) {
- throw MissingPrimaryKeyException(object_schema.name);
- }
-
- TableRef table;
- if (object_schema.table_key)
- table = realm->read_group().get_table(object_schema.table_key);
- if (!table)
- return Object(realm, object_schema, Obj());
- auto key = get_for_primary_key_impl(ctx, *table, *primary_prop, primary_value);
- return Object(realm, object_schema, key ? table->get_object(key) : Obj{});
-}
-
-template<typename ValueType, typename ContextType>
-ObjKey Object::get_for_primary_key_impl(ContextType& ctx, Table const& table,
- const Property &primary_prop,
- ValueType primary_value) {
- bool is_null = ctx.is_null(primary_value);
- if (is_null && !is_nullable(primary_prop.type))
- throw std::logic_error("Invalid null value for non-nullable primary key.");
- if (primary_prop.type == PropertyType::String) {
- return table.find_first(primary_prop.column_key,
- ctx.template unbox<StringData>(primary_value));
- }
- if (is_nullable(primary_prop.type))
- return table.find_first(primary_prop.column_key,
- ctx.template unbox<util::Optional<int64_t>>(primary_value));
- return table.find_first(primary_prop.column_key,
- ctx.template unbox<int64_t>(primary_value));
-}
-
-} // namespace realm
-
-#endif // REALM_OS_OBJECT_ACCESSOR_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp
deleted file mode 100644
index c3b91cb82..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "object_schema.hpp"
-
-#include "feature_checks.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-
-#include <realm/data_type.hpp>
-#include <realm/group.hpp>
-#include <realm/table.hpp>
-
-using namespace realm;
-
-ObjectSchema::ObjectSchema() = default;
-ObjectSchema::~ObjectSchema() = default;
-
-ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties)
-: ObjectSchema(std::move(name), persisted_properties, {})
-{
-}
-
-ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties,
- std::initializer_list<Property> computed_properties)
-: name(std::move(name))
-, persisted_properties(persisted_properties)
-, computed_properties(computed_properties)
-{
- for (auto const& prop : persisted_properties) {
- if (prop.is_primary) {
- primary_key = prop.name;
- break;
- }
- }
-}
-
-PropertyType ObjectSchema::from_core_type(Table const& table, ColKey col)
-{
- auto flags = PropertyType::Required;
- auto attr = table.get_column_attr(col);
- if (attr.test(col_attr_Nullable))
- flags |= PropertyType::Nullable;
- if (attr.test(col_attr_List))
- flags |= PropertyType::Array;
- switch (table.get_column_type(col)) {
- case type_Int: return PropertyType::Int | flags;
- case type_Float: return PropertyType::Float | flags;
- case type_Double: return PropertyType::Double | flags;
- case type_Bool: return PropertyType::Bool | flags;
- case type_String: return PropertyType::String | flags;
- case type_Binary: return PropertyType::Data | flags;
- case type_Timestamp: return PropertyType::Date | flags;
- case type_OldMixed: return PropertyType::Any | flags;
- case type_Link: return PropertyType::Object | PropertyType::Nullable;
- case type_LinkList: return PropertyType::Object | PropertyType::Array;
- default: REALM_UNREACHABLE();
- }
-}
-
-ObjectSchema::ObjectSchema(Group const& group, StringData name, TableKey key)
-: name(name)
-{
- ConstTableRef table;
- if (key) {
- table = group.get_table(key);
- }
- else {
- table = ObjectStore::table_for_object_type(group, name);
- }
- table_key = table->get_key();
-
- size_t count = table->get_column_count();
- persisted_properties.reserve(count);
-
- for (auto col_key : table->get_column_keys()) {
- StringData column_name = table->get_column_name(col_key);
-
-#if REALM_ENABLE_SYNC
- // The object ID column is an implementation detail, and is omitted from the schema.
- // FIXME: this can go away once sync adopts stable ids?
- if (column_name.begins_with("!"))
- continue;
-#endif
-
- Property property;
- property.name = column_name;
- property.type = ObjectSchema::from_core_type(*table, col_key);
- property.is_indexed = table->has_search_index(col_key) || table->get_primary_key_column() == col_key;
- property.column_key = col_key;
-
- if (property.type == PropertyType::Object) {
- // set link type for objects and arrays
- ConstTableRef linkTable = table->get_link_target(col_key);
- property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
- }
- persisted_properties.push_back(std::move(property));
- }
-
- primary_key = ObjectStore::get_primary_key_for_object(group, name);
- set_primary_key_property();
-}
-
-Property *ObjectSchema::property_for_name(StringData name) noexcept
-{
- for (auto& prop : persisted_properties) {
- if (StringData(prop.name) == name) {
- return &prop;
- }
- }
- for (auto& prop : computed_properties) {
- if (StringData(prop.name) == name) {
- return &prop;
- }
- }
- return nullptr;
-}
-
-Property *ObjectSchema::property_for_public_name(StringData public_name) noexcept
-{
- // If no `public_name` is defined, the internal `name` is also considered the public name.
- for (auto& prop : persisted_properties) {
- if (prop.public_name == public_name || (prop.public_name.empty() && prop.name == public_name))
- return &prop;
- }
-
- // Computed properties are not persisted, so creating a public name for such properties
- // are a bit pointless since the internal name is already the "public name", but since
- // this distinction isn't visible in the Property struct we allow it anyway.
- for (auto& prop : computed_properties) {
- if (StringData(prop.public_name.empty() ? prop.name : prop.public_name) == public_name)
- return &prop;
- }
- return nullptr;
-}
-
-const Property *ObjectSchema::property_for_public_name(StringData public_name) const noexcept
-{
- return const_cast<ObjectSchema *>(this)->property_for_public_name(public_name);
-}
-
-const Property *ObjectSchema::property_for_name(StringData name) const noexcept
-{
- return const_cast<ObjectSchema *>(this)->property_for_name(name);
-}
-
-bool ObjectSchema::property_is_computed(Property const& property) const noexcept
-{
- auto end = computed_properties.end();
- return std::find(computed_properties.begin(), end, property) != end;
-}
-
-void ObjectSchema::set_primary_key_property() noexcept
-{
- if (primary_key.length()) {
- if (auto primary_key_prop = primary_key_property()) {
- primary_key_prop->is_primary = true;
- }
- }
-}
-
-static void validate_property(Schema const& schema,
- std::string const& object_name,
- Property const& prop,
- Property const** primary,
- std::vector<ObjectSchemaValidationException>& exceptions)
-{
- if (prop.type == PropertyType::LinkingObjects && !is_array(prop.type)) {
- exceptions.emplace_back("Linking Objects property '%1.%2' must be an array.",
- object_name, prop.name);
- }
-
- // check nullablity
- if (is_nullable(prop.type) && !prop.type_is_nullable()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be nullable.",
- object_name, prop.name, string_for_property_type(prop.type));
- }
- else if (prop.type == PropertyType::Object && !is_nullable(prop.type) && !is_array(prop.type)) {
- exceptions.emplace_back("Property '%1.%2' of type 'object' must be nullable.", object_name, prop.name);
- }
-
- // check primary keys
- if (prop.is_primary) {
- if (prop.type != PropertyType::Int && prop.type != PropertyType::String) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be made the primary key.",
- object_name, prop.name, string_for_property_type(prop.type));
- }
- if (*primary) {
- exceptions.emplace_back("Properties '%1' and '%2' are both marked as the primary key of '%3'.",
- prop.name, (*primary)->name, object_name);
- }
- *primary = &prop;
- }
-
- // check indexable
- if (prop.is_indexed && !prop.type_is_indexable()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be indexed.",
- object_name, prop.name, string_for_property_type(prop.type));
- }
-
- // check that only link properties have object types
- if (prop.type != PropertyType::LinkingObjects && !prop.link_origin_property_name.empty()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an origin property name.",
- object_name, prop.name, string_for_property_type(prop.type));
- }
- else if (prop.type == PropertyType::LinkingObjects && prop.link_origin_property_name.empty()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' must have an origin property name.",
- object_name, prop.name, string_for_property_type(prop.type));
- }
-
- if (prop.type != PropertyType::Object && prop.type != PropertyType::LinkingObjects) {
- if (!prop.object_type.empty()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.",
- object_name, prop.name, prop.type_string());
- }
- return;
- }
-
-
- // check that the object_type is valid for link properties
- auto it = schema.find(prop.object_type);
- if (it == schema.end()) {
- exceptions.emplace_back("Property '%1.%2' of type '%3' has unknown object type '%4'",
- object_name, prop.name, string_for_property_type(prop.type), prop.object_type);
- return;
- }
- if (prop.type != PropertyType::LinkingObjects) {
- return;
- }
-
- const Property *origin_property = it->property_for_name(prop.link_origin_property_name);
- if (!origin_property) {
- exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist",
- prop.object_type, prop.link_origin_property_name,
- object_name, prop.name);
- }
- else if (origin_property->type != PropertyType::Object) {
- exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link",
- prop.object_type, prop.link_origin_property_name,
- object_name, prop.name);
- }
- else if (origin_property->object_type != object_name) {
- exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to type '%5'",
- prop.object_type, prop.link_origin_property_name,
- object_name, prop.name, origin_property->object_type);
- }
-}
-
-void ObjectSchema::validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const
-{
- std::vector<StringData> public_property_names;
- std::vector<StringData> internal_property_names;
- internal_property_names.reserve(persisted_properties.size() + computed_properties.size());
- auto gather_names = [&](auto const &properties) {
- for (auto const &prop : properties) {
- internal_property_names.push_back(prop.name);
- if (!prop.public_name.empty())
- public_property_names.push_back(prop.public_name);
- }
- };
- gather_names(persisted_properties);
- gather_names(computed_properties);
- std::sort(public_property_names.begin(), public_property_names.end());
- std::sort(internal_property_names.begin(), internal_property_names.end());
-
- // Check that property names and aliases are unique
- auto for_each_duplicate = [](auto &&container, auto &&fn) {
- auto end = container.end();
- for (auto it = std::adjacent_find(container.begin(), end); it != end; it = std::adjacent_find(it + 2, end))
- fn(*it);
- };
- for_each_duplicate(public_property_names, [&](auto public_property_name) {
- exceptions.emplace_back("Alias '%1' appears more than once in the schema for type '%2'.",
- public_property_name, name);
- });
- for_each_duplicate(internal_property_names, [&](auto internal_name) {
- exceptions.emplace_back("Property '%1' appears more than once in the schema for type '%2'.",
- internal_name, name);
- });
-
- // Check that no aliases conflict with property names
- struct ErrorWriter {
- ObjectSchema const &os;
- std::vector<ObjectSchemaValidationException> &exceptions;
-
- struct Proxy {
- ObjectSchema const &os;
- std::vector<ObjectSchemaValidationException> &exceptions;
-
- Proxy &operator=(StringData name) {
- exceptions.emplace_back(
- "Property '%1.%2' has an alias '%3' that conflicts with a property of the same name.",
- os.name, os.property_for_public_name(name)->name, name);
- return *this;
- }
- };
-
- Proxy operator*() { return Proxy{os, exceptions}; }
- ErrorWriter &operator=(const ErrorWriter &) { return *this; }
- ErrorWriter &operator++() { return *this; }
- ErrorWriter &operator++(int) { return *this; }
- } writer{*this, exceptions};
- std::set_intersection(public_property_names.begin(), public_property_names.end(),
- internal_property_names.begin(), internal_property_names.end(), writer);
-
- // Validate all properties
- const Property *primary = nullptr;
- for (auto const& prop : persisted_properties) {
- validate_property(schema, name, prop, &primary, exceptions);
- }
- for (auto const& prop : computed_properties) {
- validate_property(schema, name, prop, &primary, exceptions);
- }
-
- if (!primary_key.empty() && !primary && !primary_key_property()) {
- exceptions.emplace_back("Specified primary key '%1.%2' does not exist.", name, primary_key);
- }
-}
-
-namespace realm {
-bool operator==(ObjectSchema const& a, ObjectSchema const& b) noexcept
-{
- return std::tie(a.name, a.primary_key, a.persisted_properties, a.computed_properties)
- == std::tie(b.name, b.primary_key, b.persisted_properties, b.computed_properties);
-
-}
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp
deleted file mode 100644
index 072a1c203..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OBJECT_SCHEMA_HPP
-#define REALM_OBJECT_SCHEMA_HPP
-
-#include <realm/keys.hpp>
-#include <realm/string_data.hpp>
-
-#include <string>
-#include <vector>
-
-namespace realm {
-class Group;
-class Schema;
-class Table;
-enum class PropertyType: unsigned char;
-struct ObjectSchemaValidationException;
-struct Property;
-
-class ObjectSchema {
-public:
- ObjectSchema();
- ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties);
- ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties,
- std::initializer_list<Property> computed_properties);
- ~ObjectSchema();
-
- ObjectSchema(ObjectSchema const&) = default;
- ObjectSchema(ObjectSchema&&) noexcept = default;
- ObjectSchema& operator=(ObjectSchema const&) = default;
- ObjectSchema& operator=(ObjectSchema&&) noexcept = default;
-
- // create object schema from existing table
- // if no table key is provided it is looked up in the group
- ObjectSchema(Group const& group, StringData name, TableKey key);
-
- std::string name;
- std::vector<Property> persisted_properties;
- std::vector<Property> computed_properties;
- std::string primary_key;
- TableKey table_key;
-
- Property *property_for_public_name(StringData public_name) noexcept;
- const Property *property_for_public_name(StringData public_name) const noexcept;
- Property *property_for_name(StringData name) noexcept;
- const Property *property_for_name(StringData name) const noexcept;
- Property *primary_key_property() noexcept {
- return property_for_name(primary_key);
- }
- const Property *primary_key_property() const noexcept {
- return property_for_name(primary_key);
- }
- bool property_is_computed(Property const& property) const noexcept;
-
- void validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const;
-
- friend bool operator==(ObjectSchema const& a, ObjectSchema const& b) noexcept;
-
- static PropertyType from_core_type(Table const& table, ColKey col);
-
-private:
- void set_primary_key_property() noexcept;
-};
-}
-
-#endif /* defined(REALM_OBJECT_SCHEMA_HPP) */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp
deleted file mode 100644
index c49efae7c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp
+++ /dev/null
@@ -1,926 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "object_store.hpp"
-
-#include "feature_checks.hpp"
-#include "object_schema.hpp"
-#include "schema.hpp"
-#include "shared_realm.hpp"
-#include "sync/partial_sync.hpp"
-
-#include <realm/group.hpp>
-#include <realm/table.hpp>
-#include <realm/table_view.hpp>
-#include <realm/util/assert.hpp>
-
-#if REALM_ENABLE_SYNC
-#include <realm/sync/object.hpp>
-#include <realm/sync/permissions.hpp>
-#include <realm/sync/instruction_replication.hpp>
-#endif // REALM_ENABLE_SYNC
-
-#include <string.h>
-
-using namespace realm;
-
-constexpr uint64_t ObjectStore::NotVersioned;
-
-namespace {
-const char * const c_metadataTableName = "metadata";
-const char * const c_versionColumnName = "version";
-
-const char c_object_table_prefix[] = "class_";
-
-void create_metadata_tables(Group& group) {
- // The 'metadata' table is simply ignored by Sync
- TableRef metadata_table = group.get_or_add_table(c_metadataTableName);
-
- if (metadata_table->get_column_count() == 0) {
- metadata_table->add_column(type_Int, c_versionColumnName);
- metadata_table->create_object().set(c_versionColumnName, int64_t(ObjectStore::NotVersioned));
- }
-}
-
-void set_schema_version(Group& group, uint64_t version) {
- group.get_table(c_metadataTableName)->get_object(0).set<int64_t>(c_versionColumnName, version);
-}
-
-template<typename Group>
-auto table_for_object_schema(Group& group, ObjectSchema const& object_schema)
-{
- return ObjectStore::table_for_object_type(group, object_schema.name);
-}
-
-DataType to_core_type(PropertyType type)
-{
- REALM_ASSERT(type != PropertyType::Object); // Link columns have to be handled differently
- REALM_ASSERT(type != PropertyType::Any); // Mixed columns can't be created
- switch (type & ~PropertyType::Flags) {
- case PropertyType::Int: return type_Int;
- case PropertyType::Bool: return type_Bool;
- case PropertyType::Float: return type_Float;
- case PropertyType::Double: return type_Double;
- case PropertyType::String: return type_String;
- case PropertyType::Date: return type_Timestamp;
- case PropertyType::Data: return type_Binary;
- default: REALM_COMPILER_HINT_UNREACHABLE();
- }
-}
-
-ColKey add_column(Group& group, Table& table, Property const& property)
-{
- // Cannot directly insert a LinkingObjects column (a computed property).
- // LinkingObjects must be an artifact of an existing link column.
- REALM_ASSERT(property.type != PropertyType::LinkingObjects);
-
- if (property.is_primary) {
- // Primary key columns should have been created when the table was created
- if (auto col = table.get_column_key(property.name)) {
- return col;
- }
- }
- if (property.type == PropertyType::Object) {
- auto target_name = ObjectStore::table_name_for_object_type(property.object_type);
- TableRef link_table = group.get_table(target_name);
- REALM_ASSERT(link_table);
- return table.add_column_link(is_array(property.type) ? type_LinkList : type_Link,
- property.name, *link_table);
- }
- else if (is_array(property.type)) {
- return table.add_column_list(to_core_type(property.type & ~PropertyType::Flags),
- property.name, is_nullable(property.type));
- }
- else {
- auto key = table.add_column(to_core_type(property.type), property.name, is_nullable(property.type));
- if (property.requires_index())
- table.add_search_index(key);
- return key;
- }
-}
-
-void replace_column(Group& group, Table& table, Property const& old_property,
- Property const& new_property)
-{
- table.remove_column(old_property.column_key);
- add_column(group, table, new_property);
-}
-
-TableRef create_table(Group& group, ObjectSchema const& object_schema)
-{
- auto name = ObjectStore::table_name_for_object_type(object_schema.name);
-
- TableRef table;
- if (auto* pk_property = object_schema.primary_key_property()) {
- table = group.get_table(name);
- if (!table) {
- table = group.add_table_with_primary_key(name, to_core_type(pk_property->type), pk_property->name,
- is_nullable(pk_property->type));
- }
- }
- else {
- table = group.get_or_add_table(name);
- }
-
- return table;
-}
-
-void add_initial_columns(Group& group, ObjectSchema const& object_schema)
-{
- auto name = ObjectStore::table_name_for_object_type(object_schema.name);
- TableRef table = group.get_table(name);
-
- for (auto const& prop : object_schema.persisted_properties) {
-#if REALM_ENABLE_SYNC
- // The sync::create_table* functions create the PK column for us.
- if (prop.is_primary)
- continue;
-#endif // REALM_ENABLE_SYNC
- add_column(group, *table, prop);
- }
-}
-
-void make_property_optional(Table& table, Property property)
-{
- property.type |= PropertyType::Nullable;
- const bool throw_on_null = false;
- property.column_key = table.set_nullability(property.column_key, true, throw_on_null);
-}
-
-void make_property_required(Group& group, Table& table, Property property)
-{
- property.type &= ~PropertyType::Nullable;
- table.remove_column(property.column_key);
- property.column_key = add_column(group, table, property).value;
-}
-
-} // anonymous namespace
-
-void ObjectStore::set_schema_version(Group& group, uint64_t version) {
- ::create_metadata_tables(group);
- ::set_schema_version(group, version);
-}
-
-uint64_t ObjectStore::get_schema_version(Group const& group) {
- ConstTableRef table = group.get_table(c_metadataTableName);
- if (!table || table->get_column_count() == 0) {
- return ObjectStore::NotVersioned;
- }
- return table->get_object(0).get<int64_t>(c_versionColumnName);
-}
-
-StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) {
- if (ConstTableRef table = table_for_object_type(group, object_type)) {
- if (auto col = table->get_primary_key_column()) {
- return table->get_column_name(col);
- }
- }
- return "";
-}
-
-void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) {
- auto t = table_for_object_type(group, object_type);
- ColKey pk_col;
- if (primary_key.size()) {
- pk_col = t->get_column_key(primary_key);
- REALM_ASSERT(pk_col);
- }
- t->set_primary_key_column(pk_col);
-}
-
-StringData ObjectStore::object_type_for_table_name(StringData table_name) {
- if (table_name.begins_with(c_object_table_prefix)) {
- return table_name.substr(sizeof(c_object_table_prefix) - 1);
- }
- return StringData();
-}
-
-std::string ObjectStore::table_name_for_object_type(StringData object_type) {
- return std::string(c_object_table_prefix) + std::string(object_type);
-}
-
-TableRef ObjectStore::table_for_object_type(Group& group, StringData object_type) {
- auto name = table_name_for_object_type(object_type);
- return group.get_table(name);
-}
-
-ConstTableRef ObjectStore::table_for_object_type(Group const& group, StringData object_type) {
- auto name = table_name_for_object_type(object_type);
- return group.get_table(name);
-}
-
-namespace {
-struct SchemaDifferenceExplainer {
- std::vector<ObjectSchemaValidationException> errors;
-
- void operator()(schema_change::AddTable op)
- {
- errors.emplace_back("Class '%1' has been added.", op.object->name);
- }
-
- void operator()(schema_change::RemoveTable)
- {
- // We never do anything for RemoveTable
- }
-
- void operator()(schema_change::AddInitialProperties)
- {
- // Nothing. Always preceded by AddTable.
- }
-
- void operator()(schema_change::AddProperty op)
- {
- errors.emplace_back("Property '%1.%2' has been added.", op.object->name, op.property->name);
- }
-
- void operator()(schema_change::RemoveProperty op)
- {
- errors.emplace_back("Property '%1.%2' has been removed.", op.object->name, op.property->name);
- }
-
- void operator()(schema_change::ChangePropertyType op)
- {
- errors.emplace_back("Property '%1.%2' has been changed from '%3' to '%4'.",
- op.object->name, op.new_property->name,
- op.old_property->type_string(),
- op.new_property->type_string());
- }
-
- void operator()(schema_change::MakePropertyNullable op)
- {
- errors.emplace_back("Property '%1.%2' has been made optional.", op.object->name, op.property->name);
- }
-
- void operator()(schema_change::MakePropertyRequired op)
- {
- errors.emplace_back("Property '%1.%2' has been made required.", op.object->name, op.property->name);
- }
-
- void operator()(schema_change::ChangePrimaryKey op)
- {
- if (op.property && !op.object->primary_key.empty()) {
- errors.emplace_back("Primary Key for class '%1' has changed from '%2' to '%3'.",
- op.object->name, op.object->primary_key, op.property->name);
- }
- else if (op.property) {
- errors.emplace_back("Primary Key for class '%1' has been added.", op.object->name);
- }
- else {
- errors.emplace_back("Primary Key for class '%1' has been removed.", op.object->name);
- }
- }
-
- void operator()(schema_change::AddIndex op)
- {
- errors.emplace_back("Property '%1.%2' has been made indexed.", op.object->name, op.property->name);
- }
-
- void operator()(schema_change::RemoveIndex op)
- {
- errors.emplace_back("Property '%1.%2' has been made unindexed.", op.object->name, op.property->name);
- }
-};
-
-class TableHelper {
-public:
- TableHelper(Group& g) : m_group(g) { }
-
- Table& operator()(const ObjectSchema* object_schema)
- {
- if (object_schema != m_current_object_schema) {
- m_current_table = table_for_object_schema(m_group, *object_schema);
- m_current_object_schema = object_schema;
- }
- REALM_ASSERT(m_current_table);
- return *m_current_table;
- }
-
-private:
- Group& m_group;
- const ObjectSchema* m_current_object_schema = nullptr;
- TableRef m_current_table;
-};
-
-template<typename ErrorType, typename Verifier>
-void verify_no_errors(Verifier&& verifier, std::vector<SchemaChange> const& changes)
-{
- for (auto& change : changes) {
- change.visit(verifier);
- }
-
- if (!verifier.errors.empty()) {
- throw ErrorType(verifier.errors);
- }
-}
-} // anonymous namespace
-
-bool ObjectStore::needs_migration(std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Visitor {
- bool operator()(AddIndex) { return false; }
- bool operator()(AddInitialProperties) { return false; }
- bool operator()(AddProperty) { return true; }
- bool operator()(AddTable) { return false; }
- bool operator()(RemoveTable) { return false; }
- bool operator()(ChangePrimaryKey) { return true; }
- bool operator()(ChangePropertyType) { return true; }
- bool operator()(MakePropertyNullable) { return true; }
- bool operator()(MakePropertyRequired) { return true; }
- bool operator()(RemoveIndex) { return false; }
- bool operator()(RemoveProperty) { return true; }
- };
-
- return std::any_of(begin(changes), end(changes),
- [](auto&& change) { return change.visit(Visitor()); });
-}
-
-void ObjectStore::verify_no_changes_required(std::vector<SchemaChange> const& changes)
-{
- verify_no_errors<SchemaMismatchException>(SchemaDifferenceExplainer(), changes);
-}
-
-void ObjectStore::verify_no_migration_required(std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Verifier : SchemaDifferenceExplainer {
- using SchemaDifferenceExplainer::operator();
-
- // Adding a table or adding/removing indexes can be done automatically.
- // All other changes require migrations.
- void operator()(AddTable) { }
- void operator()(AddInitialProperties) { }
- void operator()(AddIndex) { }
- void operator()(RemoveIndex) { }
- } verifier;
- verify_no_errors<SchemaMismatchException>(verifier, changes);
-}
-
-bool ObjectStore::verify_valid_additive_changes(std::vector<SchemaChange> const& changes, bool update_indexes)
-{
- using namespace schema_change;
- struct Verifier : SchemaDifferenceExplainer {
- using SchemaDifferenceExplainer::operator();
-
- bool index_changes = false;
- bool other_changes = false;
-
- // Additive mode allows adding things, extra columns, and adding/removing indexes
- void operator()(AddTable) { other_changes = true; }
- void operator()(AddInitialProperties) { other_changes = true; }
- void operator()(AddProperty) { other_changes = true; }
- void operator()(RemoveProperty) { }
- void operator()(AddIndex) { index_changes = true; }
- void operator()(RemoveIndex) { index_changes = true; }
- } verifier;
- verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
- return verifier.other_changes || (verifier.index_changes && update_indexes);
-}
-
-void ObjectStore::verify_valid_external_changes(std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Verifier : SchemaDifferenceExplainer {
- using SchemaDifferenceExplainer::operator();
-
- // Adding new things is fine
- void operator()(AddTable) { }
- void operator()(AddInitialProperties) { }
- void operator()(AddProperty) { }
- void operator()(AddIndex) { }
- void operator()(RemoveIndex) { }
-
- // Deleting tables is not okay
- void operator()(RemoveTable op) {
- errors.emplace_back("Class '%1' has been removed.", op.object->name);
- }
- } verifier;
- verify_no_errors<InvalidExternalSchemaChangeException>(verifier, changes);
-}
-
-void ObjectStore::verify_compatible_for_immutable_and_readonly(std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Verifier : SchemaDifferenceExplainer {
- using SchemaDifferenceExplainer::operator();
-
- void operator()(AddTable) { }
- void operator()(AddInitialProperties) { }
- void operator()(RemoveProperty) { }
- void operator()(AddIndex) { }
- void operator()(RemoveIndex) { }
- } verifier;
- verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
-}
-
-static void apply_non_migration_changes(Group& group, std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Applier : SchemaDifferenceExplainer {
- Applier(Group& group) : group{group}, table{group} { }
- Group& group;
- TableHelper table;
-
- // Produce an exception listing the unsupported schema changes for
- // everything but the explicitly supported ones
- using SchemaDifferenceExplainer::operator();
-
- void operator()(AddTable op) { create_table(group, *op.object); }
- void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
- void operator()(AddIndex op) { table(op.object).add_search_index(op.property->column_key); }
- void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->column_key); }
- } applier{group};
- verify_no_errors<SchemaMismatchException>(applier, changes);
-}
-
-static void create_initial_tables(Group& group, std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Applier {
- Applier(Group& group) : group{group}, table{group} { }
- Group& group;
- TableHelper table;
-
- void operator()(AddTable op) { create_table(group, *op.object); }
- void operator()(RemoveTable) { }
- void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
-
- // Note that in normal operation none of these will be hit, as if we're
- // creating the initial tables there shouldn't be anything to update.
- // Implementing these makes us better able to handle weird
- // not-quite-correct files produced by other things and has no obvious
- // downside.
- void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
- void operator()(RemoveProperty op) { table(op.object).remove_column(op.property->column_key); }
- void operator()(MakePropertyNullable op) { make_property_optional(table(op.object), *op.property); }
- void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); }
- void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); }
- void operator()(AddIndex op) { table(op.object).add_search_index(op.property->column_key); }
- void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->column_key); }
-
- void operator()(ChangePropertyType op)
- {
- replace_column(group, table(op.object), *op.old_property, *op.new_property);
- }
- } applier{group};
-
- for (auto& change : changes) {
- change.visit(applier);
- }
-}
-
-void ObjectStore::apply_additive_changes(Group& group, std::vector<SchemaChange> const& changes, bool update_indexes)
-{
- using namespace schema_change;
- struct Applier {
- Applier(Group& group, bool update_indexes)
- : group{group}, table{group}, update_indexes{update_indexes} { }
- Group& group;
- TableHelper table;
- bool update_indexes;
-
- void operator()(AddTable op) { create_table(group, *op.object); }
- void operator()(RemoveTable) { }
- void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
- void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
- void operator()(AddIndex op) { if (update_indexes) table(op.object).add_search_index(op.property->column_key); }
- void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->column_key); }
- void operator()(RemoveProperty) { }
-
- // No need for errors for these, as we've already verified that they aren't present
- void operator()(ChangePrimaryKey) { }
- void operator()(ChangePropertyType) { }
- void operator()(MakePropertyNullable) { }
- void operator()(MakePropertyRequired) { }
- } applier{group, update_indexes};
-
- for (auto& change : changes) {
- change.visit(applier);
- }
-}
-
-static void apply_pre_migration_changes(Group& group, std::vector<SchemaChange> const& changes)
-{
- using namespace schema_change;
- struct Applier {
- Applier(Group& group) : group{group}, table{group} { }
- Group& group;
- TableHelper table;
-
- void operator()(AddTable op) { create_table(group, *op.object); }
- void operator()(RemoveTable) { }
- void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
- void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
- void operator()(RemoveProperty) { /* delayed until after the migration */ }
- void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); }
- void operator()(MakePropertyNullable op) { make_property_optional(table(op.object), *op.property); }
- void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); }
- void operator()(ChangePrimaryKey op) { table(op.object).set_primary_key_column(ColKey{}); }
- void operator()(AddIndex op) { table(op.object).add_search_index(op.property->column_key); }
- void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->column_key); }
- } applier{group};
-
- for (auto& change : changes) {
- change.visit(applier);
- }
-}
-
-enum class DidRereadSchema { Yes, No };
-
-static void apply_post_migration_changes(Group& group,
- std::vector<SchemaChange> const& changes,
- Schema const& initial_schema,
- DidRereadSchema did_reread_schema)
-{
- using namespace schema_change;
- struct Applier {
- Applier(Group& group, Schema const& initial_schema, DidRereadSchema did_reread_schema)
- : group{group}, initial_schema(initial_schema), table(group)
- , did_reread_schema(did_reread_schema == DidRereadSchema::Yes)
- { }
- Group& group;
- Schema const& initial_schema;
- TableHelper table;
- bool did_reread_schema;
-
- void operator()(RemoveProperty op)
- {
- if (!initial_schema.empty() && !initial_schema.find(op.object->name)->property_for_name(op.property->name))
- throw std::logic_error(util::format("Renamed property '%1.%2' does not exist.", op.object->name, op.property->name));
- auto table = table_for_object_schema(group, *op.object);
- table->remove_column(op.property->column_key);
- }
-
- void operator()(ChangePrimaryKey op)
- {
- Table& t = table(op.object);
- if (op.property) {
- auto col = t.get_column_key(op.property->name);
- REALM_ASSERT(col);
- t.set_primary_key_column(col);
- }
- else {
- t.set_primary_key_column(ColKey());
- }
- }
-
- void operator()(AddTable op) { create_table(group, *op.object); }
-
- void operator()(AddInitialProperties op) {
- if (did_reread_schema)
- add_initial_columns(group, *op.object);
- else {
- // If we didn't re-read the schema then AddInitialProperties was already taken care of
- // during apply_pre_migration_changes.
- }
- }
-
- void operator()(AddIndex op) { table(op.object).add_search_index(op.property->column_key); }
- void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->column_key); }
-
- void operator()(RemoveTable) { }
- void operator()(ChangePropertyType) { }
- void operator()(MakePropertyNullable) { }
- void operator()(MakePropertyRequired) { }
- void operator()(AddProperty) { }
- } applier{group, initial_schema, did_reread_schema};
-
- for (auto& change : changes) {
- change.visit(applier);
- }
-}
-
-static void create_default_permissions(Transaction& group, std::vector<SchemaChange> const& changes,
- std::string const& sync_user_id)
-{
-#if !REALM_ENABLE_SYNC
- static_cast<void>(group);
- static_cast<void>(changes);
- static_cast<void>(sync_user_id);
-#else
- _impl::initialize_schema(group);
- sync::set_up_basic_permissions(group, true);
-
- // Ensure that this user exists so that local privileges checks work immediately
- sync::add_user_to_role(group, sync_user_id, "everyone");
-
- // Ensure that the user's private role exists so that local privilege checks work immediately.
- ObjectStore::ensure_private_role_exists_for_user(group, sync_user_id);
-
- // Mark all tables we just created as fully world-accessible
- // This has to be done after the first pass of schema init is done so that we can be
- // sure that the permissions tables actually exist.
- using namespace schema_change;
- struct Applier {
- Transaction& group;
- void operator()(AddTable op)
- {
- sync::set_class_permissions_for_role(group, op.object->name, "everyone",
- static_cast<int>(ComputedPrivileges::All));
- }
-
- void operator()(RemoveTable) { }
- void operator()(AddInitialProperties) { }
- void operator()(AddProperty) { }
- void operator()(RemoveProperty) { }
- void operator()(MakePropertyNullable) { }
- void operator()(MakePropertyRequired) { }
- void operator()(ChangePrimaryKey) { }
- void operator()(AddIndex) { }
- void operator()(RemoveIndex) { }
- void operator()(ChangePropertyType) { }
- } applier{group};
-
- for (auto& change : changes) {
- change.visit(applier);
- }
-#endif
-}
-
-#if REALM_ENABLE_SYNC
-void ObjectStore::ensure_private_role_exists_for_user(Transaction& group, StringData sync_user_id)
-{
- std::string private_role_name = util::format("__User:%1", sync_user_id);
-
- TableRef roles = ObjectStore::table_for_object_type(group, "__Role");
- ObjKey private_role_ndx = roles->find_first_string(roles->get_column_key("name"), private_role_name);
- if (private_role_ndx) {
- // The private role already exists, so there's nothing for us to do.
- return;
- }
-
- // Add the user to the private role, creating the private role in the process.
- sync::add_user_to_role(group, sync_user_id, private_role_name);
-
- // Set the private role on the user.
- private_role_ndx = roles->find_first_string(roles->get_column_key("name"), private_role_name);
- TableRef users = ObjectStore::table_for_object_type(group, "__User");
- ObjKey user_ndx = users->find_first_string(users->get_column_key("id"), sync_user_id);
- users->get_object(user_ndx).set("role", private_role_ndx);
-}
-#endif
-
-void ObjectStore::apply_schema_changes(Transaction& group, uint64_t schema_version,
- Schema& target_schema, uint64_t target_schema_version,
- SchemaMode mode, std::vector<SchemaChange> const& changes,
- util::Optional<std::string> sync_user_id,
- std::function<void()> migration_function)
-{
- create_metadata_tables(group);
-
- if (mode == SchemaMode::Additive) {
- bool target_schema_is_newer = (schema_version < target_schema_version
- || schema_version == ObjectStore::NotVersioned);
-
- // With sync v2.x, indexes are no longer synced, so there's no reason to avoid creating them.
- bool update_indexes = true;
- apply_additive_changes(group, changes, update_indexes);
-
- if (target_schema_is_newer)
- set_schema_version(group, target_schema_version);
-
- if (sync_user_id)
- create_default_permissions(group, changes, *sync_user_id);
-
- set_schema_keys(group, target_schema);
- return;
- }
-
- if (schema_version == ObjectStore::NotVersioned) {
- create_initial_tables(group, changes);
- set_schema_version(group, target_schema_version);
- set_schema_keys(group, target_schema);
- return;
- }
-
- if (mode == SchemaMode::Manual) {
- set_schema_keys(group, target_schema);
- if (migration_function) {
- migration_function();
- }
-
- verify_no_changes_required(schema_from_group(group).compare(target_schema));
- group.validate_primary_columns();
- set_schema_keys(group, target_schema);
- set_schema_version(group, target_schema_version);
- return;
- }
-
- if (schema_version == target_schema_version) {
- apply_non_migration_changes(group, changes);
- set_schema_keys(group, target_schema);
- return;
- }
-
- auto old_schema = schema_from_group(group);
- apply_pre_migration_changes(group, changes);
- if (migration_function) {
- set_schema_keys(group, target_schema);
- migration_function();
-
- // Migration function may have changed the schema, so we need to re-read it
- auto schema = schema_from_group(group);
- apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes);
- group.validate_primary_columns();
- }
- else {
- apply_post_migration_changes(group, changes, {}, DidRereadSchema::No);
- }
-
- set_schema_version(group, target_schema_version);
- set_schema_keys(group, target_schema);
-}
-
-Schema ObjectStore::schema_from_group(Group const& group) {
- std::vector<ObjectSchema> schema;
- schema.reserve(group.size());
- for (auto key : group.get_table_keys()) {
- auto object_type = object_type_for_table_name(group.get_table_name(key));
- if (object_type.size()) {
- schema.emplace_back(group, object_type, key);
- }
- }
- return schema;
-}
-
-util::Optional<Property> ObjectStore::property_for_column_index(ConstTableRef& table, ColKey column_key)
-{
- StringData column_name = table->get_column_name(column_key);
-
- Property property;
- property.name = column_name;
- property.type = ObjectSchema::from_core_type(*table, column_key);
- property.is_primary = table->get_primary_key_column() == column_key;
- property.is_indexed = table->has_search_index(column_key);
- property.column_key = column_key;
-
- if (property.type == PropertyType::Object) {
- // set link type for objects and arrays
- ConstTableRef linkTable = table->get_link_target(column_key);
- property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
- }
- return property;
-}
-
-void ObjectStore::set_schema_keys(Group const& group, Schema& schema)
-{
- for (auto& object_schema : schema) {
- auto table = table_for_object_schema(group, object_schema);
- if (!table) {
- continue;
- }
- object_schema.table_key = table->get_key();
- for (auto& property : object_schema.persisted_properties) {
- property.column_key = table->get_column_key(property.name);
- }
- }
-}
-
-void ObjectStore::delete_data_for_object(Group& group, StringData object_type)
-{
- if (TableRef table = table_for_object_type(group, object_type)) {
- ObjectStore::set_primary_key_for_object(group, object_type, "");
- group.remove_table(table->get_key());
- }
-}
-
-bool ObjectStore::is_empty(Group const& group)
-{
- for (auto key : group.get_table_keys()) {
- ConstTableRef table = group.get_table(key);
- auto object_type = object_type_for_table_name(table->get_name());
- if (object_type.size() == 0 || object_type.begins_with("__")) {
- continue;
- }
- if (!table->is_empty()) {
- return false;
- }
- }
- return true;
-}
-
-void ObjectStore::rename_property(Group& group, Schema& target_schema, StringData object_type, StringData old_name, StringData new_name)
-{
- TableRef table = table_for_object_type(group, object_type);
- if (!table) {
- throw std::logic_error(util::format("Cannot rename properties for type '%1' because it does not exist.", object_type));
- }
-
- auto target_object_schema = target_schema.find(object_type);
- if (target_object_schema == target_schema.end()) {
- throw std::logic_error(util::format("Cannot rename properties for type '%1' because it has been removed from the Realm.", object_type));
- }
-
- if (target_object_schema->property_for_name(old_name)) {
- throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because the source property still exists.",
- object_type, old_name, new_name));
- }
-
- ObjectSchema table_object_schema(group, object_type, table->get_key());
- Property *old_property = table_object_schema.property_for_name(old_name);
- if (!old_property) {
- throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name));
- }
-
- Property *new_property = table_object_schema.property_for_name(new_name);
- if (!new_property) {
- // New property doesn't exist in the table, which means we're probably
- // renaming to an intermediate property in a multi-version migration.
- // This is safe because the migration will fail schema validation unless
- // this property is renamed again to a valid name before the end.
- table->rename_column(old_property->column_key, new_name);
- return;
- }
-
- if (old_property->type != new_property->type || old_property->object_type != new_property->object_type) {
- throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from type '%4' to '%5'.",
- object_type, old_name, new_name, old_property->type_string(), new_property->type_string()));
- }
-
- if (is_nullable(old_property->type) && !is_nullable(new_property->type)) {
- throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from optional to required.",
- object_type, old_name, new_name));
- }
-
- table->remove_column(new_property->column_key);
- table->rename_column(old_property->column_key, new_name);
-
- if (auto prop = target_object_schema->property_for_name(new_name)) {
- prop->column_key = old_property->column_key;
- }
-
- // update nullability for column
- if (is_nullable(new_property->type) && !is_nullable(old_property->type)) {
- auto prop = *new_property;
- prop.column_key = old_property->column_key;
- make_property_optional(*table, prop);
- }
-}
-
-InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version)
-: logic_error(util::format("Provided schema version %1 is less than last set version %2.", new_version, old_version))
-, m_old_version(old_version), m_new_version(new_version)
-{
-}
-
-SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
-: std::logic_error([&] {
- std::string message = "Schema validation failed due to the following errors:";
- for (auto const& error : errors) {
- message += std::string("\n- ") + error.what();
- }
- return message;
-}())
-{
-}
-
-SchemaMismatchException::SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors)
-: std::logic_error([&] {
- std::string message = "Migration is required due to the following errors:";
- for (auto const& error : errors) {
- message += std::string("\n- ") + error.what();
- }
- return message;
-}())
-{
-}
-
-InvalidSchemaChangeException::InvalidSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors)
-: std::logic_error([&] {
- std::string message = "The following changes cannot be made in additive-only schema mode:";
- for (auto const& error : errors) {
- message += std::string("\n- ") + error.what();
- }
- return message;
-}())
-{
-}
-
-InvalidExternalSchemaChangeException::InvalidExternalSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors)
-: std::logic_error([&] {
- std::string message =
- "Unsupported schema changes were made by another client or process. For a "
- "synchronized Realm, this may be due to the server reverting schema changes which "
- "the local user did not have permission to make.";
- for (auto const& error : errors) {
- message += std::string("\n- ") + error.what();
- }
- return message;
-}())
-{
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp
deleted file mode 100644
index b989b5fc8..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp
+++ /dev/null
@@ -1,170 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OBJECT_STORE_HPP
-#define REALM_OBJECT_STORE_HPP
-
-#include "property.hpp"
-
-#include <realm/table_ref.hpp>
-#include <realm/util/optional.hpp>
-
-#include <functional>
-#include <string>
-#include <vector>
-#include <limits>
-
-namespace realm {
-class Group;
-class Transaction;
-class Schema;
-class SchemaChange;
-class StringData;
-enum class SchemaMode : uint8_t;
-
-namespace util {
-template<typename... Args>
-std::string format(const char* fmt, Args&&... args);
-}
-
-class ObjectStore {
-public:
- // Schema version used for uninitialized Realms
- static constexpr uint64_t NotVersioned = std::numeric_limits<uint64_t>::max();
-
- // Column name used for subtables which store an array
- static constexpr const char* const ArrayColumnName = "!ARRAY_VALUE";
-
- // get the last set schema version
- static uint64_t get_schema_version(Group const& group);
-
- // set the schema version without any checks
- // and the tables for the schema version and the primary key are created if they don't exist
- // NOTE: must be performed within a write transaction
- static void set_schema_version(Group& group, uint64_t version);
-
- // check if all of the changes in the list can be applied automatically, or
- // throw if any of them require a schema version bump and migration function
- static void verify_no_migration_required(std::vector<SchemaChange> const& changes);
-
- // Similar to above, but returns a bool rather than throwing/not throwing
- static bool needs_migration(std::vector<SchemaChange> const& changes);
-
- // check if any of the schema changes in the list are forbidden in
- // additive-only mode, and if any are throw an exception
- // returns true if any of the changes are not no-ops
- static bool verify_valid_additive_changes(std::vector<SchemaChange> const& changes,
- bool update_indexes=false);
-
- // check if the schema changes made by a different process made any changes
- // which will prevent us from being able to continue (such as removing a
- // property we were relying on)
- static void verify_valid_external_changes(std::vector<SchemaChange> const& changes);
-
- static void verify_compatible_for_immutable_and_readonly(std::vector<SchemaChange> const& changes);
-
- // check if changes is empty, and throw an exception if not
- static void verify_no_changes_required(std::vector<SchemaChange> const& changes);
-
- // updates a Realm from old_schema to the given target schema, creating and updating tables as needed
- // passed in target schema is updated with the correct column mapping
- // optionally runs migration function if schema is out of date
- // NOTE: must be performed within a write transaction
- static void apply_schema_changes(Transaction& group, uint64_t schema_version,
- Schema& target_schema, uint64_t target_schema_version,
- SchemaMode mode, std::vector<SchemaChange> const& changes,
- util::Optional<std::string> sync_user_id,
- std::function<void()> migration_function={});
-
- static void apply_additive_changes(Group&, std::vector<SchemaChange> const&, bool update_indexes);
-
- // get a table for an object type
- static realm::TableRef table_for_object_type(Group& group, StringData object_type);
- static realm::ConstTableRef table_for_object_type(Group const& group, StringData object_type);
-
- // get existing Schema from a group
- static Schema schema_from_group(Group const& group);
-
- // get the property for a existing column in the given table. return none if the column is reserved internally.
- // NOTE: is_primary won't be set for the returned property.
- static util::Optional<Property> property_for_column_index(ConstTableRef& table, ColKey column_key);
-
- static void set_schema_keys(Group const& group, Schema& schema);
-
- // deletes the table for the given type
- static void delete_data_for_object(Group& group, StringData object_type);
-
- // indicates if this group contains any objects
- static bool is_empty(Group const& group);
-
- // renames the object_type's column of the old_name to the new name
- static void rename_property(Group& group, Schema& schema, StringData object_type, StringData old_name, StringData new_name);
-
- // get primary key property name for object type
- static StringData get_primary_key_for_object(Group const& group, StringData object_type);
-
- // sets primary key property for object type
- // must be in write transaction to set
- static void set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key);
-
- static std::string table_name_for_object_type(StringData class_name);
- static StringData object_type_for_table_name(StringData table_name);
-
- // creates the private role for the given user if it does not exist
- static void ensure_private_role_exists_for_user(Transaction& group, StringData sync_user_id);
-
-private:
- friend class ObjectSchema;
-};
-
-class InvalidSchemaVersionException : public std::logic_error {
-public:
- InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version);
- uint64_t old_version() const { return m_old_version; }
- uint64_t new_version() const { return m_new_version; }
-private:
- uint64_t m_old_version, m_new_version;
-};
-
-// Schema validation exceptions
-struct ObjectSchemaValidationException : public std::logic_error {
- ObjectSchemaValidationException(std::string message) : logic_error(std::move(message)) {}
-
- template<typename... Args>
- ObjectSchemaValidationException(const char* fmt, Args&&... args)
- : std::logic_error(util::format(fmt, std::forward<Args>(args)...)) { }
-};
-
-struct SchemaValidationException : public std::logic_error {
- SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors);
-};
-
-struct SchemaMismatchException : public std::logic_error {
- SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors);
-};
-
-struct InvalidSchemaChangeException : public std::logic_error {
- InvalidSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors);
-};
-
-struct InvalidExternalSchemaChangeException : public std::logic_error {
- InvalidExternalSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors);
-};
-} // namespace realm
-
-#endif /* defined(REALM_OBJECT_STORE_HPP) */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/placeholder.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/placeholder.cpp
deleted file mode 100644
index 893653421..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/placeholder.cpp
+++ /dev/null
@@ -1 +0,0 @@
-// This file is intentionally left blank.
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp
deleted file mode 100644
index 72198ae08..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp
+++ /dev/null
@@ -1,297 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_PROPERTY_HPP
-#define REALM_PROPERTY_HPP
-
-#include "util/tagged_bool.hpp"
-
-#include <realm/util/features.h>
-// FIXME: keys.hpp is currently pretty heavyweight
-#include <realm/keys.hpp>
-
-#include <string>
-
-namespace realm {
-namespace util {
- template<typename> class Optional;
-}
-class BinaryData;
-class Obj;
-class StringData;
-class Table;
-class Timestamp;
-
-enum class PropertyType : unsigned char {
- Int = 0,
- Bool = 1,
- String = 2,
- Data = 3,
- Date = 4,
- Float = 5,
- Double = 6,
- Object = 7, // currently must be either Array xor Nullable
- LinkingObjects = 8, // currently must be Array and not Nullable
-
- // deprecated and remains only for reading old files
- Any = 9,
-
- // Flags which can be combined with any of the above types except as noted
- Required = 0,
- Nullable = 64,
- Array = 128,
- Flags = Nullable | Array
-};
-
-struct Property {
- using IsPrimary = util::TaggedBool<class IsPrimaryTag>;
- using IsIndexed = util::TaggedBool<class IsIndexedTag>;
-
- // The internal column name used in the Realm file.
- std::string name;
-
- // The public name used by the binding to represent the internal column name in the Realm file. Bindings can use
- // this to expose a different name in the binding API, e.g. to map between different naming conventions.
- //
- // Public names are only ever user defined, they are not persisted on disk, so reading the schema from the file
- // will leave this field empty. If `public_name` is empty, the internal and public name are considered to be the same.
- //
- // ObjectStore will ensure that no conflicts occur between persisted properties and the public name, so
- // the public name is just as unique an identifier as the internal name in the file.
- //
- // In order to respect public names bindings should use `ObjectSchema::property_for_public_name()` in the schema
- // and `Object::value_for_property()` in the Object accessor for reading fields defined by the public name.
- //
- // For queries, bindings should provide an appropriate `KeyPathMapping` definition. Bindings are responsible
- // for creating this.
- std::string public_name;
- PropertyType type = PropertyType::Int;
- std::string object_type;
- std::string link_origin_property_name;
- IsPrimary is_primary = false;
- IsIndexed is_indexed = false;
-
- ColKey column_key;
-
- Property() = default;
-
- Property(std::string name, PropertyType type, IsPrimary primary = false,
- IsIndexed indexed = false, std::string public_name = "");
-
- Property(std::string name, PropertyType type, std::string object_type,
- std::string link_origin_property_name = "", std::string public_name = "");
-
- Property(Property const&) = default;
- Property(Property&&) noexcept = default;
- Property& operator=(Property const&) = default;
- Property& operator=(Property&&) noexcept = default;
-
- bool requires_index() const { return is_primary || is_indexed; }
-
- bool type_is_indexable() const noexcept;
- bool type_is_nullable() const noexcept;
-
- std::string type_string() const;
-};
-
-template<typename E>
-constexpr auto to_underlying(E e)
-{
- return static_cast<typename std::underlying_type<E>::type>(e);
-}
-
-inline constexpr PropertyType operator&(PropertyType a, PropertyType b)
-{
- return static_cast<PropertyType>(to_underlying(a) & to_underlying(b));
-}
-
-inline constexpr PropertyType operator|(PropertyType a, PropertyType b)
-{
- return static_cast<PropertyType>(to_underlying(a) | to_underlying(b));
-}
-
-inline constexpr PropertyType operator^(PropertyType a, PropertyType b)
-{
- return static_cast<PropertyType>(to_underlying(a) ^ to_underlying(b));
-}
-
-inline constexpr PropertyType operator~(PropertyType a)
-{
- return static_cast<PropertyType>(~to_underlying(a));
-}
-
-inline constexpr bool operator==(PropertyType a, PropertyType b)
-{
- return to_underlying(a & ~PropertyType::Flags) == to_underlying(b & ~PropertyType::Flags);
-}
-
-inline constexpr bool operator!=(PropertyType a, PropertyType b)
-{
- return !(a == b);
-}
-
-inline PropertyType& operator&=(PropertyType & a, PropertyType b)
-{
- a = a & b;
- return a;
-}
-
-inline PropertyType& operator|=(PropertyType & a, PropertyType b)
-{
- a = a | b;
- return a;
-}
-
-inline PropertyType& operator^=(PropertyType & a, PropertyType b)
-{
- a = a ^ b;
- return a;
-}
-
-inline constexpr bool is_array(PropertyType a)
-{
- return to_underlying(a & PropertyType::Array) == to_underlying(PropertyType::Array);
-}
-
-inline constexpr bool is_nullable(PropertyType a)
-{
- return to_underlying(a & PropertyType::Nullable) == to_underlying(PropertyType::Nullable);
-}
-
-// Some of the places we use switch_on_type() the Obj version isn't instantiatable
-// or reachable, so we want to map it to a valid type to let the unreachable code compile
-template<typename T>
-struct NonObjType {
- using type = std::remove_reference_t<T>;
-};
-template<>
-struct NonObjType<Obj&> {
- using type = int64_t;
-};
-template<typename T>
-using NonObjTypeT = typename NonObjType<T>::type;
-
-template<typename ObjType=Obj, typename Fn>
-static auto switch_on_type(PropertyType type, Fn&& fn)
-{
- using PT = PropertyType;
- bool is_optional = is_nullable(type);
- switch (type & ~PropertyType::Flags) {
- case PT::Int: return is_optional ? fn((util::Optional<int64_t>*)0) : fn((int64_t*)0);
- case PT::Bool: return is_optional ? fn((util::Optional<bool>*)0) : fn((bool*)0);
- case PT::Float: return is_optional ? fn((util::Optional<float>*)0) : fn((float*)0);
- case PT::Double: return is_optional ? fn((util::Optional<double>*)0) : fn((double*)0);
- case PT::String: return fn((StringData*)0);
- case PT::Data: return fn((BinaryData*)0);
- case PT::Date: return fn((Timestamp*)0);
- case PT::Object: return fn((ObjType*)0);
- default: REALM_COMPILER_HINT_UNREACHABLE();
- }
-}
-
-static const char *string_for_property_type(PropertyType type)
-{
- if (is_array(type)) {
- if (type == PropertyType::LinkingObjects)
- return "linking objects";
- return "array";
- }
- switch (type & ~PropertyType::Flags) {
- case PropertyType::String: return "string";
- case PropertyType::Int: return "int";
- case PropertyType::Bool: return "bool";
- case PropertyType::Date: return "date";
- case PropertyType::Data: return "data";
- case PropertyType::Double: return "double";
- case PropertyType::Float: return "float";
- case PropertyType::Object: return "object";
- case PropertyType::Any: return "any";
- case PropertyType::LinkingObjects: return "linking objects";
- default: REALM_COMPILER_HINT_UNREACHABLE();
- }
-}
-
-inline Property::Property(std::string name, PropertyType type,
- IsPrimary primary, IsIndexed indexed,
- std::string public_name)
-: name(std::move(name))
-, public_name(std::move(public_name))
-, type(type)
-, is_primary(primary)
-, is_indexed(indexed)
-{
-}
-
-inline Property::Property(std::string name, PropertyType type,
- std::string object_type,
- std::string link_origin_property_name,
- std::string public_name)
-: name(std::move(name))
-, public_name(std::move(public_name))
-, type(type)
-, object_type(std::move(object_type))
-, link_origin_property_name(std::move(link_origin_property_name))
-{
-}
-
-inline bool Property::type_is_indexable() const noexcept
-{
- return type == PropertyType::Int
- || type == PropertyType::Bool
- || type == PropertyType::Date
- || type == PropertyType::String;
-}
-
-inline bool Property::type_is_nullable() const noexcept
-{
- return !(is_array(type) && type == PropertyType::Object) && type != PropertyType::LinkingObjects;
-}
-
-inline std::string Property::type_string() const
-{
- if (is_array(type)) {
- if (type == PropertyType::Object)
- return "array<" + object_type + ">";
- if (type == PropertyType::LinkingObjects)
- return "linking objects<" + object_type + ">";
- return std::string("array<") + string_for_property_type(type & ~PropertyType::Flags) + ">";
- }
- switch (auto base_type = (type & ~PropertyType::Flags)) {
- case PropertyType::Object:
- return "<" + object_type + ">";
- case PropertyType::LinkingObjects:
- return "linking objects<" + object_type + ">";
- default:
- return string_for_property_type(base_type);
- }
-}
-
-inline bool operator==(Property const& lft, Property const& rgt)
-{
- // note: not checking column_key
- // ordered roughly by the cost of the check
- return to_underlying(lft.type) == to_underlying(rgt.type)
- && lft.is_primary == rgt.is_primary
- && lft.requires_index() == rgt.requires_index()
- && lft.name == rgt.name
- && lft.object_type == rgt.object_type
- && lft.link_origin_property_name == rgt.link_origin_property_name;
-}
-} // namespace realm
-
-#endif // REALM_PROPERTY_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp
deleted file mode 100644
index ea93e85da..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp
+++ /dev/null
@@ -1,1114 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "results.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "impl/results_notifier.hpp"
-#include "audit.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "schema.hpp"
-
-#include <stdexcept>
-
-namespace realm {
-
-Results::Results() = default;
-Results::~Results() = default;
-
-Results::Results(SharedRealm r, Query q, DescriptorOrdering o)
-: m_realm(std::move(r))
-, m_query(std::move(q))
-, m_table(m_query.get_table())
-, m_descriptor_ordering(std::move(o))
-, m_mode(Mode::Query)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
-}
-
-Results::Results(SharedRealm r, ConstTableRef table)
-: m_realm(std::move(r))
-, m_table(table)
-, m_mode(Mode::Table)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
-}
-
-Results::Results(std::shared_ptr<Realm> r, std::shared_ptr<LstBase> list)
-: m_realm(std::move(r))
-, m_list(list)
-, m_mode(Mode::List)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
-}
-
-Results::Results(std::shared_ptr<Realm> r, std::shared_ptr<LstBase> list, DescriptorOrdering o)
-: m_realm(std::move(r))
-, m_descriptor_ordering(std::move(o))
-, m_list(std::move(list))
-, m_mode(Mode::List)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
-}
-
-Results::Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o)
-: m_realm(std::move(r))
-, m_table_view(std::move(tv))
-, m_descriptor_ordering(std::move(o))
-, m_mode(Mode::TableView)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
- m_table = m_table_view.get_parent();
-}
-
-Results::Results(std::shared_ptr<Realm> r, std::shared_ptr<LnkLst> lv, util::Optional<Query> q, SortDescriptor s)
-: m_realm(std::move(r))
-, m_link_list(std::move(lv))
-, m_mode(Mode::LinkList)
-, m_mutex(m_realm && m_realm->is_frozen())
-{
- m_table = m_link_list->get_target_table();
- if (q) {
- m_query = std::move(*q);
- m_mode = Mode::Query;
- }
- m_descriptor_ordering.append_sort(std::move(s));
-}
-
-Results::Results(const Results&) = default;
-Results& Results::operator=(const Results&) = default;
-Results::Results(Results&&) = default;
-Results& Results::operator=(Results&&) = default;
-
-Results::Mode Results::get_mode() const noexcept
-{
- CheckedUniqueLock lock(m_mutex);
- return m_mode;
-}
-
-bool Results::is_valid() const
-{
- if (m_realm) {
- m_realm->verify_thread();
- }
-
- // Here we cannot just use if (m_table) as it combines a check if the
- // reference contains a value and if that value is valid.
- // First we check if a table is referenced ...
- if (m_table.unchecked_ptr() != nullptr)
- return !!m_table; // ... and then we check if it is valid
-
- if (m_list)
- return m_list->is_attached();
-
- return true;
-}
-
-void Results::validate_read() const
-{
- // is_valid ensures that we're on the correct thread.
- if (!is_valid())
- throw InvalidatedException();
-}
-
-void Results::validate_write() const
-{
- validate_read();
- if (!m_realm || !m_realm->is_in_transaction())
- throw InvalidTransactionException("Must be in a write transaction");
-}
-
-size_t Results::size()
-{
- util::CheckedUniqueLock lock(m_mutex);
- return do_size();
-}
-
-size_t Results::do_size()
-{
- validate_read();
- switch (m_mode) {
- case Mode::Empty: return 0;
- case Mode::Table: return m_table->size();
- case Mode::LinkList: return m_link_list->size();
- case Mode::List:
- evaluate_sort_and_distinct_on_list();
- return m_list_indices ? m_list_indices->size() : m_list->size();
- case Mode::Query:
- m_query.sync_view_if_needed();
- if (!m_descriptor_ordering.will_apply_distinct())
- return m_query.count(m_descriptor_ordering);
- REALM_FALLTHROUGH;
- case Mode::TableView:
- do_evaluate_query_if_needed();
- return m_table_view.size();
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-const ObjectSchema& Results::get_object_schema() const
-{
- validate_read();
-
- auto object_schema = m_object_schema.load();
- if (!object_schema) {
- REALM_ASSERT(m_realm);
- auto it = m_realm->schema().find(get_object_type());
- REALM_ASSERT(it != m_realm->schema().end());
- m_object_schema = object_schema = &*it;
- }
-
- return *object_schema;
-}
-
-StringData Results::get_object_type() const noexcept
-{
- if (!m_table) {
- return StringData();
- }
-
- return ObjectStore::object_type_for_table_name(m_table->get_name());
-}
-
-template<typename T>
-auto& Results::list_as() const
-{
- return static_cast<Lst<T>&>(*m_list);
-}
-
-void Results::evaluate_sort_and_distinct_on_list()
-{
- if (m_descriptor_ordering.is_empty())
- return;
-
- // We can't use the sorted list from the notifier if we're in a write
- // transaction as we only check the transaction version to see if the data matches
- if (m_notifier && m_notifier->get_list_indices(m_list_indices) && !m_realm->is_in_transaction())
- return;
-
- bool needs_update = m_list->has_changed();
- if (!m_list_indices) {
- m_list_indices = std::vector<size_t>{};
- needs_update = true;
- }
- if (!needs_update)
- return;
- if (m_list->is_empty()) {
- m_list_indices->clear();
- return;
- }
-
- util::Optional<bool> sort_order;
- bool do_distinct = false;
- auto sz = m_descriptor_ordering.size();
- for (size_t i = 0; i < sz; i++) {
- auto descr = m_descriptor_ordering[i];
- if (descr->get_type() == DescriptorType::Sort)
- sort_order = static_cast<const SortDescriptor*>(descr)->is_ascending(0);
- if (descr->get_type() == DescriptorType::Distinct)
- do_distinct = true;
- }
-
- if (do_distinct)
- m_list->distinct(*m_list_indices, sort_order);
- else if (sort_order)
- m_list->sort(*m_list_indices, *sort_order);
-}
-
-template<typename T>
-util::Optional<T> Results::try_get(size_t ndx)
-{
- validate_read();
- if (m_mode == Mode::List) {
- evaluate_sort_and_distinct_on_list();
- if (m_list_indices) {
- if (ndx < m_list_indices->size())
- return list_as<T>().get((*m_list_indices)[ndx]);
- }
- else {
- if (ndx < m_list->size())
- return list_as<T>().get(ndx);
- }
- }
- return util::none;
-}
-
-Results::IteratorWrapper::IteratorWrapper(IteratorWrapper const& rgt)
-{
- *this = rgt;
-}
-
-Results::IteratorWrapper& Results::IteratorWrapper::operator=(IteratorWrapper const& rgt)
-{
- if (rgt.m_it)
- m_it = std::make_unique<Table::ConstIterator>(*rgt.m_it);
- return *this;
-}
-
-Obj Results::IteratorWrapper::get(Table const& table, size_t ndx)
-{
- // Using a Table iterator is much faster for repeated access into a table
- // than indexing into it as the iterator caches the cluster the last accessed
- // object is stored in.
- if (!m_it && table.size() > 5) {
- m_it = std::make_unique<Table::ConstIterator>(table.begin());
- }
- if (!m_it) {
- return const_cast<Table&>(table).get_object(ndx);
- }
- try {
- return (*m_it)[ndx];
- }
- catch (...) {
- // Iterator might be outdated
- m_it = std::make_unique<Table::ConstIterator>(table.begin());
- return (*m_it)[ndx];
- }
-}
-
-template<>
-util::Optional<Obj> Results::try_get(size_t row_ndx)
-{
- validate_read();
- switch (m_mode) {
- case Mode::Empty:
- case Mode::List:
- break;
- case Mode::Table:
- if (row_ndx < m_table->size())
- return m_table_iterator.get(*m_table, row_ndx);
- break;
- case Mode::LinkList:
- if (update_linklist()) {
- if (row_ndx < m_link_list->size())
- return m_link_list->get_object(row_ndx);
- break;
- }
- REALM_FALLTHROUGH;
- case Mode::Query:
- case Mode::TableView:
- do_evaluate_query_if_needed();
- if (row_ndx >= m_table_view.size())
- break;
- if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_obj_valid(row_ndx))
- return Obj{};
- return m_table_view.get(row_ndx);
- }
- return util::none;
-}
-
-template<typename T>
-T Results::get(size_t row_ndx)
-{
- util::CheckedUniqueLock lock(m_mutex);
- if (auto row = try_get<T>(row_ndx)) {
- return *row;
- }
- throw OutOfBoundsIndexException{row_ndx, do_size()};
-}
-
-template<typename T>
-util::Optional<T> Results::first()
-{
- util::CheckedUniqueLock lock(m_mutex);
- return try_get<T>(0);
-}
-
-template<typename T>
-util::Optional<T> Results::last()
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- if (m_mode == Mode::Query)
- do_evaluate_query_if_needed(); // avoid running the query twice (for size() and for get())
- return try_get<T>(do_size() - 1);
-}
-
-bool Results::update_linklist()
-{
- REALM_ASSERT(m_update_policy == UpdatePolicy::Auto);
-
- if (!m_descriptor_ordering.is_empty()) {
- m_query = do_get_query();
- m_mode = Mode::Query;
- do_evaluate_query_if_needed();
- return false;
- }
- return true;
-}
-
-void Results::evaluate_query_if_needed(bool wants_notifications)
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- do_evaluate_query_if_needed(wants_notifications);
-}
-
-void Results::do_evaluate_query_if_needed(bool wants_notifications)
-{
- if (m_update_policy == UpdatePolicy::Never) {
- REALM_ASSERT(m_mode == Mode::TableView);
- return;
- }
-
- switch (m_mode) {
- case Mode::Empty:
- case Mode::Table:
- case Mode::List:
- case Mode::LinkList:
- return;
- case Mode::Query:
- if (m_notifier && m_notifier->get_tableview(m_table_view)) {
- m_mode = Mode::TableView;
- break;
- }
- m_query.sync_view_if_needed();
- if (m_update_policy == UpdatePolicy::Auto)
- m_table_view = m_query.find_all(m_descriptor_ordering);
- m_mode = Mode::TableView;
- REALM_FALLTHROUGH;
- case Mode::TableView:
- if (wants_notifications && !m_notifier)
- prepare_async(ForCallback{false});
- else if (m_notifier)
- m_notifier->get_tableview(m_table_view);
- if (m_update_policy == UpdatePolicy::Auto)
- m_table_view.sync_if_needed();
- if (auto audit = m_realm->audit_context())
- audit->record_query(m_realm->read_transaction_version(), m_table_view);
- break;
- }
-}
-
-template<>
-size_t Results::index_of(Obj const& row)
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- if (!row.is_valid()) {
- throw DetatchedAccessorException{};
- }
- if (m_table && row.get_table() != m_table) {
- throw IncorrectTableException(
- ObjectStore::object_type_for_table_name(m_table->get_name()),
- ObjectStore::object_type_for_table_name(row.get_table()->get_name()),
- "Attempting to get the index of a Row of the wrong type"
- );
- }
-
- switch (m_mode) {
- case Mode::Empty:
- case Mode::List:
- return not_found;
- case Mode::Table:
- return m_table->get_object_ndx(row.get_key());
- case Mode::LinkList:
- if (update_linklist())
- return m_link_list->Lst<ObjKey>::find_first(row.get_key());
- REALM_FALLTHROUGH;
- case Mode::Query:
- case Mode::TableView:
- do_evaluate_query_if_needed();
- return m_table_view.find_by_source_ndx(row.get_key());
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-template<typename T>
-size_t Results::index_of(T const& value)
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- if (m_mode != Mode::List)
- return not_found; // Non-List results can only ever contain Objects
- evaluate_sort_and_distinct_on_list();
- if (m_list_indices) {
- for (size_t i = 0; i < m_list_indices->size(); ++i) {
- if (list_as<T>().get((*m_list_indices)[i]) == value)
- return i;
- }
- return not_found;
- }
- return list_as<T>().find_first(value);
-}
-
-size_t Results::index_of(Query&& q)
-{
- if (m_descriptor_ordering.will_apply_sort()) {
- Results filtered(filter(std::move(q)));
- filtered.assert_unlocked();
- auto first = filtered.first();
- return first ? index_of(*first) : not_found;
- }
-
- auto query = get_query().and_query(std::move(q));
- query.sync_view_if_needed();
- ObjKey row = query.find();
- return row ? index_of(const_cast<Table&>(*m_table).get_object(row)) : not_found;
-}
-
-DataType Results::prepare_for_aggregate(ColKey column, const char* name)
-{
- DataType type;
- switch (m_mode) {
- case Mode::Table:
- type = m_table->get_column_type(column);
- break;
- case Mode::List:
- type = m_list->get_table()->get_column_type(m_list->get_col_key());
- break;
- case Mode::LinkList:
- m_query = do_get_query();
- m_mode = Mode::Query;
- REALM_FALLTHROUGH;
- case Mode::Query:
- case Mode::TableView:
- do_evaluate_query_if_needed();
- type = m_table->get_column_type(column);
- break;
- default:
- REALM_COMPILER_HINT_UNREACHABLE();
- }
- switch (type) {
- case type_Timestamp: case type_Double: case type_Float: case type_Int: break;
- default: throw UnsupportedColumnTypeException{column, *m_table, name};
- }
- return type;
-}
-
-namespace {
-template<typename T, typename Table>
-struct AggregateHelper;
-
-template<typename Table>
-struct AggregateHelper<int64_t, Table> {
- Table& table;
- Mixed min(ColKey col, ObjKey* obj) { return table.minimum_int(col, obj); }
- Mixed max(ColKey col, ObjKey* obj) { return table.maximum_int(col, obj); }
- Mixed sum(ColKey col) { return table.sum_int(col); }
- Mixed avg(ColKey col, size_t* count) { return table.average_int(col, count); }
-};
-
-template<typename Table>
-struct AggregateHelper<double, Table> {
- Table& table;
- Mixed min(ColKey col, ObjKey* obj) { return table.minimum_double(col, obj); }
- Mixed max(ColKey col, ObjKey* obj) { return table.maximum_double(col, obj); }
- Mixed sum(ColKey col) { return table.sum_double(col); }
- Mixed avg(ColKey col, size_t* count) { return table.average_double(col, count); }
-};
-
-template<typename Table>
-struct AggregateHelper<float, Table> {
- Table& table;
- Mixed min(ColKey col, ObjKey* obj) { return table.minimum_float(col, obj); }
- Mixed max(ColKey col, ObjKey* obj) { return table.maximum_float(col, obj); }
- Mixed sum(ColKey col) { return table.sum_float(col); }
- Mixed avg(ColKey col, size_t* count) { return table.average_float(col, count); }
-};
-
-template<typename Table>
-struct AggregateHelper<Timestamp, Table> {
- Table& table;
- Mixed min(ColKey col, ObjKey* obj) { return table.minimum_timestamp(col, obj); }
- Mixed max(ColKey col, ObjKey* obj) { return table.maximum_timestamp(col, obj); }
- Mixed sum(ColKey col) { throw Results::UnsupportedColumnTypeException{col, table, "sum"}; }
- Mixed avg(ColKey col, size_t*) { throw Results::UnsupportedColumnTypeException{col, table, "avg"}; }
-};
-
-struct ListAggregateHelper {
- LstBase& list;
- Mixed min(ColKey, size_t* ndx) { return list.min(ndx); }
- Mixed max(ColKey, size_t* ndx) { return list.max(ndx); }
- Mixed sum(ColKey) { return list.sum(); }
- Mixed avg(ColKey, size_t* count) { return list.avg(count); }
-};
-
-template<> struct AggregateHelper<int64_t, LstBase&> : ListAggregateHelper
-{ AggregateHelper(LstBase& l) : ListAggregateHelper{l} {} };
-template<> struct AggregateHelper<double, LstBase&> : ListAggregateHelper
-{ AggregateHelper(LstBase& l) : ListAggregateHelper{l} {} };
-template<> struct AggregateHelper<float, LstBase&> : ListAggregateHelper
-{ AggregateHelper(LstBase& l) : ListAggregateHelper{l} {} };
-
-template<>
-struct AggregateHelper<Timestamp, LstBase&> : ListAggregateHelper {
- AggregateHelper(LstBase& l) : ListAggregateHelper{l} {}
- Mixed sum(ColKey) { throw Results::UnsupportedColumnTypeException{list.get_col_key(), *list.get_table(), "sum"}; }
- Mixed avg(ColKey, size_t*) { throw Results::UnsupportedColumnTypeException{list.get_col_key(), *list.get_table(), "avg"}; }
-};
-
-template<typename Table, typename Func>
-Mixed call_with_helper(Func&& func, Table&& table, DataType type)
-{
- switch (type) {
- case type_Timestamp: return func(AggregateHelper<Timestamp, Table>{table});
- case type_Double: return func(AggregateHelper<double, Table>{table});
- case type_Float: return func(AggregateHelper<Float, Table>{table});
- case type_Int: return func(AggregateHelper<Int, Table>{table});
- default: REALM_COMPILER_HINT_UNREACHABLE();
- }
-}
-
-struct ReturnIndexHelper {
- ObjKey key;
- size_t index = npos;
- operator ObjKey*() { return &key; }
- operator size_t*() { return &index; }
- operator bool() { return key || index != npos; }
-};
-} // anonymous namespace
-
-template<typename AggregateFunction>
-util::Optional<Mixed> Results::aggregate(ColKey column, const char* name,
- AggregateFunction&& func)
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- if (!m_table && !m_list)
- return none;
-
- auto type = prepare_for_aggregate(column, name);
- switch (m_mode) {
- case Mode::Table:
- return call_with_helper(func, *m_table, type);
- case Mode::List:
- return call_with_helper(func, *m_list, type);
- default:
- return call_with_helper(func, m_table_view, type);
- }
-}
-
-util::Optional<Mixed> Results::max(ColKey column)
-{
- ReturnIndexHelper return_ndx;
- auto results = aggregate(column, "max", [&](auto&& helper) {
- return helper.max(column, return_ndx);
- });
- return return_ndx ? results : none;
-}
-
-util::Optional<Mixed> Results::min(ColKey column)
-{
- ReturnIndexHelper return_ndx;
- auto results = aggregate(column, "min", [&](auto&& helper) {
- return helper.min(column, return_ndx);
- });
- return return_ndx ? results : none;
-}
-
-util::Optional<Mixed> Results::sum(ColKey column)
-{
- return aggregate(column, "sum", [&](auto&& helper) { return helper.sum(column); });
-}
-
-util::Optional<double> Results::average(ColKey column)
-{
- size_t value_count = 0;
- auto results = aggregate(column, "avg", [&](auto&& helper) {
- return helper.avg(column, &value_count);
- });
- return value_count == 0 ? none : util::make_optional(results->get_double());
-}
-
-void Results::clear()
-{
- util::CheckedUniqueLock lock(m_mutex);
- switch (m_mode) {
- case Mode::Empty:
- return;
- case Mode::Table:
- validate_write();
- if (m_realm->is_partial())
- m_table->where().find_all().clear();
- else
- const_cast<Table&>(*m_table).clear();
- break;
- case Mode::Query:
- // Not using Query:remove() because building the tableview and
- // clearing it is actually significantly faster
- case Mode::TableView:
- validate_write();
- do_evaluate_query_if_needed();
-
- switch (m_update_policy) {
- case UpdatePolicy::Auto:
- m_table_view.clear();
- break;
- case UpdatePolicy::AsyncOnly:
- case UpdatePolicy::Never: {
- // Copy the TableView because a frozen Results shouldn't let its size() change.
- TableView copy(m_table_view);
- copy.clear();
- break;
- }
- }
- break;
- case Mode::List:
- validate_write();
- m_list->clear();
- break;
- case Mode::LinkList:
- validate_write();
- m_link_list->remove_all_target_rows();
- break;
- }
-}
-
-PropertyType Results::get_type() const
-{
- util::CheckedUniqueLock lock(m_mutex);
- return do_get_type();
-}
-
-PropertyType Results::do_get_type() const
-{
- validate_read();
- switch (m_mode) {
- case Mode::Empty:
- case Mode::LinkList:
- return PropertyType::Object;
- case Mode::Query:
- case Mode::TableView:
- case Mode::Table:
- return PropertyType::Object;
- case Mode::List:
- return ObjectSchema::from_core_type(*m_list->get_table(), m_list->get_col_key());
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-Query Results::get_query() const
-{
- util::CheckedUniqueLock lock(m_mutex);
- return do_get_query();
-}
-
-Query Results::do_get_query() const
-{
- validate_read();
- switch (m_mode) {
- case Mode::Empty:
- case Mode::Query:
- case Mode::List:
- return m_query;
- case Mode::TableView: {
- if (const_cast<Query&>(m_query).get_table())
- return m_query;
-
- // A TableView has an associated Query if it was produced by Query::find_all. This is indicated
- // by TableView::get_query returning a Query with a non-null table.
- Query query = m_table_view.get_query();
- if (query.get_table()) {
- return query;
- }
-
- // The TableView has no associated query so create one with no conditions that is restricted
- // to the rows in the TableView.
- if (m_update_policy == UpdatePolicy::Auto) {
- m_table_view.sync_if_needed();
- }
- return Query(m_table, std::unique_ptr<ConstTableView>(new TableView(m_table_view)));
- }
- case Mode::LinkList:
- return m_table->where(*m_link_list);
- case Mode::Table:
- return m_table->where();
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-TableView Results::get_tableview()
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- switch (m_mode) {
- case Mode::Empty:
- case Mode::List:
- return {};
- case Mode::LinkList:
- if (update_linklist())
- return m_table->where(*m_link_list).find_all();
- REALM_FALLTHROUGH;
- case Mode::Query:
- case Mode::TableView:
- do_evaluate_query_if_needed();
- return m_table_view;
- case Mode::Table:
- return m_table->where().find_all();
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-static std::vector<ColKey> parse_keypath(StringData keypath, Schema const& schema,
- const ObjectSchema *object_schema)
-{
- auto check = [&](bool condition, const char* fmt, auto... args) {
- if (!condition) {
- throw std::invalid_argument(util::format("Cannot sort on key path '%1': %2.",
- keypath, util::format(fmt, args...)));
- }
- };
- auto is_sortable_type = [](PropertyType type) {
- return !is_array(type) && type != PropertyType::LinkingObjects && type != PropertyType::Data;
- };
-
- const char* begin = keypath.data();
- const char* end = keypath.data() + keypath.size();
- check(begin != end, "missing property name");
-
- std::vector<ColKey> indices;
- while (begin != end) {
- auto sep = std::find(begin, end, '.');
- check(sep != begin && sep + 1 != end, "missing property name");
- StringData key(begin, sep - begin);
- begin = sep + (sep != end);
-
- auto prop = object_schema->property_for_name(key);
- check(prop, "property '%1.%2' does not exist", object_schema->name, key);
- check(is_sortable_type(prop->type), "property '%1.%2' is of unsupported type '%3'",
- object_schema->name, key, string_for_property_type(prop->type));
- if (prop->type == PropertyType::Object)
- check(begin != end, "property '%1.%2' of type 'object' cannot be the final property in the key path",
- object_schema->name, key);
- else
- check(begin == end, "property '%1.%2' of type '%3' may only be the final property in the key path",
- object_schema->name, key, prop->type_string());
-
- indices.push_back(ColKey(prop->column_key));
- if (prop->type == PropertyType::Object)
- object_schema = &*schema.find(prop->object_type);
- }
- return indices;
-}
-
-Results Results::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
-{
- if (keypaths.empty())
- return *this;
- auto type = get_type();
- if (type != PropertyType::Object) {
- if (keypaths.size() != 1)
- throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path",
- string_for_property_type(type & ~PropertyType::Flags)));
- if (keypaths[0].first != "self")
- throw std::invalid_argument(
- util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'",
- keypaths[0].first, string_for_property_type(type & ~PropertyType::Flags)));
- return sort({{{}}, {keypaths[0].second}});
- }
-
- std::vector<std::vector<ColKey>> column_keys;
- std::vector<bool> ascending;
- column_keys.reserve(keypaths.size());
- ascending.reserve(keypaths.size());
-
- for (auto& keypath : keypaths) {
- column_keys.push_back(parse_keypath(keypath.first, m_realm->schema(),
- &get_object_schema()));
- ascending.push_back(keypath.second);
- }
- return sort({std::move(column_keys), std::move(ascending)});
-}
-
-Results Results::sort(SortDescriptor&& sort) const
-{
- util::CheckedUniqueLock lock(m_mutex);
- DescriptorOrdering new_order = m_descriptor_ordering;
- new_order.append_sort(std::move(sort));
- if (m_mode == Mode::LinkList)
- return Results(m_realm, m_link_list, util::none, std::move(sort));
- else if (m_mode == Mode::List)
- return Results(m_realm, m_list, std::move(new_order));
- return Results(m_realm, do_get_query(), std::move(new_order));
-}
-
-Results Results::filter(Query&& q) const
-{
- if (m_descriptor_ordering.will_apply_limit())
- throw UnimplementedOperationException("Filtering a Results with a limit is not yet implemented");
- return Results(m_realm, get_query().and_query(std::move(q)), m_descriptor_ordering);
-}
-
-Results Results::limit(size_t max_count) const
-{
- auto new_order = m_descriptor_ordering;
- new_order.append_limit(max_count);
- return Results(m_realm, get_query(), std::move(new_order));
-}
-
-Results Results::apply_ordering(DescriptorOrdering&& ordering)
-{
- DescriptorOrdering new_order = m_descriptor_ordering;
- for (size_t i = 0; i < ordering.size(); ++i) {
- switch (ordering.get_type(i)) {
- case DescriptorType::Sort: {
- auto sort = dynamic_cast<const SortDescriptor*>(ordering[i]);
- new_order.append_sort(std::move(*sort));
- break;
- }
- case DescriptorType::Distinct: {
- auto distinct = dynamic_cast<const DistinctDescriptor*>(ordering[i]);
- new_order.append_distinct(std::move(*distinct));
- break;
- }
- case DescriptorType::Limit: {
- auto limit = dynamic_cast<const LimitDescriptor*>(ordering[i]);
- new_order.append_limit(std::move(*limit));
- break;
- }
- case DescriptorType::Include: {
- auto include = dynamic_cast<const IncludeDescriptor*>(ordering[i]);
- new_order.append_include(std::move(*include));
- break;
- }
- }
- }
- return Results(m_realm, get_query(), std::move(new_order));
-}
-
-Results Results::distinct(DistinctDescriptor&& uniqueness) const
-{
- DescriptorOrdering new_order = m_descriptor_ordering;
- new_order.append_distinct(std::move(uniqueness));
- util::CheckedUniqueLock lock(m_mutex);
- if (m_mode == Mode::List)
- return Results(m_realm, m_list, std::move(new_order));
- return Results(m_realm, do_get_query(), std::move(new_order));
-}
-
-Results Results::distinct(std::vector<std::string> const& keypaths) const
-{
- if (keypaths.empty())
- return *this;
- auto type = get_type();
- if (type != PropertyType::Object) {
- if (keypaths.size() != 1)
- throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path",
- string_for_property_type(type & ~PropertyType::Flags)));
- if (keypaths[0] != "self")
- throw std::invalid_argument(
- util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'", keypaths[0],
- string_for_property_type(type & ~PropertyType::Flags)));
- return distinct(DistinctDescriptor({{ColKey()}}));
- }
-
- std::vector<std::vector<ColKey>> column_keys;
- column_keys.reserve(keypaths.size());
- for (auto& keypath : keypaths)
- column_keys.push_back(parse_keypath(keypath, m_realm->schema(), &get_object_schema()));
- return distinct({std::move(column_keys)});
-}
-
-Results Results::snapshot() const&
-{
- validate_read();
- auto clone = *this;
- clone.assert_unlocked();
- return static_cast<Results&&>(clone).snapshot();
-}
-
-Results Results::snapshot() &&
-{
- util::CheckedUniqueLock lock(m_mutex);
- validate_read();
- switch (m_mode) {
- case Mode::Empty:
- return Results();
-
- case Mode::Table:
- case Mode::LinkList:
- m_query = do_get_query();
- m_mode = Mode::Query;
-
- REALM_FALLTHROUGH;
- case Mode::Query:
- case Mode::TableView:
- case Mode::List: // FIXME Correct?
- do_evaluate_query_if_needed(false);
- m_notifier.reset();
- m_update_policy = UpdatePolicy::Never;
- return std::move(*this);
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-// This function cannot be called on frozen results and so does not require locking
-void Results::prepare_async(ForCallback force) NO_THREAD_SAFETY_ANALYSIS
-{
- REALM_ASSERT(m_realm);
- if (m_notifier)
- return;
- if (!m_realm->verify_notifications_available(force))
- return;
- if (m_update_policy == UpdatePolicy::Never) {
- if (force)
- throw std::logic_error("Cannot create asynchronous query for snapshotted Results.");
- return;
- }
-
- REALM_ASSERT(!force || !m_realm->is_frozen());
- if (!force) {
- // Don't do implicit background updates if we can't actually deliver them
- if (!m_realm->can_deliver_notifications())
- return;
- // Don't do implicit background updates if there isn't actually anything
- // that needs to be run.
- if (!m_query.get_table() && m_descriptor_ordering.is_empty())
- return;
- }
-
- if (m_list)
- m_notifier = std::make_shared<_impl::ListResultsNotifier>(*this);
- else
- m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
- _impl::RealmCoordinator::register_notifier(m_notifier);
-}
-
-NotificationToken Results::add_notification_callback(CollectionChangeCallback cb) &
-{
- prepare_async(ForCallback{true});
- return {m_notifier, m_notifier->add_callback(std::move(cb))};
-}
-
-// This function cannot be called on frozen results and so does not require locking
-bool Results::is_in_table_order() const NO_THREAD_SAFETY_ANALYSIS
-{
- REALM_ASSERT(!m_realm || !m_realm->is_frozen());
- switch (m_mode) {
- case Mode::Empty:
- case Mode::Table:
- case Mode::List:
- return true;
- case Mode::LinkList:
- return false;
- case Mode::Query:
- return m_query.produces_results_in_table_order()
- && !m_descriptor_ordering.will_apply_sort();
- case Mode::TableView:
- return m_table_view.is_in_table_order();
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-ColKey Results::key(StringData name) const
-{
- return m_table->get_column_key(name);
-}
-#define REALM_RESULTS_TYPE(T) \
- template T Results::get<T>(size_t); \
- template util::Optional<T> Results::first<T>(); \
- template util::Optional<T> Results::last<T>(); \
- template size_t Results::index_of<T>(T const&);
-
-template Obj Results::get<Obj>(size_t);
-template util::Optional<Obj> Results::first<Obj>();
-template util::Optional<Obj> Results::last<Obj>();
-
-REALM_RESULTS_TYPE(bool)
-REALM_RESULTS_TYPE(int64_t)
-REALM_RESULTS_TYPE(float)
-REALM_RESULTS_TYPE(double)
-REALM_RESULTS_TYPE(StringData)
-REALM_RESULTS_TYPE(BinaryData)
-REALM_RESULTS_TYPE(Timestamp)
-REALM_RESULTS_TYPE(util::Optional<bool>)
-REALM_RESULTS_TYPE(util::Optional<int64_t>)
-REALM_RESULTS_TYPE(util::Optional<float>)
-REALM_RESULTS_TYPE(util::Optional<double>)
-
-#undef REALM_RESULTS_TYPE
-
-Results Results::freeze(std::shared_ptr<Realm> const& frozen_realm)
-{
- util::CheckedUniqueLock lock(m_mutex);
- if (m_mode == Mode::Empty)
- return *this;
- switch (m_mode) {
- case Mode::Table:
- return Results(frozen_realm, frozen_realm->import_copy_of(m_table));
- case Mode::List:
- return Results(frozen_realm, frozen_realm->import_copy_of(*m_list), m_descriptor_ordering);
- case Mode::LinkList: {
- std::shared_ptr<LnkLst> frozen_ll(frozen_realm->import_copy_of(std::make_unique<LnkLst>(*m_link_list)).release());
-
- // If query/sort was provided for the original Results, mode would have changed to Query, so no need
- // include them here.
- return Results(frozen_realm, std::move(frozen_ll));
- }
- case Mode::Query:
- return Results(frozen_realm, *frozen_realm->import_copy_of(m_query, PayloadPolicy::Copy), m_descriptor_ordering);
- case Mode::TableView: {
- Results results(frozen_realm, *frozen_realm->import_copy_of(m_table_view, PayloadPolicy::Copy), m_descriptor_ordering);
- results.assert_unlocked();
- results.evaluate_query_if_needed(false);
- return results;
- }
- default:
- REALM_COMPILER_HINT_UNREACHABLE();
- }
-}
-
-bool Results::is_frozen()
-{
- return !m_realm || m_realm->is_frozen();
-}
-
-Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
-: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1))
-, requested(r), valid_count(c) {}
-
-static std::string unsupported_operation_msg(ColKey column, Table const& table, const char* operation)
-{
- auto type = ObjectSchema::from_core_type(table, column);
- const char* column_type = string_for_property_type(type & ~PropertyType::Array);
- if (!is_array(type))
- return util::format("Cannot %1 property '%2': operation not supported for '%3' properties",
- operation, table.get_column_name(column), column_type);
- return util::format("Cannot %1 '%2' array: operation not supported",
- operation, column_type);
-}
-
-Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(ColKey column, Table const& table,
- const char* operation)
-: std::logic_error(unsupported_operation_msg(column, table, operation))
-, column_key(column)
-, column_name(table.get_column_name(column))
-, property_type(ObjectSchema::from_core_type(table, ColKey(column)) & ~PropertyType::Array)
-{
-}
-
-Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(ColKey column, TableView const& tv,
- const char* operation)
-: UnsupportedColumnTypeException(column, tv.ObjList::get_parent(), operation)
-{
-}
-
-Results::InvalidPropertyException::InvalidPropertyException(StringData object_type, StringData property_name)
-: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name))
-, object_type(object_type), property_name(property_name)
-{
-}
-
-Results::UnimplementedOperationException::UnimplementedOperationException(const char* msg)
-: std::logic_error(msg)
-{
-}
-
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp
deleted file mode 100644
index e84e8c85d..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp
+++ /dev/null
@@ -1,393 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_RESULTS_HPP
-#define REALM_RESULTS_HPP
-
-#include "collection_notifications.hpp"
-#include "impl/collection_notifier.hpp"
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-#include "shared_realm.hpp"
-#include "util/checked_mutex.hpp"
-#include "util/copyable_atomic.hpp"
-
-#include <realm/table_view.hpp>
-#include <realm/util/optional.hpp>
-
-namespace realm {
-class Mixed;
-class ObjectSchema;
-
-namespace _impl {
- class ResultsNotifierBase;
-}
-
-class Results {
-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();
- Results(std::shared_ptr<Realm> r, ConstTableRef table);
- Results(std::shared_ptr<Realm> r, std::shared_ptr<LstBase> list);
- Results(std::shared_ptr<Realm> r, std::shared_ptr<LstBase> list, DescriptorOrdering o);
- Results(std::shared_ptr<Realm> r, Query q, DescriptorOrdering o = {});
- Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o = {});
- Results(std::shared_ptr<Realm> r, std::shared_ptr<LnkLst> list, util::Optional<Query> q = {}, SortDescriptor s = {});
- ~Results();
-
- // Results is copyable and moveable
- Results(Results&&);
- Results& operator=(Results&&);
- Results(const Results&);
- Results& operator=(const Results&);
-
- // Get the Realm
- std::shared_ptr<Realm> get_realm() const { return m_realm; }
-
- // Object schema describing the vendored object type
- const ObjectSchema &get_object_schema() const REQUIRES(!m_mutex);
-
- // 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
- Query get_query() const REQUIRES(!m_mutex);
-
- // Get the Lst this Results is derived from, if any
- std::shared_ptr<LstBase> const& get_list() const { return m_list; }
-
- // Get the list of sort and distinct operations applied for this Results.
- DescriptorOrdering const& get_descriptor_ordering() const noexcept { return m_descriptor_ordering; }
-
- // Get a tableview containing the same rows as this Results
- TableView get_tableview() REQUIRES(!m_mutex);
-
- // Get the object type which will be returned by get()
- StringData get_object_type() const noexcept;
-
- PropertyType get_type() const REQUIRES(!m_mutex);
-
- // Get the size of this results
- // Can be either O(1) or O(N) depending on the state of things
- size_t size() REQUIRES(!m_mutex);
-
- // Get the row accessor for the given index
- // Throws OutOfBoundsIndexException if index >= size()
- template<typename T = Obj>
- T get(size_t index) REQUIRES(!m_mutex);
-
- // Get the boxed row accessor for the given index
- // Throws OutOfBoundsIndexException if index >= size()
- template<typename Context>
- auto get(Context&, size_t index) REQUIRES(!m_mutex);
-
- // Get a row accessor for the first/last row, or none if the results are empty
- // More efficient than calling size()+get()
- template<typename T = Obj>
- util::Optional<T> first() REQUIRES(!m_mutex);
- template<typename T = Obj>
- util::Optional<T> last() REQUIRES(!m_mutex);
-
- // Get the index of the first row matching the query in this table
- size_t index_of(Query&& q) REQUIRES(!m_mutex);
-
- // Get the first index of the given value in this results, or not_found
- // Throws DetachedAccessorException if row is not attached
- // Throws IncorrectTableException if row belongs to a different table
- template<typename T>
- size_t index_of(T const& value) REQUIRES(!m_mutex);
-
- // Delete all of the rows in this Results from the Realm
- // size() will always be zero afterwards
- // Throws InvalidTransactionException if not in a write transaction
- void clear() REQUIRES(!m_mutex);
-
- // Create a new Results by further filtering or sorting this Results
- Results filter(Query&& q) const REQUIRES(!m_mutex);
- Results sort(SortDescriptor&& sort) const REQUIRES(!m_mutex);
- Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const REQUIRES(!m_mutex);
-
- // Create a new Results by removing duplicates
- Results distinct(DistinctDescriptor&& uniqueness) const REQUIRES(!m_mutex);
- Results distinct(std::vector<std::string> const& keypaths) const REQUIRES(!m_mutex);
-
- // Create a new Results with only the first `max_count` entries
- Results limit(size_t max_count) const REQUIRES(!m_mutex);
-
- // Create a new Results by adding sort and distinct combinations
- Results apply_ordering(DescriptorOrdering&& ordering) REQUIRES(!m_mutex);
-
- // Return a snapshot of this Results that never updates to reflect changes in the underlying data.
- Results snapshot() const& REQUIRES(!m_mutex);
- Results snapshot() && REQUIRES(!m_mutex);
-
- // Returns a frozen copy of this result
- Results freeze(std::shared_ptr<Realm> const& realm) REQUIRES(!m_mutex);
-
- // Returns whether or not this Results is frozen.
- bool is_frozen() REQUIRES(!m_mutex);
-
- // 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
- // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
- // Throws OutOfBoundsIndexException for an out-of-bounds column
- util::Optional<Mixed> max(ColKey column={}) REQUIRES(!m_mutex);
- util::Optional<Mixed> min(ColKey column={}) REQUIRES(!m_mutex);
- util::Optional<double> average(ColKey column={}) REQUIRES(!m_mutex);
- util::Optional<Mixed> sum(ColKey column={}) REQUIRES(!m_mutex);
-
- util::Optional<Mixed> max(StringData column_name) REQUIRES(!m_mutex) { return max(key(column_name)); }
- util::Optional<Mixed> min(StringData column_name) REQUIRES(!m_mutex) { return min(key(column_name)); }
- util::Optional<double> average(StringData column_name) REQUIRES(!m_mutex) { return average(key(column_name)); }
- util::Optional<Mixed> sum(StringData column_name) REQUIRES(!m_mutex) { return sum(key(column_name)); }
-
- enum class Mode {
- Empty, // Backed by nothing (for missing tables)
- Table, // Backed directly by a Table
- List, // Backed by a list-of-primitives that is not a link list.
- Query, // Backed by a query that has not yet been turned into a TableView
- LinkList, // Backed directly by a LinkList
- 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 noexcept REQUIRES(!m_mutex);
-
- // 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::logic_error {
- InvalidatedException() : std::logic_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);
- const size_t requested;
- const size_t valid_count;
- };
-
- // The input Row object is not attached
- 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::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::logic_error {
- ColKey column_key;
- StringData column_name;
- PropertyType property_type;
-
- UnsupportedColumnTypeException(ColKey column, Table const& table, const char* operation);
- UnsupportedColumnTypeException(ColKey column, TableView const& tv, const char* operation);
- };
-
- // The property request does not exist in the schema
- struct InvalidPropertyException : public std::logic_error {
- InvalidPropertyException(StringData object_type, StringData property_name);
- const std::string object_type;
- const std::string property_name;
- };
-
- // The requested operation is valid, but has not yet been implemented
- struct UnimplementedOperationException : public std::logic_error {
- UnimplementedOperationException(const char *message);
- };
-
- // Create an async query from this Results
- // The query will be run on a background thread and delivered to the callback,
- // and then rerun after each commit (if needed) and redelivered if it changed
- NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
-
- // Returns whether the rows are guaranteed to be in table order.
- bool is_in_table_order() const;
-
- template<typename Context> auto first(Context&) REQUIRES(!m_mutex);
- template<typename Context> auto last(Context&) REQUIRES(!m_mutex);
-
- template<typename Context, typename T>
- size_t index_of(Context&, T value) REQUIRES(!m_mutex);
-
- // Batch updates all items in this collection with the provided value
- // Must be called inside a transaction
- // Throws an exception if the value does not match the type for given prop_name
- template<typename ValueType, typename ContextType>
- void set_property_value(ContextType& ctx, StringData prop_name, ValueType value) REQUIRES(!m_mutex);
-
- // Execute the query immediately if needed. When the relevant query is slow, size()
- // may cost similar time compared with creating the tableview. Use this function to
- // avoid running the query twice for size() and other accessors.
- void evaluate_query_if_needed(bool wants_notifications = true) REQUIRES(!m_mutex);
-
- enum class UpdatePolicy {
- Auto, // Update automatically to reflect changes in the underlying data.
- AsyncOnly, // Only update via ResultsNotifier and never run queries synchronously
- Never, // Never update.
- };
- // For tests only. Use snapshot() for normal uses.
- void set_update_policy(UpdatePolicy policy) { m_update_policy = policy; }
-
-private:
- std::shared_ptr<Realm> m_realm;
- mutable util::CopyableAtomic<const ObjectSchema*> m_object_schema = nullptr;
- Query m_query GUARDED_BY(m_mutex);
- TableView m_table_view GUARDED_BY(m_mutex);
- ConstTableRef m_table;
- DescriptorOrdering m_descriptor_ordering;
- std::shared_ptr<LnkLst> m_link_list;
- std::shared_ptr<LstBase> m_list;
- util::Optional<std::vector<size_t>> m_list_indices GUARDED_BY(m_mutex);
-
- _impl::CollectionNotifier::Handle<_impl::ResultsNotifierBase> m_notifier;
-
- Mode m_mode GUARDED_BY(m_mutex) = Mode::Empty;
- UpdatePolicy m_update_policy = UpdatePolicy::Auto;
-
- bool update_linklist() REQUIRES(m_mutex);
-
- void validate_read() const;
- void validate_write() const;
-
- size_t do_size() REQUIRES(m_mutex);
- Query do_get_query() const REQUIRES(m_mutex);
- PropertyType do_get_type() const REQUIRES(m_mutex);
-
- using ForCallback = util::TaggedBool<class ForCallback>;
- void prepare_async(ForCallback);
-
- ColKey key(StringData) const;
-
- template<typename T>
- util::Optional<T> try_get(size_t) REQUIRES(m_mutex);
-
- template<typename AggregateFunction>
- util::Optional<Mixed> aggregate(ColKey column, const char* name,
- AggregateFunction&& func) REQUIRES(!m_mutex);
- DataType prepare_for_aggregate(ColKey column, const char* name) REQUIRES(m_mutex);
-
- template<typename Fn>
- auto dispatch(Fn&&) const REQUIRES(!m_mutex);
-
- template<typename T>
- auto& list_as() const;
-
- void evaluate_sort_and_distinct_on_list() REQUIRES(m_mutex);
- void do_evaluate_query_if_needed(bool wants_notifications = true) REQUIRES(m_mutex);
-
- class IteratorWrapper {
- public:
- IteratorWrapper() = default;
- IteratorWrapper(IteratorWrapper const&);
- IteratorWrapper& operator=(IteratorWrapper const&);
- IteratorWrapper(IteratorWrapper&&) = default;
- IteratorWrapper& operator=(IteratorWrapper&&) = default;
-
- Obj get(Table const& table, size_t ndx);
- private:
- std::unique_ptr<Table::ConstIterator> m_it;
- } m_table_iterator;
-
- util::CheckedOptionalMutex m_mutex;
-
- // A work around for what appears to be a false positive in clang's thread
- // analysis when constructing a different object of the same type within a
- // member function. Putting the ACQUIRE on the constructor seems like it
- // should work, but doesn't.
- void assert_unlocked() ACQUIRE(!m_mutex) {}
-};
-
-template<typename Fn>
-auto Results::dispatch(Fn&& fn) const
-{
- return switch_on_type(get_type(), std::forward<Fn>(fn));
-}
-
-template<typename Context>
-auto Results::get(Context& ctx, size_t row_ndx)
-{
- return dispatch([&](auto t) { return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx)); });
-}
-
-template<typename Context>
-auto Results::first(Context& ctx)
-{
- // GCC 4.9 complains about `ctx` not being defined within the lambda without this goofy capture
- return dispatch([this, ctx = &ctx](auto t) {
- auto value = this->first<std::decay_t<decltype(*t)>>();
- return value ? static_cast<decltype(ctx->no_value())>(ctx->box(std::move(*value))) : ctx->no_value();
- });
-}
-
-template<typename Context>
-auto Results::last(Context& ctx)
-{
- return dispatch([&](auto t) {
- auto value = this->last<std::decay_t<decltype(*t)>>();
- return value ? static_cast<decltype(ctx.no_value())>(ctx.box(std::move(*value))) : ctx.no_value();
- });
-}
-
-template<typename Context, typename T>
-size_t Results::index_of(Context& ctx, T value)
-{
- return dispatch([&](auto t) {
- return this->index_of(ctx.template unbox<std::decay_t<decltype(*t)>>(value, CreatePolicy::Skip));
- });
-}
-
-template <typename ValueType, typename ContextType>
-void Results::set_property_value(ContextType& ctx, StringData prop_name, ValueType value) NO_THREAD_SAFETY_ANALYSIS
-{
- // Check invariants for calling this method
- validate_write();
- const ObjectSchema& object_schema = get_object_schema();
- const Property* prop = object_schema.property_for_name(prop_name);
- if (!prop) {
- throw InvalidPropertyException(object_schema.name, prop_name);
- }
- if (prop->is_primary && !m_realm->is_in_migration()) {
- throw ModifyPrimaryKeyException(object_schema.name, prop->name);
- }
-
- // Update all objects in this ResultSets. Use snapshot to avoid correctness problems if the
- // object is removed from the TableView after the property update as well as avoiding to
- // re-evaluating the query too many times.
- auto snapshot = this->snapshot();
- size_t size = snapshot.size();
- for (size_t i = 0; i < size; ++i) {
- Object obj(m_realm, *m_object_schema, snapshot.get(i));
- obj.set_property_value_impl(ctx, *prop, value, CreatePolicy::ForceCreate, false);
- }
-}
-
-} // namespace realm
-
-#endif // REALM_RESULTS_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp
deleted file mode 100644
index d91375081..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "schema.hpp"
-
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-
-#include <algorithm>
-
-using namespace realm;
-
-namespace realm {
-bool operator==(Schema const& a, Schema const& b) noexcept
-{
- return static_cast<Schema::base const&>(a) == static_cast<Schema::base const&>(b);
-}
-}
-
-Schema::Schema() noexcept = default;
-Schema::~Schema() = default;
-Schema::Schema(Schema const&) = default;
-Schema::Schema(Schema &&) noexcept = default;
-Schema& Schema::operator=(Schema const&) = default;
-Schema& Schema::operator=(Schema&&) noexcept = default;
-
-Schema::Schema(std::initializer_list<ObjectSchema> types) : Schema(base(types)) { }
-
-Schema::Schema(base types) noexcept
-: base(std::move(types))
-{
- std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) {
- return lft.name < rgt.name;
- });
-}
-
-Schema::iterator Schema::find(StringData name) noexcept
-{
- auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) {
- return lft.name < rgt;
- });
- if (it != end() && it->name != name) {
- it = end();
- }
- return it;
-}
-
-Schema::const_iterator Schema::find(StringData name) const noexcept
-{
- return const_cast<Schema *>(this)->find(name);
-}
-
-Schema::iterator Schema::find(ObjectSchema const& object) noexcept
-{
- return find(object.name);
-}
-
-Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept
-{
- return const_cast<Schema *>(this)->find(object);
-}
-
-void Schema::validate() const
-{
- std::vector<ObjectSchemaValidationException> exceptions;
-
- // As the types are added sorted by name, we can detect duplicates by just looking at the following element.
- auto find_next_duplicate = [&](const_iterator start) {
- return std::adjacent_find(start, cend(), [](ObjectSchema const& lft, ObjectSchema const& rgt) {
- return lft.name == rgt.name;
- });
- };
-
- for (auto it = find_next_duplicate(cbegin()); it != cend(); it = find_next_duplicate(++it)) {
- exceptions.push_back(ObjectSchemaValidationException("Type '%1' appears more than once in the schema.",
- it->name));
- }
-
- for (auto const& object : *this) {
- object.validate(*this, exceptions);
- }
-
- if (exceptions.size()) {
- throw SchemaValidationException(exceptions);
- }
-}
-
-static void compare(ObjectSchema const& existing_schema,
- ObjectSchema const& target_schema,
- std::vector<SchemaChange>& changes)
-{
- for (auto& current_prop : existing_schema.persisted_properties) {
- auto target_prop = target_schema.property_for_name(current_prop.name);
-
- if (!target_prop) {
- changes.emplace_back(schema_change::RemoveProperty{&existing_schema, &current_prop});
- continue;
- }
- if (target_schema.property_is_computed(*target_prop)) {
- changes.emplace_back(schema_change::RemoveProperty{&existing_schema, &current_prop});
- continue;
- }
- if (current_prop.type != target_prop->type ||
- current_prop.object_type != target_prop->object_type ||
- is_array(current_prop.type) != is_array(target_prop->type)) {
-
- changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, &current_prop, target_prop});
- continue;
- }
- if (is_nullable(current_prop.type) != is_nullable(target_prop->type)) {
- if (is_nullable(current_prop.type))
- changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, &current_prop});
- else
- changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, &current_prop});
- }
- if (target_prop->requires_index()) {
- if (!current_prop.is_indexed)
- changes.emplace_back(schema_change::AddIndex{&existing_schema, &current_prop});
- }
- else if (current_prop.requires_index()) {
- changes.emplace_back(schema_change::RemoveIndex{&existing_schema, &current_prop});
- }
- }
-
- for (auto& target_prop : target_schema.persisted_properties) {
- if (!existing_schema.property_for_name(target_prop.name)) {
- changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop});
- }
- }
-
- if (existing_schema.primary_key != target_schema.primary_key) {
- changes.emplace_back(schema_change::ChangePrimaryKey{&existing_schema, target_schema.primary_key_property()});
- }
-}
-
-template<typename T, typename U, typename Func>
-void Schema::zip_matching(T&& a, U&& b, Func&& func) noexcept
-{
- size_t i = 0, j = 0;
- while (i < a.size() && j < b.size()) {
- auto& object_schema = a[i];
- auto& matching_schema = b[j];
- int cmp = object_schema.name.compare(matching_schema.name);
- if (cmp == 0) {
- func(&object_schema, &matching_schema);
- ++i;
- ++j;
- }
- else if (cmp < 0) {
- func(&object_schema, nullptr);
- ++i;
- }
- else {
- func(nullptr, &matching_schema);
- ++j;
- }
- }
- for (; i < a.size(); ++i)
- func(&a[i], nullptr);
- for (; j < b.size(); ++j)
- func(nullptr, &b[j]);
-
-}
-
-std::vector<SchemaChange> Schema::compare(Schema const& target_schema, bool include_table_removals) const
-{
- std::vector<SchemaChange> changes;
-
- // Add missing tables
- zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
- if (target && !existing) {
- changes.emplace_back(schema_change::AddTable{target});
- }
- else if (include_table_removals && existing && !target) {
- changes.emplace_back(schema_change::RemoveTable{existing});
- }
- });
-
- // Modify columns
- zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
- if (target && existing)
- ::compare(*existing, *target, changes);
- else if (target) {
- // Target is a new table -- add all properties
- changes.emplace_back(schema_change::AddInitialProperties{target});
- }
- // nothing for tables in existing but not target
- });
- return changes;
-}
-
-void Schema::copy_keys_from(realm::Schema const& other) noexcept
-{
- zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) {
- if (!existing || !other)
- return;
-
- existing->table_key = other->table_key;
- for (auto& current_prop : other->persisted_properties) {
- auto target_prop = existing->property_for_name(current_prop.name);
- if (target_prop) {
- target_prop->column_key = current_prop.column_key;
- }
- }
- });
-}
-
-namespace realm {
-bool operator==(SchemaChange const& lft, SchemaChange const& rgt) noexcept
-{
- if (lft.m_kind != rgt.m_kind)
- return false;
-
- using namespace schema_change;
- struct Visitor {
- SchemaChange const& value;
-
- #define REALM_SC_COMPARE(type, ...) \
- bool operator()(type rgt) const \
- { \
- auto cmp = [](auto&& v) { return std::tie(__VA_ARGS__); }; \
- return cmp(value.type) == cmp(rgt); \
- }
-
- REALM_SC_COMPARE(AddIndex, v.object, v.property)
- REALM_SC_COMPARE(AddProperty, v.object, v.property)
- REALM_SC_COMPARE(AddInitialProperties, v.object)
- REALM_SC_COMPARE(AddTable, v.object)
- REALM_SC_COMPARE(RemoveTable, v.object)
- REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property)
- REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property)
- REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property)
- REALM_SC_COMPARE(MakePropertyRequired, v.object, v.property)
- REALM_SC_COMPARE(RemoveIndex, v.object, v.property)
- REALM_SC_COMPARE(RemoveProperty, v.object, v.property)
-
- #undef REALM_SC_COMPARE
- } visitor{lft};
- return rgt.visit(visitor);
-}
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp
deleted file mode 100644
index 178a3fa83..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp
+++ /dev/null
@@ -1,183 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_SCHEMA_HPP
-#define REALM_SCHEMA_HPP
-
-#include <string>
-#include <vector>
-
-#include <realm/util/features.h>
-
-namespace realm {
-class ObjectSchema;
-class SchemaChange;
-class StringData;
-struct Property;
-
-class Schema : private std::vector<ObjectSchema> {
-private:
- using base = std::vector<ObjectSchema>;
-public:
- Schema() noexcept;
- ~Schema();
- // Create a schema from a vector of ObjectSchema
- Schema(base types) noexcept;
- Schema(std::initializer_list<ObjectSchema> types);
-
- Schema(Schema const&);
- Schema(Schema&&) noexcept;
- Schema& operator=(Schema const&);
- Schema& operator=(Schema&&) noexcept;
-
- // find an ObjectSchema by name
- iterator find(StringData name) noexcept;
- const_iterator find(StringData name) const noexcept;
-
- // find an ObjectSchema with the same name as the passed in one
- iterator find(ObjectSchema const& object) noexcept;
- const_iterator find(ObjectSchema const& object) const noexcept;
-
- // Verify that this schema is internally consistent (i.e. all properties are
- // valid, links link to types that actually exist, etc.)
- void validate() const;
-
- // Get the changes which must be applied to this schema to produce the passed-in schema
- std::vector<SchemaChange> compare(Schema const&, bool include_removals=false) const;
-
- void copy_keys_from(Schema const&) noexcept;
-
- friend bool operator==(Schema const&, Schema const&) noexcept;
- friend bool operator!=(Schema const& a, Schema const& b) noexcept { return !(a == b); }
-
- using base::iterator;
- using base::const_iterator;
- using base::begin;
- using base::end;
- using base::empty;
- using base::size;
-
-private:
- template<typename T, typename U, typename Func>
- static void zip_matching(T&& a, U&& b, Func&& func) noexcept;
-};
-
-namespace schema_change {
-struct AddTable {
- const ObjectSchema* object;
-};
-
-struct RemoveTable {
- const ObjectSchema* object;
-};
-
-struct AddInitialProperties {
- const ObjectSchema* object;
-};
-
-struct AddProperty {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct RemoveProperty {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct ChangePropertyType {
- const ObjectSchema* object;
- const Property* old_property;
- const Property* new_property;
-};
-
-struct MakePropertyNullable {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct MakePropertyRequired {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct AddIndex {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct RemoveIndex {
- const ObjectSchema* object;
- const Property* property;
-};
-
-struct ChangePrimaryKey {
- const ObjectSchema* object;
- const Property* property;
-};
-}
-
-#define REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(macro) \
- macro(AddTable) \
- macro(RemoveTable) \
- macro(AddInitialProperties) \
- macro(AddProperty) \
- macro(RemoveProperty) \
- macro(ChangePropertyType) \
- macro(MakePropertyNullable) \
- macro(MakePropertyRequired) \
- macro(AddIndex) \
- macro(RemoveIndex) \
- macro(ChangePrimaryKey) \
-
-class SchemaChange {
-public:
-#define REALM_SCHEMA_CHANGE_CONSTRUCTOR(name) \
- SchemaChange(schema_change::name value) : m_kind(Kind::name) { name = value; }
- REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SCHEMA_CHANGE_CONSTRUCTOR)
-#undef REALM_SCHEMA_CHANGE_CONSTRUCTOR
-
- template<typename Visitor>
- auto visit(Visitor&& visitor) const {
- switch (m_kind) {
-#define REALM_SWITCH_CASE(name) case Kind::name: return visitor(name);
- REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SWITCH_CASE)
-#undef REALM_SWITCH_CASE
- }
- REALM_COMPILER_HINT_UNREACHABLE();
- }
-
- friend bool operator==(SchemaChange const& lft, SchemaChange const& rgt) noexcept;
-private:
- enum class Kind {
-#define REALM_SCHEMA_CHANGE_TYPE(name) name,
- REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SCHEMA_CHANGE_TYPE)
-#undef REALM_SCHEMA_CHANGE_TYPE
-
- } m_kind;
- union {
-#define REALM_DEFINE_FIELD(name) schema_change::name name;
- REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_DEFINE_FIELD)
-#undef REALM_DEFINE_FIELD
- };
-};
-
-#undef REALM_FOR_EACH_SCHEMA_CHANGE_TYPE
-}
-
-#endif /* defined(REALM_SCHEMA_HPP) */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp
deleted file mode 100644
index 41ef88471..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp
+++ /dev/null
@@ -1,1029 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "shared_realm.hpp"
-
-#include "impl/collection_notifier.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "impl/transact_log_handler.hpp"
-
-#include "audit.hpp"
-#include "binding_context.hpp"
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "thread_safe_reference.hpp"
-
-#include "util/scheduler.hpp"
-
-#include <realm/db.hpp>
-#include <realm/util/scope_exit.hpp>
-#include <realm/util/fifo_helper.hpp>
-
-#if REALM_ENABLE_SYNC
-#include "sync/impl/sync_file.hpp"
-#include "sync/sync_config.hpp"
-#include "sync/sync_manager.hpp"
-
-#include <realm/sync/history.hpp>
-#include <realm/sync/permissions.hpp>
-#include <realm/sync/version.hpp>
-#else
-namespace realm {
-namespace sync {
- struct PermissionsCache {};
- struct TableInfoCache {};
-}
-}
-#endif
-
-using namespace realm;
-using namespace realm::_impl;
-
-Realm::Realm(Config config, util::Optional<VersionID> version, std::shared_ptr<_impl::RealmCoordinator> coordinator, MakeSharedTag)
-: m_config(std::move(config))
-, m_frozen_version(std::move(version))
-, m_scheduler(m_config.scheduler)
-{
- if (!coordinator->get_cached_schema(m_schema, m_schema_version, m_schema_transaction_version)) {
- m_group = coordinator->begin_read();
- read_schema_from_group_if_needed();
- coordinator->cache_schema(m_schema, m_schema_version, m_schema_transaction_version);
- m_group = nullptr;
- }
-
- m_coordinator = std::move(coordinator);
-}
-
-Realm::~Realm()
-{
- if (m_coordinator) {
- m_coordinator->unregister_realm(this);
- }
-}
-
-bool Realm::is_partial() const noexcept
-{
-#if REALM_ENABLE_SYNC
- return m_config.sync_config && m_config.sync_config->is_partial;
-#else
- return false;
-#endif
-}
-
-Group& Realm::read_group()
-{
- verify_open();
-
- if (!m_group)
- begin_read(m_frozen_version.value_or(VersionID{}));
- return *m_group;
-}
-
-Transaction& Realm::transaction()
-{
- REALM_ASSERT(!m_config.immutable());
- return static_cast<Transaction&>(read_group());
-}
-
-Transaction& Realm::transaction() const
-{
- REALM_ASSERT(!m_config.immutable());
- // FIXME: read_group() is not even remotly const
- return static_cast<Transaction&>(const_cast<Realm*>(this)->read_group());
-}
-
-std::shared_ptr<Transaction> Realm::transaction_ref()
-{
- return std::static_pointer_cast<Transaction>(m_group);
-}
-
-std::shared_ptr<Transaction> Realm::duplicate() const
-{
- return std::static_pointer_cast<Transaction>(m_coordinator->begin_read(read_transaction_version(), is_frozen()));
-}
-
-std::shared_ptr<DB>& Realm::Internal::get_db(Realm& realm) {
- return realm.m_coordinator->m_db;
-}
-
-void Realm::Internal::begin_read(Realm& realm, VersionID version_id)
-{
- realm.begin_read(version_id);
-}
-
-void Realm::begin_read(VersionID version_id)
-{
- REALM_ASSERT(!m_group);
- m_group = m_coordinator->begin_read(version_id, bool(m_frozen_version));
- add_schema_change_handler();
- read_schema_from_group_if_needed();
-}
-
-SharedRealm Realm::get_shared_realm(Config config)
-{
- auto coordinator = RealmCoordinator::get_coordinator(config.path);
- return coordinator->get_realm(std::move(config), util::none);
-}
-
-SharedRealm Realm::get_frozen_realm(Config config, VersionID version)
-{
- auto coordinator = RealmCoordinator::get_coordinator(config.path);
- SharedRealm realm = coordinator->get_realm(std::move(config), util::Optional<VersionID>(version));
- realm->set_auto_refresh(false);
- return realm;
-}
-
-SharedRealm Realm::get_shared_realm(ThreadSafeReference ref, std::shared_ptr<util::Scheduler> scheduler)
-{
- if (!scheduler)
- scheduler = util::Scheduler::make_default();
- SharedRealm realm = ref.resolve<std::shared_ptr<Realm>>(nullptr);
- REALM_ASSERT(realm);
- auto& config = realm->config();
- auto coordinator = RealmCoordinator::get_coordinator(config.path);
- realm->m_scheduler = scheduler;
- coordinator->bind_to_context(*realm);
- return realm;
-}
-
-#if REALM_ENABLE_SYNC
-std::shared_ptr<AsyncOpenTask> Realm::get_synchronized_realm(Config config)
-{
- auto coordinator = RealmCoordinator::get_coordinator(config.path);
- return coordinator->get_synchronized_realm(std::move(config));
-}
-#endif
-
-void Realm::set_schema(Schema const& reference, Schema schema)
-{
- m_dynamic_schema = false;
- schema.copy_keys_from(reference);
- m_schema = std::move(schema);
- notify_schema_changed();
-}
-
-void Realm::read_schema_from_group_if_needed()
-{
- if (m_config.immutable()) {
- REALM_ASSERT(m_group);
- if (m_schema.empty()) {
- m_schema_version = ObjectStore::get_schema_version(*m_group);
- m_schema = ObjectStore::schema_from_group(*m_group);
- }
- return;
- }
-
- Group& group = read_group();
- auto current_version = transaction().get_version_of_current_transaction().version;
- if (m_schema_transaction_version == current_version)
- return;
-
- m_schema_transaction_version = current_version;
- m_schema_version = ObjectStore::get_schema_version(group);
- auto schema = ObjectStore::schema_from_group(group);
- if (m_coordinator)
- m_coordinator->cache_schema(schema, m_schema_version,
- m_schema_transaction_version);
-
- if (m_dynamic_schema) {
- if (m_schema == schema) {
- // The structure of the schema hasn't changed. Bring the table column indices up to date.
- m_schema.copy_keys_from(schema);
- }
- else {
- // The structure of the schema has changed, so replace our copy of the schema.
- m_schema = std::move(schema);
- }
- }
- else {
- ObjectStore::verify_valid_external_changes(m_schema.compare(schema));
- m_schema.copy_keys_from(schema);
- }
- notify_schema_changed();
-}
-
-bool Realm::reset_file(Schema& schema, std::vector<SchemaChange>& required_changes)
-{
- // FIXME: this does not work if multiple processes try to open the file at
- // the same time, or even multiple threads if there is not any external
- // synchronization. The latter is probably fixable, but making it
- // multi-process-safe requires some sort of multi-process exclusive lock
- m_group = nullptr;
- m_coordinator->close();
- util::File::remove(m_config.path);
-
- m_schema = ObjectStore::schema_from_group(read_group());
- m_schema_version = ObjectStore::get_schema_version(read_group());
- required_changes = m_schema.compare(schema);
- m_coordinator->clear_schema_cache_and_set_schema_version(m_schema_version);
- return false;
-}
-
-bool Realm::schema_change_needs_write_transaction(Schema& schema,
- std::vector<SchemaChange>& changes,
- uint64_t version)
-{
- if (version == m_schema_version && changes.empty())
- return false;
-
- switch (m_config.schema_mode) {
- case SchemaMode::Automatic:
- if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
- throw InvalidSchemaVersionException(m_schema_version, version);
- return true;
-
- case SchemaMode::Immutable:
- if (version != m_schema_version)
- throw InvalidSchemaVersionException(m_schema_version, version);
- REALM_FALLTHROUGH;
- case SchemaMode::ReadOnlyAlternative:
- ObjectStore::verify_compatible_for_immutable_and_readonly(changes);
- return false;
-
- case SchemaMode::ResetFile:
- if (m_schema_version == ObjectStore::NotVersioned)
- return true;
- if (m_schema_version == version && !ObjectStore::needs_migration(changes))
- return true;
- reset_file(schema, changes);
- return true;
-
- case SchemaMode::Additive: {
- bool will_apply_index_changes = version > m_schema_version;
- if (ObjectStore::verify_valid_additive_changes(changes, will_apply_index_changes))
- return true;
- return version != m_schema_version;
- }
-
- case SchemaMode::Manual:
- if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
- throw InvalidSchemaVersionException(m_schema_version, version);
- if (version == m_schema_version) {
- ObjectStore::verify_no_changes_required(changes);
- REALM_UNREACHABLE(); // changes is non-empty so above line always throws
- }
- return true;
- }
- REALM_COMPILER_HINT_UNREACHABLE();
-}
-
-Schema Realm::get_full_schema()
-{
- if (!m_config.immutable())
- do_refresh();
-
- // If the user hasn't specified a schema previously then m_schema is always
- // the full schema
- if (m_dynamic_schema)
- return m_schema;
-
- // Otherwise we may have a subset of the file's schema, so we need to get
- // the complete thing to calculate what changes to make
- if (m_config.immutable())
- return ObjectStore::schema_from_group(read_group());
-
- Schema actual_schema;
- uint64_t actual_version;
- uint64_t version = -1;
- bool got_cached = m_coordinator->get_cached_schema(actual_schema, actual_version, version);
- if (!got_cached || version != transaction().get_version_of_current_transaction().version)
- return ObjectStore::schema_from_group(read_group());
- return actual_schema;
-}
-
-void Realm::set_schema_subset(Schema schema)
-{
- REALM_ASSERT(m_dynamic_schema);
- REALM_ASSERT(m_schema_version != ObjectStore::NotVersioned);
-
- std::vector<SchemaChange> changes = m_schema.compare(schema);
- switch (m_config.schema_mode) {
- case SchemaMode::Automatic:
- case SchemaMode::ResetFile:
- ObjectStore::verify_no_migration_required(changes);
- break;
-
- case SchemaMode::Immutable:
- case SchemaMode::ReadOnlyAlternative:
- ObjectStore::verify_compatible_for_immutable_and_readonly(changes);
- break;
-
- case SchemaMode::Additive:
- ObjectStore::verify_valid_additive_changes(changes);
- break;
-
- case SchemaMode::Manual:
- ObjectStore::verify_no_changes_required(changes);
- break;
- }
-
- set_schema(m_schema, std::move(schema));
-}
-
-void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction migration_function,
- DataInitializationFunction initialization_function, bool in_transaction)
-{
- schema.validate();
-
- bool was_in_read_transaction = is_in_read_transaction();
- Schema actual_schema = get_full_schema();
- std::vector<SchemaChange> required_changes = actual_schema.compare(schema);
-
- if (!schema_change_needs_write_transaction(schema, required_changes, version)) {
- if (!was_in_read_transaction)
- m_group = nullptr;
- set_schema(actual_schema, std::move(schema));
- return;
- }
- // Either the schema version has changed or we need to do non-migration changes
-
- // Cancel the write transaction if we exit this function before committing it
- auto cleanup = util::make_scope_exit([&]() noexcept {
- // When in_transaction is true, caller is responsible to cancel the transaction.
- if (!in_transaction && is_in_transaction())
- cancel_transaction();
- if (!was_in_read_transaction)
- m_group = nullptr;
- });
-
- if (!in_transaction) {
- transaction().promote_to_write();
-
- // Beginning the write transaction may have advanced the version and left
- // us with nothing to do if someone else initialized the schema on disk
- if (m_new_schema) {
- actual_schema = *m_new_schema;
- required_changes = actual_schema.compare(schema);
- if (!schema_change_needs_write_transaction(schema, required_changes, version)) {
- cancel_transaction();
- cache_new_schema();
- set_schema(actual_schema, std::move(schema));
- return;
- }
- }
- cache_new_schema();
- }
-
- uint64_t old_schema_version = m_schema_version;
- bool additive = m_config.schema_mode == SchemaMode::Additive;
- if (migration_function && !additive) {
- auto wrapper = [&] {
- auto config = m_config;
- config.schema_mode = SchemaMode::ReadOnlyAlternative;
- config.schema = util::none;
- // Don't go through the normal codepath for opening a Realm because
- // we're using a mismatched config
- auto old_realm = std::make_shared<Realm>(std::move(config), none, m_coordinator, MakeSharedTag{});
- migration_function(old_realm, shared_from_this(), m_schema);
- };
-
- // migration function needs to see the target schema on the "new" Realm
- std::swap(m_schema, schema);
- std::swap(m_schema_version, version);
- m_in_migration = true;
- auto restore = util::make_scope_exit([&]() noexcept {
- std::swap(m_schema, schema);
- std::swap(m_schema_version, version);
- m_in_migration = false;
- });
-
- ObjectStore::apply_schema_changes(transaction(), version, m_schema, m_schema_version,
- m_config.schema_mode, required_changes, util::none, wrapper);
- }
- else {
- util::Optional<std::string> sync_user_id;
-#if REALM_ENABLE_SYNC
- if (m_config.sync_config && m_config.sync_config->is_partial)
- sync_user_id = m_config.sync_config->user->identity();
-#endif
- ObjectStore::apply_schema_changes(transaction(), m_schema_version, schema, version,
- m_config.schema_mode, required_changes, std::move(sync_user_id));
- REALM_ASSERT_DEBUG(additive || (required_changes = ObjectStore::schema_from_group(read_group()).compare(schema)).empty());
- }
-
- if (initialization_function && old_schema_version == ObjectStore::NotVersioned) {
- // Initialization function needs to see the latest schema
- uint64_t temp_version = ObjectStore::get_schema_version(read_group());
- std::swap(m_schema, schema);
- std::swap(m_schema_version, temp_version);
- auto restore = util::make_scope_exit([&]() noexcept {
- std::swap(m_schema, schema);
- std::swap(m_schema_version, temp_version);
- });
- initialization_function(shared_from_this());
- }
-
- m_schema = std::move(schema);
- m_new_schema = ObjectStore::schema_from_group(read_group());
- m_schema_version = ObjectStore::get_schema_version(read_group());
- m_dynamic_schema = false;
- m_coordinator->clear_schema_cache_and_set_schema_version(version);
-
- if (!in_transaction) {
- m_coordinator->commit_write(*this);
- invalidate_permission_cache();
- cache_new_schema();
- }
-
- notify_schema_changed();
-}
-
-void Realm::add_schema_change_handler()
-{
- if (m_config.immutable())
- return;
- m_group->set_schema_change_notification_handler([&] {
- m_new_schema = ObjectStore::schema_from_group(read_group());
- m_schema_version = ObjectStore::get_schema_version(read_group());
- if (m_dynamic_schema) {
- m_schema = *m_new_schema;
- }
- else
- m_schema.copy_keys_from(*m_new_schema);
-
- notify_schema_changed();
- });
-}
-
-void Realm::cache_new_schema()
-{
- if (!is_closed()) {
- auto new_version = transaction().get_version_of_current_transaction().version;
- if (m_new_schema)
- m_coordinator->cache_schema(std::move(*m_new_schema),
- m_schema_version, new_version);
- else
- m_coordinator->advance_schema_cache(m_schema_transaction_version, new_version);
- m_schema_transaction_version = new_version;
- m_new_schema = util::none;
- }
-}
-
-void Realm::translate_schema_error()
-{
- // Read the new (incompatible) schema without changing our read transaction
- auto new_schema = ObjectStore::schema_from_group(*m_coordinator->begin_read());
-
- // Should always throw
- ObjectStore::verify_valid_external_changes(m_schema.compare(new_schema, true));
-
- // Something strange happened so just rethrow the old exception
- throw;
-}
-
-void Realm::notify_schema_changed()
-{
- if (m_binding_context) {
- m_binding_context->schema_did_change(m_schema);
- }
-}
-
-static void check_can_create_any_transaction(const Realm* realm)
-{
- if (realm->config().immutable()) {
- throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
- }
-}
-
-static void check_can_create_write_transaction(const Realm* realm)
-{
- if (realm->config().immutable() || realm->config().read_only_alternative()) {
- throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
- }
- if (realm->is_frozen()) {
- throw InvalidTransactionException("Can't perform transactions on a frozen Realm");
- }
- if (!realm->is_closed() && realm->get_number_of_versions() > realm->config().max_number_of_active_versions) {
- throw InvalidTransactionException(util::format("Number of active versions (%1) in the Realm exceeded the limit of %2",
- realm->get_number_of_versions(),
- realm->config().max_number_of_active_versions));
- }
-}
-
-void Realm::verify_thread() const
-{
- if (m_scheduler && !m_scheduler->is_on_thread())
- throw IncorrectThreadException();
-}
-
-void Realm::verify_in_write() const
-{
- if (!is_in_transaction()) {
- throw InvalidTransactionException("Cannot modify managed objects outside of a write transaction.");
- }
-}
-
-void Realm::verify_open() const
-{
- if (is_closed()) {
- throw ClosedRealmException();
- }
-}
-
-bool Realm::verify_notifications_available(bool throw_on_error) const
-{
- if (is_frozen()) {
- if (throw_on_error)
- throw InvalidTransactionException("Notifications are not available on frozen lists since they do not change.");
- return false;
- }
- if (config().immutable()) {
- if (throw_on_error)
- throw InvalidTransactionException("Cannot create asynchronous query for immutable Realms");
- return false;
- }
- if (is_in_transaction()) {
- if (throw_on_error)
- throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction");
- return false;
- }
-
- return true;
-}
-
-VersionID Realm::read_transaction_version() const
-{
- verify_thread();
- verify_open();
- check_can_create_any_transaction(this);
- return static_cast<Transaction&>(*m_group).get_version_of_current_transaction();
-}
-
-uint_fast64_t Realm::get_number_of_versions() const
-{
- verify_open();
- check_can_create_any_transaction(this);
- return m_coordinator->get_number_of_versions();
-}
-
-bool Realm::is_in_transaction() const noexcept
-{
- return !m_config.immutable()
- && !is_closed()
- && m_group && transaction().get_transact_stage() == DB::transact_Writing;
-}
-
-util::Optional<VersionID> Realm::current_transaction_version() const
-{
- util::Optional<VersionID> ret;
- if (m_group) {
- ret = static_cast<Transaction&>(*m_group).get_version_of_current_transaction();
- }
- else if (m_frozen_version) {
- ret = m_frozen_version;
- }
- return ret;
-}
-
-void Realm::enable_wait_for_change()
-{
- m_coordinator->enable_wait_for_change();
-}
-
-bool Realm::wait_for_change()
-{
- if (m_frozen_version) {
- return false;
- }
- return m_group ? m_coordinator->wait_for_change(transaction_ref()) : false;
-}
-
-void Realm::wait_for_change_release()
-{
- m_coordinator->wait_for_change_release();
-}
-
-void Realm::begin_transaction()
-{
- verify_thread();
- check_can_create_write_transaction(this);
-
- if (is_in_transaction()) {
- throw InvalidTransactionException("The Realm is already in a write transaction");
- }
-
- // Any of the callbacks to user code below could drop the last remaining
- // strong reference to `this`
- auto retain_self = shared_from_this();
-
- // If we're already in the middle of sending notifications, just begin the
- // write transaction without sending more notifications. If this actually
- // advances the read version this could leave the user in an inconsistent
- // state, but that's unavoidable.
- if (m_is_sending_notifications) {
- _impl::NotifierPackage notifiers;
- transaction::begin(transaction_ref(), m_binding_context.get(), notifiers);
- return;
- }
-
- // make sure we have a read transaction
- read_group();
-
- m_is_sending_notifications = true;
- auto cleanup = util::make_scope_exit([this]() noexcept {
- m_is_sending_notifications = false;
- });
-
- try {
- m_coordinator->promote_to_write(*this);
- }
- catch (_impl::UnsupportedSchemaChange const&) {
- translate_schema_error();
- }
- cache_new_schema();
-}
-
-void Realm::commit_transaction()
-{
- check_can_create_write_transaction(this);
- verify_thread();
-
- if (!is_in_transaction()) {
- throw InvalidTransactionException("Can't commit a non-existing write transaction");
- }
-
- if (auto audit = audit_context()) {
- auto prev_version = transaction().get_version_of_current_transaction();
- m_coordinator->commit_write(*this);
- audit->record_write(prev_version, transaction().get_version_of_current_transaction());
- // m_shared_group->unpin_version(prev_version);
- }
- else {
- m_coordinator->commit_write(*this);
- }
- cache_new_schema();
- invalidate_permission_cache();
-}
-
-void Realm::cancel_transaction()
-{
- check_can_create_write_transaction(this);
- verify_thread();
-
- if (!is_in_transaction()) {
- throw InvalidTransactionException("Can't cancel a non-existing write transaction");
- }
-
- transaction::cancel(transaction(), m_binding_context.get());
- invalidate_permission_cache();
-}
-
-void Realm::invalidate()
-{
- verify_open();
- verify_thread();
- check_can_create_any_transaction(this);
-
- if (m_is_sending_notifications) {
- return;
- }
-
- if (is_in_transaction()) {
- cancel_transaction();
- }
-
- m_permissions_cache = nullptr;
- m_table_info_cache = nullptr;
- m_group = nullptr;
-}
-
-bool Realm::compact()
-{
- verify_thread();
- verify_open();
-
- if (m_config.immutable() || m_config.read_only_alternative()) {
- throw InvalidTransactionException("Can't compact a read-only Realm");
- }
- if (is_in_transaction()) {
- throw InvalidTransactionException("Can't compact a Realm within a write transaction");
- }
-
- verify_open();
- m_group = nullptr;
- return m_coordinator->compact();
-}
-
-void Realm::write_copy(StringData path, BinaryData key)
-{
- if (key.data() && key.size() != 64) {
- throw InvalidEncryptionKeyException();
- }
- verify_thread();
- try {
- read_group().write(path, key.data());
- }
- catch (...) {
- _impl::translate_file_exception(path);
- }
-}
-
-OwnedBinaryData Realm::write_copy()
-{
- verify_thread();
- BinaryData buffer = read_group().write_to_mem();
-
- // Since OwnedBinaryData does not have a constructor directly taking
- // ownership of BinaryData, we have to do this to avoid copying the buffer
- return OwnedBinaryData(std::unique_ptr<char[]>((char*)buffer.data()), buffer.size());
-}
-
-void Realm::notify()
-{
- if (is_closed() || is_in_transaction() || is_frozen()) {
- return;
- }
-
- verify_thread();
- invalidate_permission_cache();
-
- // Any of the callbacks to user code below could drop the last remaining
- // strong reference to `this`
- auto retain_self = shared_from_this();
-
- if (m_binding_context) {
- m_binding_context->before_notify();
- if (is_closed() || is_in_transaction()) {
- return;
- }
- }
-
- auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; });
- if (!m_coordinator->can_advance(*this)) {
- m_is_sending_notifications = true;
- m_coordinator->process_available_async(*this);
- return;
- }
-
- if (m_binding_context) {
- m_binding_context->changes_available();
-
- // changes_available() may have advanced the read version, and if
- // so we don't need to do anything further
- if (!m_coordinator->can_advance(*this))
- return;
- }
-
- m_is_sending_notifications = true;
- if (m_auto_refresh) {
- if (m_group) {
- try {
- m_coordinator->advance_to_ready(*this);
- }
- catch (_impl::UnsupportedSchemaChange const&) {
- translate_schema_error();
- }
- if (!is_closed())
- cache_new_schema();
- }
- else {
- if (m_binding_context) {
- m_binding_context->did_change({}, {});
- }
- if (!is_closed()) {
- m_coordinator->process_available_async(*this);
- }
- }
- }
-}
-
-bool Realm::refresh()
-{
- verify_thread();
- check_can_create_any_transaction(this);
- return do_refresh();
-}
-
-bool Realm::do_refresh()
-{
- // Frozen Realms never change.
- if (is_frozen()) {
- return false;
- }
-
- // can't be any new changes if we're in a write transaction
- if (is_in_transaction()) {
- return false;
- }
- // don't advance if we're already in the process of advancing as that just
- // makes things needlessly complicated
- if (m_is_sending_notifications) {
- return false;
- }
- invalidate_permission_cache();
-
- // Any of the callbacks to user code below could drop the last remaining
- // strong reference to `this`
- auto retain_self = shared_from_this();
-
- m_is_sending_notifications = true;
- auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; });
-
- if (m_binding_context) {
- m_binding_context->before_notify();
- }
- if (m_group) {
- try {
- bool version_changed = m_coordinator->advance_to_latest(*this);
- if (is_closed())
- return false;
- cache_new_schema();
- return version_changed;
- }
- catch (_impl::UnsupportedSchemaChange const&) {
- translate_schema_error();
- }
- }
-
- // No current read transaction, so just create a new one
- read_group();
- m_coordinator->process_available_async(*this);
- return true;
-}
-
-void Realm::set_auto_refresh(bool auto_refresh)
-{
- if (is_frozen() && auto_refresh) {
- throw std::logic_error("Auto-refresh cannot be enabled for frozen Realms.");
- }
- m_auto_refresh = auto_refresh;
-}
-
-
-bool Realm::can_deliver_notifications() const noexcept
-{
- if (m_config.immutable() || !m_config.automatic_change_notifications) {
- return false;
- }
-
- if (!m_scheduler || !m_scheduler->can_deliver_notifications()) {
- return false;
- }
-
- return true;
-}
-
-uint64_t Realm::get_schema_version(const Realm::Config &config)
-{
- auto coordinator = RealmCoordinator::get_coordinator(config.path);
- auto version = coordinator->get_schema_version();
- if (version == ObjectStore::NotVersioned)
- version = ObjectStore::get_schema_version(coordinator->get_realm(config, util::none)->read_group());
- return version;
-}
-
-
-bool Realm::is_frozen() const
-{
- bool result = bool(m_frozen_version);
- REALM_ASSERT_DEBUG((result && m_group) ? m_group->is_frozen() : true);
- return result;
-}
-
-SharedRealm Realm::freeze()
-{
- auto config = m_config;
- config.scheduler = util::Scheduler::get_frozen();
- return Realm::get_frozen_realm(std::move(config), read_transaction_version());
-}
-
-void Realm::close()
-{
- if (m_coordinator) {
- m_coordinator->unregister_realm(this);
- }
- if (!m_config.immutable() && m_group) {
- transaction().close();
- }
-
- m_permissions_cache = nullptr;
- m_table_info_cache = nullptr;
- m_group = nullptr;
- m_binding_context = nullptr;
- m_coordinator = nullptr;
-}
-
-AuditInterface* Realm::audit_context() const noexcept
-{
- return m_coordinator ? m_coordinator->audit_context() : nullptr;
-}
-
-#if REALM_ENABLE_SYNC
-static_assert(static_cast<int>(ComputedPrivileges::Read) == static_cast<int>(sync::Privilege::Read), "");
-static_assert(static_cast<int>(ComputedPrivileges::Update) == static_cast<int>(sync::Privilege::Update), "");
-static_assert(static_cast<int>(ComputedPrivileges::Delete) == static_cast<int>(sync::Privilege::Delete), "");
-static_assert(static_cast<int>(ComputedPrivileges::SetPermissions) == static_cast<int>(sync::Privilege::SetPermissions), "");
-static_assert(static_cast<int>(ComputedPrivileges::Query) == static_cast<int>(sync::Privilege::Query), "");
-static_assert(static_cast<int>(ComputedPrivileges::Create) == static_cast<int>(sync::Privilege::Create), "");
-static_assert(static_cast<int>(ComputedPrivileges::ModifySchema) == static_cast<int>(sync::Privilege::ModifySchema), "");
-
-static constexpr const uint8_t s_allRealmPrivileges = sync::Privilege::Read
- | sync::Privilege::Update
- | sync::Privilege::SetPermissions
- | sync::Privilege::ModifySchema;
-static constexpr const uint8_t s_allClassPrivileges = sync::Privilege::Read
- | sync::Privilege::Update
- | sync::Privilege::Create
- | sync::Privilege::Query
- | sync::Privilege::SetPermissions;
-static constexpr const uint8_t s_allObjectPrivileges = sync::Privilege::Read
- | sync::Privilege::Update
- | sync::Privilege::Delete
- | sync::Privilege::SetPermissions;
-
-bool Realm::init_permission_cache()
-{
- verify_thread();
-
- if (m_permissions_cache) {
- // Rather than trying to track changes to permissions tables, just skip the caching
- // entirely within write transactions for now
- if (is_in_transaction())
- m_permissions_cache->clear();
- return true;
- }
-
- // Admin users bypass permissions checks outside of the logic in PermissionsCache
- if (m_config.sync_config && m_config.sync_config->is_partial && !m_config.sync_config->user->is_admin()) {
- m_table_info_cache = std::make_unique<sync::TableInfoCache>(transaction());
- m_permissions_cache = std::make_unique<sync::PermissionsCache>(transaction(), *m_table_info_cache,
- m_config.sync_config->user->identity());
- return true;
- }
- return false;
-}
-
-void Realm::invalidate_permission_cache()
-{
- if (m_permissions_cache)
- m_permissions_cache->clear();
-}
-
-ComputedPrivileges Realm::get_privileges()
-{
- if (!init_permission_cache())
- return static_cast<ComputedPrivileges>(s_allRealmPrivileges);
- return static_cast<ComputedPrivileges>(m_permissions_cache->get_realm_privileges() & s_allRealmPrivileges);
-}
-
-static uint8_t inherited_mask(uint32_t privileges)
-{
- uint8_t mask = ~0;
- if (!(privileges & sync::Privilege::Read))
- mask = 0;
- else if (!(privileges & sync::Privilege::Update))
- mask = static_cast<uint8_t>(sync::Privilege::Read | sync::Privilege::Query);
- return mask;
-}
-
-ComputedPrivileges Realm::get_privileges(StringData object_type)
-{
- if (!init_permission_cache())
- return static_cast<ComputedPrivileges>(s_allClassPrivileges);
- auto privileges = inherited_mask(m_permissions_cache->get_realm_privileges())
- & m_permissions_cache->get_class_privileges(object_type);
- return static_cast<ComputedPrivileges>(privileges & s_allClassPrivileges);
-}
-
-ComputedPrivileges Realm::get_privileges(ConstObj const& obj)
-{
- if (!init_permission_cache())
- return static_cast<ComputedPrivileges>(s_allObjectPrivileges);
-
- auto table = obj.get_table();
- auto object_type = ObjectStore::object_type_for_table_name(table->get_name());
- sync::GlobalID global_id{object_type, table->get_object_id(obj.get_key())};
- auto privileges = inherited_mask(m_permissions_cache->get_realm_privileges())
- & inherited_mask(m_permissions_cache->get_class_privileges(object_type))
- & m_permissions_cache->get_object_privileges(global_id);
- return static_cast<ComputedPrivileges>(privileges & s_allObjectPrivileges);
-}
-#else
-void Realm::invalidate_permission_cache() { }
-#endif
-
-MismatchedConfigException::MismatchedConfigException(StringData message, StringData path)
-: std::logic_error(util::format(message.data(), path)) { }
-
-MismatchedRealmException::MismatchedRealmException(StringData message)
-: std::logic_error(message.data()) { }
-
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp
deleted file mode 100644
index b15f6efb1..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp
+++ /dev/null
@@ -1,540 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_REALM_HPP
-#define REALM_REALM_HPP
-
-#include "schema.hpp"
-
-#include <realm/util/optional.hpp>
-#include <realm/binary_data.hpp>
-#include <realm/db.hpp>
-#include <realm/version_id.hpp>
-
-#if REALM_ENABLE_SYNC
-#include <realm/sync/client.hpp>
-#endif
-
-#include <memory>
-
-namespace realm {
-class AsyncOpenTask;
-class AuditInterface;
-class BindingContext;
-class DB;
-class Group;
-class Obj;
-class Realm;
-class Replication;
-class StringData;
-class Table;
-class ThreadSafeReference;
-class Transaction;
-struct SyncConfig;
-typedef std::shared_ptr<Realm> SharedRealm;
-typedef std::weak_ptr<Realm> WeakRealm;
-
-namespace util {
-class Scheduler;
-}
-
-namespace _impl {
- class AnyHandover;
- class CollectionNotifier;
- class PartialSyncHelper;
- class RealmCoordinator;
- class RealmFriend;
-}
-namespace sync {
- struct PermissionsCache;
- struct TableInfoCache;
-}
-
-// How to handle update_schema() being called on a file which has
-// already been initialized with a different schema
-enum class SchemaMode : uint8_t {
- // If the schema version has increased, automatically apply all
- // changes, then call the migration function.
- //
- // If the schema version has not changed, verify that the only
- // changes are to add new tables and add or remove indexes, and then
- // apply them if so. Does not call the migration function.
- //
- // This mode does not automatically remove tables which are not
- // present in the schema that must be manually done in the migration
- // function, to support sharing a Realm file between processes using
- // different class subsets.
- //
- // This mode allows using schemata with different subsets of tables
- // on different threads, but the tables which are shared must be
- // identical.
- Automatic,
-
- // Open the file in immutable mode. Schema version must match the
- // version in the file, and all tables present in the file must
- // exactly match the specified schema, except for indexes. Tables
- // are allowed to be missing from the file.
- // WARNING: This is the original ReadOnly mode.
- Immutable,
-
- // Open the Realm in read-only mode, transactions are not allowed to
- // be performed on the Realm instance. The schema of the existing Realm
- // file won't be changed through this Realm instance. Extra tables and
- // extra properties are allowed in the existing Realm schema. The
- // difference of indexes is allowed as well. Other schema differences
- // than those will cause an exception. This is different from Immutable
- // mode, sync Realm can be opened with ReadOnly mode. Changes
- // can be made to the Realm file through another writable Realm instance.
- // Thus, notifications are also allowed in this mode.
- // FIXME: Rename this to ReadOnly
- // WARNING: This is not the original ReadOnly mode. The original ReadOnly
- // has been renamed to Immutable.
- ReadOnlyAlternative,
-
- // If the schema version matches and the only schema changes are new
- // tables and indexes being added or removed, apply the changes to
- // the existing file.
- // Otherwise delete the file and recreate it from scratch.
- // The migration function is not used.
- //
- // This mode allows using schemata with different subsets of tables
- // on different threads, but the tables which are shared must be
- // identical.
- ResetFile,
-
- // The only changes allowed are to add new tables, add columns to
- // existing tables, and to add or remove indexes from existing
- // columns. Extra tables not present in the schema are ignored.
- // Indexes are only added to or removed from existing columns if the
- // schema version is greater than the existing one (and unlike other
- // modes, the schema version is allowed to be less than the existing
- // one).
- // The migration function is not used.
- //
- // This mode allows updating the schema with additive changes even
- // if the Realm is already open on another thread.
- Additive,
-
- // Verify that the schema version has increased, call the migraiton
- // function, and then verify that the schema now matches.
- // The migration function is mandatory for this mode.
- //
- // This mode requires that all threads and processes which open a
- // file use identical schemata.
- Manual
-};
-
-enum class ComputedPrivileges : uint8_t {
- None = 0,
-
- Read = (1 << 0),
- Update = (1 << 1),
- Delete = (1 << 2),
- SetPermissions = (1 << 3),
- Query = (1 << 4),
- Create = (1 << 5),
- ModifySchema = (1 << 6),
-
- AllRealm = Read | Update | SetPermissions | ModifySchema,
- AllClass = Read | Update | Create | Query | SetPermissions,
- AllObject = Read | Update | Delete | SetPermissions,
- All = (1 << 7) - 1
-};
-
-class Realm : public std::enable_shared_from_this<Realm> {
-public:
- // A callback function to be called during a migration for Automatic and
- // Manual schema modes. It is passed a SharedRealm at the version before
- // the migration, the SharedRealm in the migration, and a mutable reference
- // to the realm's Schema. Updating the schema with changes made within the
- // migration function is only required if you wish to use the ObjectStore
- // functions which take a Schema from within the migration function.
- using MigrationFunction = std::function<void (SharedRealm old_realm, SharedRealm realm, Schema&)>;
-
- // A callback function to be called the first time when a schema is created.
- // It is passed a SharedRealm which is in a write transaction with the schema
- // initialized. So it is possible to create some initial objects inside the callback
- // with the given SharedRealm. Those changes will be committed together with the
- // schema creation in a single transaction.
- using DataInitializationFunction = std::function<void (SharedRealm realm)>;
-
- // A callback function called when opening a SharedRealm when no cached
- // version of this Realm exists. It is passed the total bytes allocated for
- // the file (file size) and the total bytes used by data in the file.
- // Return `true` to indicate that an attempt to compact the file should be made
- // if it is possible to do so.
- // Won't compact the file if another process is accessing it.
- //
- // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
- // because it's not crash safe! It may corrupt your database if something fails
- using ShouldCompactOnLaunchFunction = std::function<bool (uint64_t total_bytes, uint64_t used_bytes)>;
-
- struct Config {
- // Path and binary data are mutually exclusive
- std::string path;
- BinaryData realm_data;
- // User-supplied encryption key. Must be either empty or 64 bytes.
- std::vector<char> encryption_key;
-
- // Core and Object Store will in some cases need to create named pipes alongside the Realm file.
- // But on some filesystems this can be a problem (e.g. external storage on Android that uses FAT32).
- // In order to work around this, a separate path can be specified for these files.
- std::string fifo_files_fallback_path;
-
- bool in_memory = false;
- SchemaMode schema_mode = SchemaMode::Automatic;
-
- // Optional schema for the file.
- // If the schema and schema version are supplied, update_schema() is
- // called with the supplied schema, version and migration function when
- // the Realm is actually opened and not just retrieved from the cache
- util::Optional<Schema> schema;
- uint64_t schema_version = -1;
- MigrationFunction migration_function;
-
- DataInitializationFunction initialization_function;
-
- // A callback function called when opening a SharedRealm when no cached
- // version of this Realm exists. It is passed the total bytes allocated for
- // the file (file size) and the total bytes used by data in the file.
- // Return `true` to indicate that an attempt to compact the file should be made
- // if it is possible to do so.
- // Won't compact the file if another process is accessing it.
- //
- // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
- // because it's not crash safe! It may corrupt your database if something fails
- ShouldCompactOnLaunchFunction should_compact_on_launch_function;
-
- // WARNING: The original read_only() has been renamed to immutable().
- bool immutable() const { return schema_mode == SchemaMode::Immutable; }
- // FIXME: Rename this to read_only().
- bool read_only_alternative() const { return schema_mode == SchemaMode::ReadOnlyAlternative; }
-
- // The following are intended for internal/testing purposes and
- // should not be publicly exposed in binding APIs
-
- // Throw an exception rather than automatically upgrading the file
- // format. Used by the browser to warn the user that it'll modify
- // the file.
- bool disable_format_upgrade = false;
- // Disable the background worker thread for producing change
- // notifications. Useful for tests for those notifications so that
- // everything can be done deterministically on one thread, and
- // speeds up tests that don't need notifications.
- bool automatic_change_notifications = true;
-
- // The Scheduler which this Realm should be bound to. If not supplied,
- // a default one for the current thread will be used.
- std::shared_ptr<util::Scheduler> scheduler;
-
- /// A data structure storing data used to configure the Realm for sync support.
- std::shared_ptr<SyncConfig> sync_config;
-
- // Open the Realm using the sync history mode even if a sync
- // configuration is not supplied.
- bool force_sync_history = false;
-
- // A factory function which produces an audit implementation.
- std::function<std::shared_ptr<AuditInterface>()> audit_factory;
-
- // Maximum number of active versions in the Realm file allowed before an exception
- // is thrown.
- uint_fast64_t max_number_of_active_versions = std::numeric_limits<uint_fast64_t>::max();
- };
-
- // Returns a thread-confined live Realm for the given configuration
- static SharedRealm get_shared_realm(Config config);
-
- // Get a Realm for the given scheduler (or current thread if `none`)
- // from the thread safe reference.
- static SharedRealm get_shared_realm(ThreadSafeReference, std::shared_ptr<util::Scheduler> = nullptr);
-
-#if REALM_ENABLE_SYNC
- // Open a synchronized Realm and make sure it is fully up to date before
- // returning it.
- //
- // It is possible to both cancel the download and listen to download progress
- // using the `AsyncOpenTask` returned. Note that the download doesn't actually
- // start until you call `AsyncOpenTask::start(callback)`
- static std::shared_ptr<AsyncOpenTask> get_synchronized_realm(Config config);
-#endif
- // Returns a frozen Realm for the given Realm. This Realm can be accessed from any thread.
- static SharedRealm get_frozen_realm(Config config, VersionID version);
-
- // Updates a Realm to a given schema, using the Realm's pre-set schema mode.
- void update_schema(Schema schema, uint64_t version=0,
- MigrationFunction migration_function=nullptr,
- DataInitializationFunction initialization_function=nullptr,
- bool in_transaction=false);
-
- // Set the schema used for this Realm, but do not update the file's schema
- // if it is not compatible (and instead throw an error).
- // Cannot be called multiple times on a single Realm instance or an instance
- // which has already had update_schema() called on it.
- void set_schema_subset(Schema schema);
-
- // Read the schema version from the file specified by the given config, or
- // ObjectStore::NotVersioned if it does not exist
- static uint64_t get_schema_version(Config const& config);
-
- Config const& config() const { return m_config; }
- Schema const& schema() const { return m_schema; }
- uint64_t schema_version() const { return m_schema_version; }
-
- // Returns `true` if this Realm is a Partially synchronized Realm.
- bool is_partial() const noexcept;
-
- void begin_transaction();
- void commit_transaction();
- void cancel_transaction();
- bool is_in_transaction() const noexcept;
-
- // Returns a frozen copy for the current version of this Realm
- SharedRealm freeze();
-
- // Returns `true` if the Realm is frozen, `false` otherwise.
- bool is_frozen() const;
-
- // Returns true if the Realm is either in a read or frozen transaction
- bool is_in_read_transaction() const { return m_group != nullptr; }
- uint64_t last_seen_transaction_version() { return m_schema_transaction_version; }
-
- // Returns the number of versions in the Realm file.
- uint_fast64_t get_number_of_versions() const;
-
- VersionID read_transaction_version() const;
- Group& read_group();
-
- // Get the version of the current read or frozen transaction, or `none` if the Realm
- // is not in a read transaction
- util::Optional<VersionID> current_transaction_version() const;
-
- TransactionRef duplicate() const;
-
- void enable_wait_for_change();
- bool wait_for_change();
- void wait_for_change_release();
-
- bool is_in_migration() const noexcept { return m_in_migration; }
-
- bool refresh();
- void set_auto_refresh(bool auto_refresh);
- bool auto_refresh() const { return m_auto_refresh; }
- void notify();
-
- void invalidate();
-
- // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
- // because it's not crash safe! It may corrupt your database if something fails
- bool compact();
- void write_copy(StringData path, BinaryData encryption_key);
- OwnedBinaryData write_copy();
-
- void verify_thread() const;
- void verify_in_write() const;
- void verify_open() const;
- bool verify_notifications_available(bool throw_on_error = true) const;
-
- bool can_deliver_notifications() const noexcept;
- std::shared_ptr<util::Scheduler> scheduler() const noexcept { return m_scheduler; }
-
- // Close this Realm. Continuing to use a Realm after closing it will throw ClosedRealmException
- void close();
- bool is_closed() const { return !m_group && !m_coordinator; }
-
- // returns the file format version upgraded from if an upgrade took place
- util::Optional<int> file_format_upgraded_from_version() const;
-
- Realm(const Realm&) = delete;
- Realm& operator=(const Realm&) = delete;
- Realm(Realm&&) = delete;
- Realm& operator=(Realm&&) = delete;
- ~Realm();
-
- ComputedPrivileges get_privileges();
- ComputedPrivileges get_privileges(StringData object_type);
- ComputedPrivileges get_privileges(ConstObj const& obj);
-
- AuditInterface* audit_context() const noexcept;
-
- template<typename... Args>
- auto import_copy_of(Args&&... args)
- {
- return transaction().import_copy_of(std::forward<Args>(args)...);
- }
-
- static SharedRealm make_shared_realm(Config config, util::Optional<VersionID> version, std::shared_ptr<_impl::RealmCoordinator> coordinator)
- {
- return std::make_shared<Realm>(std::move(config), std::move(version), std::move(coordinator), MakeSharedTag{});
- }
-
- // Expose some internal functionality to other parts of the ObjectStore
- // without making it public to everyone
- class Internal {
- friend class _impl::CollectionNotifier;
- friend class _impl::PartialSyncHelper;
- friend class _impl::RealmCoordinator;
- friend class GlobalNotifier;
- friend class TestHelper;
- friend class ThreadSafeReference;
-
- static Transaction& get_transaction(Realm& realm) { return realm.transaction(); }
- static std::shared_ptr<Transaction> get_transaction_ref(Realm& realm) { return realm.transaction_ref(); }
-
- // CollectionNotifier needs to be able to access the owning
- // coordinator to wake up the worker thread when a callback is
- // added, and coordinators need to be able to get themselves from a Realm
- static _impl::RealmCoordinator& get_coordinator(Realm& realm) { return *realm.m_coordinator; }
-
- static std::shared_ptr<DB>& get_db(Realm& realm);
- static void begin_read(Realm&, VersionID);
- };
-
-private:
- struct MakeSharedTag {};
-
- std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
- std::unique_ptr<sync::TableInfoCache> m_table_info_cache;
- std::unique_ptr<sync::PermissionsCache> m_permissions_cache;
-
- Config m_config;
- util::Optional<VersionID> m_frozen_version;
- std::shared_ptr<util::Scheduler> m_scheduler;
- bool m_auto_refresh = true;
-
- std::shared_ptr<Group> m_group;
-
- uint64_t m_schema_version;
- Schema m_schema;
- util::Optional<Schema> m_new_schema;
- uint64_t m_schema_transaction_version = -1;
-
- // FIXME: this should be a Dynamic schema mode instead, but only once
- // that's actually fully working
- bool m_dynamic_schema = true;
-
- // True while sending the notifications caused by advancing the read
- // transaction version, to avoid recursive notifications where possible
- bool m_is_sending_notifications = false;
-
- // True while we're performing a schema migration via this Realm instance
- // to allow for different behavior (such as allowing modifications to
- // primary key values)
- bool m_in_migration = false;
-
- void begin_read(VersionID);
- bool do_refresh();
-
- void set_schema(Schema const& reference, Schema schema);
- bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
- bool schema_change_needs_write_transaction(Schema& schema, std::vector<SchemaChange>& changes, uint64_t version);
- Schema get_full_schema();
-
- // Ensure that m_schema and m_schema_version match that of the current
- // version of the file
- void read_schema_from_group_if_needed();
-
- void add_schema_change_handler();
- void cache_new_schema();
- void translate_schema_error();
- void notify_schema_changed();
-
- bool init_permission_cache();
- void invalidate_permission_cache();
-
- Transaction& transaction();
- Transaction& transaction() const;
- std::shared_ptr<Transaction> transaction_ref();
-
-public:
- std::unique_ptr<BindingContext> m_binding_context;
-
- // `enable_shared_from_this` is unsafe with public constructors; use `make_shared_realm` instead
- Realm(Config config, util::Optional<VersionID> version, std::shared_ptr<_impl::RealmCoordinator> coordinator, MakeSharedTag);
-};
-
-class RealmFileException : public std::runtime_error {
-public:
- enum class Kind {
- /** Thrown for any I/O related exception scenarios when a realm is opened. */
- AccessError,
- /** Thrown if the history type of the on-disk Realm is unexpected or incompatible. */
- BadHistoryError,
- /** Thrown if the user does not have permission to open or create
- the specified file in the specified access mode when the realm is opened. */
- PermissionDenied,
- /** Thrown if create_Always was specified and the file did already exist when the realm is opened. */
- Exists,
- /** Thrown if no_create was specified and the file was not found when the realm is opened. */
- NotFound,
- /** Thrown if the database file is currently open in another
- process which cannot share with the current process due to an
- architecture mismatch. */
- IncompatibleLockFile,
- /** 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::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::logic_error {
-public:
- MismatchedConfigException(StringData message, StringData path);
-};
-
-class MismatchedRealmException : public std::logic_error {
-public:
- MismatchedRealmException(StringData message);
-};
-
-class InvalidTransactionException : public std::logic_error {
-public:
- InvalidTransactionException(std::string message) : std::logic_error(message) {}
-};
-
-class IncorrectThreadException : public std::logic_error {
-public:
- IncorrectThreadException() : std::logic_error("Realm accessed from incorrect thread.") {}
-};
-
-class ClosedRealmException : public std::logic_error {
-public:
- ClosedRealmException() : std::logic_error("Cannot access realm that has been closed.") {}
-};
-
-class UninitializedRealmException : public std::runtime_error {
-public:
- UninitializedRealmException(std::string message) : std::runtime_error(message) {}
-};
-
-class InvalidEncryptionKeyException : public std::logic_error {
-public:
- InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
-};
-} // namespace realm
-
-#endif /* defined(REALM_REALM_HPP) */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp
deleted file mode 100644
index ecae494ae..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "thread_safe_reference.hpp"
-
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "results.hpp"
-#include "shared_realm.hpp"
-
-#include "impl/realm_coordinator.hpp"
-
-#include <realm/db.hpp>
-#include <realm/keys.hpp>
-
-namespace realm {
-class ThreadSafeReference::Payload {
-public:
- virtual ~Payload() = default;
- Payload(Realm& realm)
- : m_transaction(realm.is_in_read_transaction() ? realm.duplicate() : nullptr)
- , m_coordinator(Realm::Internal::get_coordinator(realm).shared_from_this())
- , m_created_in_write_transaction(realm.is_in_transaction())
- {
- }
-
- void refresh_target_realm(Realm&);
-
-protected:
- const TransactionRef m_transaction;
-
-private:
- const std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
- const bool m_created_in_write_transaction;
-};
-
-void ThreadSafeReference::Payload::refresh_target_realm(Realm& realm)
-{
- if (!realm.is_in_read_transaction()) {
- if (m_created_in_write_transaction)
- realm.read_group();
- else
- Realm::Internal::begin_read(realm, m_transaction->get_version_of_current_transaction());
- }
- else {
- auto version = realm.read_transaction_version();
- auto target_version = m_transaction->get_version_of_current_transaction();
- if (version < target_version || (version == target_version && m_created_in_write_transaction))
- realm.refresh();
- }
-}
-
-template<>
-class ThreadSafeReference::PayloadImpl<List> : public ThreadSafeReference::Payload {
-public:
- PayloadImpl(List const& list)
- : Payload(*list.get_realm())
- , m_key(list.get_parent_object_key())
- , m_table_key(list.get_parent_table_key())
- , m_col_key(list.get_parent_column_key())
- {
- }
-
- List import_into(std::shared_ptr<Realm> const& r)
- {
- Obj obj = r->read_group().get_table(m_table_key)->get_object(m_key);
- return List(r, obj, m_col_key);
- }
-
-private:
- ObjKey m_key;
- TableKey m_table_key;
- ColKey m_col_key;
-};
-
-template<>
-class ThreadSafeReference::PayloadImpl<Object> : public ThreadSafeReference::Payload {
-public:
- PayloadImpl(Object const& object)
- : Payload(*object.get_realm())
- , m_key(object.obj().get_key())
- , m_object_schema_name(object.get_object_schema().name)
- {
- }
-
- Object import_into(std::shared_ptr<Realm> const& r)
- {
- return Object(r, m_object_schema_name, m_key);
- }
-
-private:
- ObjKey m_key;
- std::string m_object_schema_name;
-};
-
-template<>
-class ThreadSafeReference::PayloadImpl<Results> : public ThreadSafeReference::Payload {
-public:
- PayloadImpl(Results const& r)
- : Payload(*r.get_realm())
- , m_ordering(r.get_descriptor_ordering())
- {
- if (auto list = r.get_list()) {
- m_key = list->get_key();
- m_table_key = list->get_table()->get_key();
- m_col_key = list->get_col_key();
- }
- else {
- Query q(r.get_query());
- if (!q.produces_results_in_table_order() && r.get_realm()->is_in_transaction()) {
- // FIXME: This is overly restrictive. It's only a problem if
- // the parent of the List or LinkingObjects was created in this
- // write transaction, but Query doesn't expose a way to check
- // if the source view is valid so we have to forbid it always.
- throw std::logic_error("Cannot create a ThreadSafeReference to Results backed by a List of objects or LinkingObjects inside a write transaction");
- }
- m_query = m_transaction->import_copy_of(q, PayloadPolicy::Stay);
- }
- }
-
- Results import_into(std::shared_ptr<Realm> const& r)
- {
- if (m_key) {
- LstBasePtr list;
- auto table = r->read_group().get_table(m_table_key);
- try {
- list = table->get_object(m_key).get_listbase_ptr(m_col_key);
- }
- catch (InvalidKey const&) {
- // Create a detached list of the appropriate type so that we
- // return an invalid Results rather than an Empty Results, to
- // match what happens for other types of handover where the
- // object doesn't exist.
- switch_on_type(ObjectSchema::from_core_type(*table, m_col_key), [&](auto* t) -> void {
- list = std::make_unique<Lst<NonObjTypeT<decltype(*t)>>>();
- });
- }
- return Results(r, std::move(list), m_ordering);
- }
- auto q = r->import_copy_of(*m_query, PayloadPolicy::Stay);
- return Results(std::move(r), std::move(*q), m_ordering);
- }
-
-private:
- DescriptorOrdering m_ordering;
- std::unique_ptr<Query> m_query;
- ObjKey m_key;
- TableKey m_table_key;
- ColKey m_col_key;
-};
-
-template<>
-class ThreadSafeReference::PayloadImpl<std::shared_ptr<Realm>> : public ThreadSafeReference::Payload {
-public:
- PayloadImpl(std::shared_ptr<Realm> const& realm)
- : Payload(*realm)
- , m_realm(realm)
- {
- }
-
- std::shared_ptr<Realm> get_realm()
- {
- return std::move(m_realm);
- }
-
-private:
- std::shared_ptr<Realm> m_realm;
-};
-
-ThreadSafeReference::ThreadSafeReference() noexcept = default;
-ThreadSafeReference::~ThreadSafeReference() = default;
-ThreadSafeReference::ThreadSafeReference(ThreadSafeReference&&) noexcept = default;
-ThreadSafeReference& ThreadSafeReference::operator=(ThreadSafeReference&&) noexcept = default;
-
-template<typename T>
-ThreadSafeReference::ThreadSafeReference(T const& value)
-{
- auto realm = value.get_realm();
- realm->verify_thread();
- m_payload.reset(new PayloadImpl<T>(value));
-}
-
-template<>
-ThreadSafeReference::ThreadSafeReference(std::shared_ptr<Realm> const& value)
-{
- m_payload.reset(new PayloadImpl<std::shared_ptr<Realm>>(value));
-}
-
-template ThreadSafeReference::ThreadSafeReference(List const&);
-template ThreadSafeReference::ThreadSafeReference(Results const&);
-template ThreadSafeReference::ThreadSafeReference(Object const&);
-
-template<typename T>
-T ThreadSafeReference::resolve(std::shared_ptr<Realm> const& realm)
-{
- REALM_ASSERT(realm);
- realm->verify_thread();
-
- REALM_ASSERT(m_payload);
- auto& payload = static_cast<PayloadImpl<T>&>(*m_payload);
- REALM_ASSERT(typeid(payload) == typeid(PayloadImpl<T>));
-
- m_payload->refresh_target_realm(*realm);
- try {
- return payload.import_into(realm);
- }
- catch (InvalidKey const&) {
- // Object was deleted in a version after when the TSR was created
- return {};
- }
-}
-
-template<>
-std::shared_ptr<Realm> ThreadSafeReference::resolve<std::shared_ptr<Realm>>(std::shared_ptr<Realm> const&)
-{
- REALM_ASSERT(m_payload);
- auto& payload = static_cast<PayloadImpl<std::shared_ptr<Realm>>&>(*m_payload);
- REALM_ASSERT(typeid(payload) == typeid(PayloadImpl<std::shared_ptr<Realm>>));
-
- return payload.get_realm();
-}
-
-template Results ThreadSafeReference::resolve<Results>(std::shared_ptr<Realm> const&);
-template List ThreadSafeReference::resolve<List>(std::shared_ptr<Realm> const&);
-template Object ThreadSafeReference::resolve<Object>(std::shared_ptr<Realm> const&);
-
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp
deleted file mode 100644
index 7dba87f5e..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_OS_THREAD_SAFE_REFERENCE_HPP
-#define REALM_OS_THREAD_SAFE_REFERENCE_HPP
-
-#include <memory>
-
-namespace realm {
-class List;
-class Object;
-class Realm;
-class Results;
-
-// Opaque type-ereased wrapper for a Realm object which can be imported into another Realm
-class ThreadSafeReference {
-public:
- ThreadSafeReference() noexcept;
- ~ThreadSafeReference();
- ThreadSafeReference(const ThreadSafeReference&) = delete;
- ThreadSafeReference& operator=(const ThreadSafeReference&) = delete;
- ThreadSafeReference(ThreadSafeReference&&) noexcept;
- ThreadSafeReference& operator=(ThreadSafeReference&&) noexcept;
-
- template<typename T>
- ThreadSafeReference(T const& value);
-
- // Import the object into the destination Realm
- template<typename T>
- T resolve(std::shared_ptr<Realm> const&);
-
- explicit operator bool() const noexcept { return !!m_payload; }
-
-private:
- class Payload;
- template<typename> class PayloadImpl;
- std::unique_ptr<Payload> m_payload;
-};
-
-template<> ThreadSafeReference::ThreadSafeReference(std::shared_ptr<Realm> const&);
-template<> std::shared_ptr<Realm> ThreadSafeReference::resolve(std::shared_ptr<Realm> const&);
-}
-
-#endif /* REALM_OS_THREAD_SAFE_REFERENCE_HPP */
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/aligned_union.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/aligned_union.hpp
deleted file mode 100644
index b75c649cc..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/aligned_union.hpp
+++ /dev/null
@@ -1,65 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 utilied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#ifndef REALM_OS_ALIGNED_UNION_HPP
-#define REALM_OS_ALIGNED_UNION_HPP
-
-#include <cstddef>
-#include <initializer_list>
-#include <type_traits>
-
-namespace realm {
-
-// Provide our own implementation of max as GCC 4.9's is not marked as constexpr.
-namespace _impl {
-
-template <typename T>
-static constexpr const T& constexpr_max(const T& a, const T& b)
-{
- return a > b ? a : b;
-}
-
-template <typename T>
-static constexpr const T& constexpr_max(const T* begin, const T *end)
-{
- return begin + 1 == end ? *begin : constexpr_max(*begin, constexpr_max(begin + 1, end));
-}
-
-template <typename T>
-static constexpr const T& constexpr_max(std::initializer_list<T> list)
-{
- return constexpr_max(list.begin(), list.end());
-}
-
-} // namespace _impl
-
-namespace util {
-
-// Provide our own implementation of `std::aligned_union` as it is missing from GCC 4.9.
-template <size_t Len, typename... Types>
-struct AlignedUnion
-{
- static constexpr size_t alignment_value = _impl::constexpr_max({alignof(Types)...});
- static constexpr size_t storage_size = _impl::constexpr_max({Len, sizeof(Types)...});
- using type = typename std::aligned_storage<storage_size, alignment_value>::type;
-};
-
-} // namespace util
-} // namespace realm
-
-#endif // REALM_OS_ALIGNED_UNION_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/atomic_shared_ptr.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/atomic_shared_ptr.hpp
deleted file mode 100644
index b4eb52f3a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/atomic_shared_ptr.hpp
+++ /dev/null
@@ -1,148 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_ATOMIC_SHARED_PTR_HPP
-#define REALM_ATOMIC_SHARED_PTR_HPP
-
-#include <atomic>
-#include <memory>
-#include <mutex>
-
-namespace realm {
-namespace _impl {
-
-// Check if std::atomic_load has an overload taking a std::shared_ptr, and set
-// HasAtomicPtrOps to either true_type or false_type
-
-template<typename... Ts> struct make_void { typedef void type; };
-template<typename... Ts> using void_t = typename make_void<Ts...>::type;
-
-template<typename, typename = void_t<>>
-struct HasAtomicPtrOps : std::false_type { };
-
-template<class T>
-struct HasAtomicPtrOps<T, void_t<decltype(std::atomic_load(std::declval<T*>()))>> : std::true_type { };
-} // namespace _impl
-
-namespace util {
-// A wrapper for std::shared_ptr that enables sharing a shared_ptr instance
-// (and not just a thing *pointed to* by a shared_ptr) between threads. Is
-// lock-free iff the underlying shared_ptr implementation supports atomic
-// operations. Currently the only implemented operation other than copy/move
-// construction/assignment is exchange().
-template<typename T, bool = _impl::HasAtomicPtrOps<std::shared_ptr<T>>::value>
-class AtomicSharedPtr;
-
-template<typename T>
-class AtomicSharedPtr<T, true> {
-public:
- AtomicSharedPtr() = default;
- AtomicSharedPtr(std::shared_ptr<T> ptr) : m_ptr(std::move(ptr)) { }
-
- AtomicSharedPtr(AtomicSharedPtr const& ptr) : m_ptr(std::atomic_load(&ptr.m_ptr)) { }
- AtomicSharedPtr(AtomicSharedPtr&& ptr) : m_ptr(std::atomic_exchange(&ptr.m_ptr, {})) { }
-
- AtomicSharedPtr& operator=(AtomicSharedPtr const& ptr)
- {
- if (&ptr != this) {
- std::atomic_store(&m_ptr, std::atomic_load(&ptr.m_ptr));
- }
- return *this;
- }
-
- AtomicSharedPtr& operator=(AtomicSharedPtr&& ptr)
- {
- std::atomic_store(&m_ptr, std::atomic_exchange(&ptr.m_ptr, {}));
- return *this;
- }
-
- std::shared_ptr<T> exchange(std::shared_ptr<T> ptr)
- {
- return std::atomic_exchange(&m_ptr, std::move(ptr));
- }
-
- std::shared_ptr<T> load() const noexcept
- {
- return std::atomic_load(&m_ptr);
- }
-
-private:
- std::shared_ptr<T> m_ptr = nullptr;
-};
-
-template<typename T>
-class AtomicSharedPtr<T, false> {
-public:
- AtomicSharedPtr() = default;
- AtomicSharedPtr(std::shared_ptr<T> ptr) : m_ptr(std::move(ptr)) { }
-
- AtomicSharedPtr(AtomicSharedPtr const& ptr)
- {
- std::lock_guard<std::mutex> lock(ptr.m_mutex);
- m_ptr = ptr.m_ptr;
- }
- AtomicSharedPtr(AtomicSharedPtr&& ptr)
- {
- std::lock_guard<std::mutex> lock(ptr.m_mutex);
- m_ptr = std::move(ptr.m_ptr);
- }
-
- AtomicSharedPtr& operator=(AtomicSharedPtr const& ptr)
- {
- if (&ptr != this) {
- // std::lock() ensures that these are locked in a consistent order
- // to avoid deadlock
- std::lock(m_mutex, ptr.m_mutex);
- m_ptr = ptr.m_ptr;
- m_mutex.unlock();
- ptr.m_mutex.unlock();
- }
- return *this;
- }
-
- AtomicSharedPtr& operator=(AtomicSharedPtr&& ptr)
- {
- std::lock(m_mutex, ptr.m_mutex);
- m_ptr = std::move(ptr.m_ptr);
- m_mutex.unlock();
- ptr.m_mutex.unlock();
- return *this;
- }
-
- std::shared_ptr<T> exchange(std::shared_ptr<T> ptr)
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_ptr.swap(ptr);
- return ptr;
- }
-
- std::shared_ptr<T> load() const noexcept
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- return m_ptr;
- }
-
-private:
- mutable std::mutex m_mutex;
- std::shared_ptr<T> m_ptr = nullptr;
-};
-
-}
-}
-
-#endif // REALM_ATOMIC_SHARED_PTR_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp
deleted file mode 100644
index 9baa2f258..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_UTIL_TAGGED_BOOL_HPP
-#define REALM_OS_UTIL_TAGGED_BOOL_HPP
-
-#include <type_traits>
-
-namespace realm {
-namespace util {
-// A type factory which defines a type which is implicitly convertable to and
-// from `bool`, but not to other TaggedBool types
-//
-// Usage:
-// using IsIndexed = util::TaggedBool<class IsIndexedTag>;
-// using IsPrimary = util::TaggedBool<class IsPrimaryTag>;
-// void foo(IsIndexed is_indexed, IsPrimary is_primary);
-//
-// foo(IsIndexed{true}, IsPrimary{false}); // compiles
-// foo(IsPrimary{true}, IsIndexed{false}); // doesn't compile
-template <typename Tag>
-struct TaggedBool {
- // Allow explicit construction from anything convertible to bool
- constexpr explicit TaggedBool(bool v) noexcept : m_value(v) { }
-
- // Allow implicit construction from *just* bool and not things convertible
- // to bool (such as other types of tagged bools)
- template <typename Bool, typename = typename std::enable_if<std::is_same<Bool, bool>::value>::type>
- constexpr TaggedBool(Bool v) noexcept : m_value(v) {}
-
- constexpr TaggedBool(TaggedBool const& v) noexcept : m_value(v.m_value) {}
-
- constexpr operator bool() const noexcept { return m_value; }
- constexpr TaggedBool operator!() const noexcept { return TaggedBool{!m_value}; }
-
- friend constexpr bool operator==(TaggedBool l, TaggedBool r) noexcept { return l.m_value == r.m_value; }
- friend constexpr bool operator!=(TaggedBool l, TaggedBool r) noexcept { return l.m_value != r.m_value; }
-
-private:
- bool m_value;
-};
-
-}
-}
-#endif // REALM_OS_UTIL_TAGGED_BOOL_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.cpp
deleted file mode 100644
index 011000226..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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/uuid.hpp"
-
-#include <algorithm>
-#include <array>
-#include <functional>
-#include <random>
-#include <stdio.h>
-
-namespace {
-
-// Seed `engine` with as much random state as it requires, based on the approach outlined in P0205R0.
-// <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html>
-template <typename T>
-T create_and_seed_engine()
-{
- constexpr auto bytes_needed = T::state_size * sizeof(typename T::result_type);
-
- constexpr auto numbers_needed = sizeof(std::random_device::result_type) < sizeof(std::seed_seq::result_type)
- ? (bytes_needed / sizeof(std::random_device::result_type))
- : (bytes_needed / sizeof(std::seed_seq::result_type));
-
- std::array<std::random_device::result_type, numbers_needed> state;
- std::random_device rd;
- std::generate(begin(state), end(state), std::ref(rd));
- std::seed_seq seeds(begin(state), end(state));
-
- T engine;
- engine.seed(seeds);
- return engine;
-}
-
-} // unnamed namespace
-
-namespace realm {
-namespace util {
-
-std::string uuid_string()
-{
- static auto engine = create_and_seed_engine<std::mt19937>();
-
- std::array<uint8_t, 16> uuid_bytes;
- std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<uint8_t>::max());
- std::generate(begin(uuid_bytes), end(uuid_bytes), [&] { return distribution(engine); });
-
- // Version 4 UUID.
- uuid_bytes[6] = (uuid_bytes[6] & 0x0f) | 0x40;
- // IETF variant.
- uuid_bytes[8] = (uuid_bytes[8] & 0x3f) | 0x80;
-
- std::array<char, 37> uuid_formatted;
- snprintf(uuid_formatted.data(), uuid_formatted.size(),
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
- uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
- uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
- uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
-
- return std::string(uuid_formatted.data(), uuid_formatted.size() - 1);
-}
-
-} // namespace util
-} // namespace realm
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.hpp
deleted file mode 100644
index 8f37e5214..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/uuid.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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_OS_UTIL_UUID_HPP
-#define REALM_OS_UTIL_UUID_HPP
-
-#include <string>
-
-namespace realm {
-namespace util {
-
-// Generate a random UUID and return its formatted string representation.
-std::string uuid_string();
-
-} // namespace util
-} // namespace realm
-
-#endif // REALM_OS_UTIL_UUID_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt
deleted file mode 100644
index 72f951c06..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-include_directories(../external/catch/single_include .)
-
-set(HEADERS
- util/event_loop.hpp
- util/index_helpers.hpp
- util/test_file.hpp
- util/test_utils.hpp
-)
-
-set(SOURCES
- collection_change_indices.cpp
- frozen_objects.cpp
- index_set.cpp
- list.cpp
- main.cpp
- migrations.cpp
- object.cpp
- object_store.cpp
- primitive_list.cpp
- realm.cpp
- results.cpp
- schema.cpp
- thread_safe_reference.cpp
- transaction_log_parsing.cpp
- uuid.cpp
-
- util/event_loop.cpp
- util/test_file.cpp
- util/test_utils.cpp
-)
-
-
-if(REALM_ENABLE_SYNC)
- list(APPEND HEADERS
- sync/sync_test_utils.hpp
- sync/session/session_util.hpp
- )
- list(APPEND SOURCES
- sync/file.cpp
- sync/metadata.cpp
- sync/partial_sync.cpp
- sync/permission.cpp
- sync/session/connection_change_notifications.cpp
- sync/session/progress_notifications.cpp
- sync/session/session.cpp
- sync/session/wait_for_completion.cpp
- sync/sync_manager.cpp
- sync/sync_test_utils.cpp
- sync/user.cpp
- )
-endif()
-
-if(REALM_ENABLE_SERVER)
- list(APPEND SOURCES
- sync/global_notifier.cpp
- )
-endif()
-
-add_executable(tests ${SOURCES} ${HEADERS})
-target_compile_definitions(tests PRIVATE ${PLATFORM_DEFINES})
-
-if(VSCODE_TEST_RUNNER)
- # Increase the Catch2 virtual console width so that the Visual Studio Code
- # Test Explorer extension can parse long test names
- target_compile_definitions(tests PRIVATE -DCATCH_CONFIG_CONSOLE_WIDTH=300)
-endif()
-
-if(REALM_ENABLE_SYNC)
- # It's necessary to explicitly link to realm-sync here to control the order in which libraries are
- # linked to avoid link errors when using GNU ld.
- target_link_libraries(tests realm-sync realm-sync-server realm-parser)
-endif()
-
-target_link_libraries(tests realm-object-store ${PLATFORM_LIBRARIES})
-add_custom_command(TARGET tests POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/sync-1.x.realm $<TARGET_FILE_DIR:tests>)
-
-create_coverage_target(generate-coverage tests)
-
-add_custom_target(run-tests USES_TERMINAL DEPENDS tests COMMAND ./tests)
-
-add_subdirectory(notifications-fuzzer)
-add_subdirectory(benchmarks)
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp
deleted file mode 100644
index 62a218844..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp
+++ /dev/null
@@ -1,965 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "impl/collection_notifier.hpp"
-
-#include "util/index_helpers.hpp"
-
-#include <limits>
-
-using namespace realm;
-
-TEST_CASE("collection_change: insert()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("adds the row to the insertions set") {
- c.insert(5);
- c.insert(8);
- REQUIRE_INDICES(c.insertions, 5, 8);
- }
-
- SECTION("shifts previous insertions and modifications") {
- c.insert(5);
- c.modify(8, 1);
-
- c.insert(1);
- REQUIRE_INDICES(c.insertions, 1, 6);
- REQUIRE_INDICES(c.modifications, 9);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 9);
- }
-
- SECTION("does not shift previous deletions") {
- c.erase(8);
- c.erase(3);
- c.insert(5);
-
- REQUIRE_INDICES(c.insertions, 5);
- REQUIRE_INDICES(c.deletions, 3, 8);
- }
-
- SECTION("shifts destination of previous moves after the insertion point") {
- c.moves = {{10, 5}, {10, 2}, {3, 10}};
- c.insert(4);
- REQUIRE_MOVES(c, {10, 6}, {10, 2}, {3, 11});
- }
-}
-
-TEST_CASE("collection_change: modify()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("marks the row as modified") {
- c.modify(5);
- REQUIRE_INDICES(c.modifications, 5);
- REQUIRE(c.columns.empty());
- }
-
- SECTION("also marks newly inserted rows as modified") {
- c.insert(5);
- c.modify(5);
- REQUIRE_INDICES(c.modifications, 5);
- REQUIRE(c.columns.empty());
- }
-
- SECTION("is idempotent") {
- c.modify(5);
- c.modify(5);
- c.modify(5);
- c.modify(5);
- REQUIRE_INDICES(c.modifications, 5);
- REQUIRE(c.columns.empty());
- }
-
- SECTION("marks the appropriate column as modified when applicable") {
- c.modify(5, 2);
- REQUIRE_INDICES(c.modifications, 5);
- REQUIRE(c.columns.size() == 1);
- REQUIRE_INDICES(c.columns[2], 5);
-
- c.modify(4, 2);
- REQUIRE_INDICES(c.modifications, 4, 5);
- REQUIRE(c.columns.size() == 1);
- REQUIRE_INDICES(c.columns[2], 4, 5);
-
- c.modify(3, 1);
- REQUIRE_INDICES(c.modifications, 3, 4, 5);
- REQUIRE(c.columns.size() == 2);
- REQUIRE_INDICES(c.columns[1], 3);
- REQUIRE_INDICES(c.columns[2], 4, 5);
- }
-}
-
-TEST_CASE("collection_change: erase()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("adds the row to the deletions set") {
- c.erase(5);
- REQUIRE_INDICES(c.deletions, 5);
- }
-
- SECTION("is shifted for previous deletions") {
- c.erase(5);
- c.erase(6);
- REQUIRE_INDICES(c.deletions, 5, 7);
- }
-
- SECTION("is shifted for previous insertions") {
- c.insert(5);
- c.erase(6);
- REQUIRE_INDICES(c.deletions, 5);
- }
-
- SECTION("removes previous insertions") {
- c.insert(5);
- c.erase(5);
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
- }
-
- SECTION("removes previous modifications") {
- c.modify(5, 0);
- c.erase(5);
- REQUIRE(c.modifications.empty());
- REQUIRE(c.columns[0].empty());
- REQUIRE_INDICES(c.deletions, 5);
- }
-
- SECTION("shifts previous modifications") {
- c.modify(5, 0);
- c.erase(4);
- REQUIRE_INDICES(c.modifications, 4);
- REQUIRE_COLUMN_INDICES(c.columns, 0, 4);
- REQUIRE_INDICES(c.deletions, 4);
- }
-
- SECTION("removes previous moves to the row being erased") {
- c.moves = {{10, 5}};
- c.erase(5);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("shifts the destination of previous moves") {
- c.moves = {{10, 5}, {10, 2}, {3, 10}};
- c.erase(4);
- REQUIRE_MOVES(c, {10, 4}, {10, 2}, {3, 9});
- }
-}
-
-TEST_CASE("collection_change: clear()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("removes all insertions") {
- c.insertions = {1, 2, 3};
- c.clear(0);
- REQUIRE(c.insertions.empty());
- }
-
- SECTION("removes all modifications") {
- c.modifications = {1, 2, 3};
- c.clear(0);
- REQUIRE(c.modifications.empty());
- }
-
- SECTION("removes all moves") {
- c.moves = {{1, 3}};
- c.clear(0);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("sets deletions to the number of rows before any changes") {
- c.insertions = {1, 2, 3};
- c.clear(5);
- REQUIRE_INDICES(c.deletions, 0, 1);
-
- c.deletions = {1, 2, 3};
- c.clear(5);
- REQUIRE_INDICES(c.deletions, 0, 1, 2, 3, 4, 5, 6, 7);
- }
-}
-
-TEST_CASE("collection_change: move()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("adds the move to the list of moves") {
- c.move(5, 6);
- REQUIRE_MOVES(c, {5, 6});
- }
-
- SECTION("updates previous moves to the source of this move") {
- c.move(5, 6);
- c.move(6, 7);
- REQUIRE_MOVES(c, {5, 7});
- }
-
- SECTION("shifts previous moves and is shifted by them") {
- c.move(5, 10);
- c.move(6, 12);
- REQUIRE_MOVES(c, {5, 9}, {7, 12});
-
- c.move(10, 0);
- REQUIRE_MOVES(c, {5, 10}, {7, 12}, {11, 0});
- }
-
- SECTION("does not report a move if the source is newly inserted") {
- c.insert(5);
- c.move(5, 10);
- REQUIRE_INDICES(c.insertions, 10);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("shifts previous insertions and modifications") {
- c.insert(5);
- c.modify(6, 0);
- c.move(10, 0);
- REQUIRE_INDICES(c.insertions, 0, 6);
- REQUIRE_INDICES(c.modifications, 7);
- REQUIRE_COLUMN_INDICES(c.columns, 0, 7);
- REQUIRE_MOVES(c, {9, 0});
- }
-
- SECTION("marks the target row as modified if the source row was") {
- c.modify(5, 0);
-
- c.move(5, 10);
- REQUIRE_INDICES(c.modifications, 10);
- REQUIRE_COLUMN_INDICES(c.columns, 0, 10);
-
- c.move(6, 12);
- REQUIRE_INDICES(c.modifications, 9);
- REQUIRE_COLUMN_INDICES(c.columns, 0, 9);
- }
-
- SECTION("bumps previous moves to the same location") {
- c.move(5, 10);
- c.move(7, 10);
- REQUIRE_MOVES(c, {5, 9}, {8, 10});
-
- c = {};
- c.move(5, 10);
- c.move(15, 10);
- REQUIRE_MOVES(c, {5, 11}, {15, 10});
- }
-
- SECTION("collapses redundant swaps of adjacent rows to a no-op") {
- c.move(7, 8);
- c.move(7, 8);
- c.clean_up_stale_moves();
- REQUIRE(c.empty());
- }
-}
-
-TEST_CASE("collection_change: calculate() table order") {
- _impl::CollectionChangeBuilder c;
-
- auto all_modified = [](size_t) { return true; };
- auto none_modified = [](size_t) { return false; };
- bool in_table_order = true;
-
- SECTION("returns an empty set when input and output are identical") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, in_table_order);
- REQUIRE(c.empty());
- }
-
- SECTION("marks all as inserted when prev is empty") {
- c = _impl::CollectionChangeBuilder::calculate({}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.insertions, 0, 1, 2);
- }
-
- SECTION("marks all as deleted when new is empty") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 0, 1, 2);
- }
-
- SECTION("marks npos rows in prev as deleted") {
- c = _impl::CollectionChangeBuilder::calculate({-1, 1, 2, 3, -1}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 0, 4);
- }
-
- SECTION("marks modified rows which do not move as modified") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.modifications, 0, 1, 2);
- }
-
- SECTION("does not mark unmodified rows as modified") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, in_table_order);
- REQUIRE(c.modifications.empty());
- }
-
- SECTION("marks newly added rows as insertions") {
- c = _impl::CollectionChangeBuilder::calculate({2, 3}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.insertions, 0);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 3}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.insertions, 1);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2}, {1, 2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.insertions, 2);
- }
-
- SECTION("marks removed rows as deleted") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {2, 3}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 0);
- }
-
- SECTION("marks rows as both inserted and deleted") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 4}, all_modified, in_table_order);
- REQUIRE_INDICES(c.deletions, 1);
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("does not mark rows as modified if they are new") {
- c = _impl::CollectionChangeBuilder::calculate({3}, {3, 5}, all_modified, in_table_order);
- REQUIRE_INDICES(c.modifications, 0);
- }
-
-#if 0 // FIXME: these tests might be applicable to LinkingObjects
- SECTION("reports moves which can be produced by move_last_over()") {
- auto calc = [&](std::vector<int64_t> values) {
- return _impl::CollectionChangeBuilder::calculate(values, {1, 2, 3}, none_modified, all);
- };
-
- REQUIRE(calc({1, 2, 3}).empty());
- REQUIRE_MOVES(calc({1, 3, 2}), {2, 1});
- REQUIRE_MOVES(calc({2, 1, 3}), {1, 0});
- REQUIRE_MOVES(calc({2, 3, 1}), {2, 0});
- REQUIRE_MOVES(calc({3, 1, 2}), {1, 0}, {2, 1});
- REQUIRE_MOVES(calc({3, 2, 1}), {2, 0}, {1, 1});
- }
-
- SECTION("resolves ambiguous moves using the candidate set") {
- c = _impl::CollectionChangeBuilder::calculate({3, 1, 2}, {1, 2, 3}, none_modified, all);
- REQUIRE_MOVES(c, {1, 0}, {2, 1});
- c = _impl::CollectionChangeBuilder::calculate({3, 1, 2}, {1, 2, 3}, none_modified, IndexSet{3});
- REQUIRE_MOVES(c, {0, 2});
- }
-#endif
-}
-
-TEST_CASE("collection_change: calculate() sorted") {
- _impl::CollectionChangeBuilder c;
-
- auto all_modified = [](size_t) { return true; };
- auto none_modified = [](size_t) { return false; };
- size_t npos = -1;
-
- SECTION("returns an empty set when input and output are identical") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified);
- REQUIRE(c.empty());
- }
-
- SECTION("marks all as inserted when prev is empty") {
- c = _impl::CollectionChangeBuilder::calculate({}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.insertions, 0, 1, 2);
- }
-
- SECTION("marks all as deleted when new is empty") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {}, all_modified);
- REQUIRE_INDICES(c.deletions, 0, 1, 2);
- }
-
- SECTION("marks npos rows in prev as deleted") {
- c = _impl::CollectionChangeBuilder::calculate({npos, 1, 2, 3, npos}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.deletions, 0, 4);
- }
-
- SECTION("marks modified rows which do not move as modified") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.modifications, 0, 1, 2);
- }
-
- SECTION("does not mark unmodified rows as modified") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified);
- REQUIRE(c.modifications.empty());
- }
-
- SECTION("marks newly added rows as insertions") {
- c = _impl::CollectionChangeBuilder::calculate({2, 3}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.insertions, 0);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 3}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.insertions, 1);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2}, {1, 2, 3}, all_modified);
- REQUIRE_INDICES(c.insertions, 2);
- }
-
- SECTION("marks removed rows as deleted") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2}, all_modified);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3}, all_modified);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {2, 3}, all_modified);
- REQUIRE_INDICES(c.deletions, 0);
- }
-
- SECTION("marks rows as both inserted and deleted") {
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 4}, all_modified);
- REQUIRE_INDICES(c.deletions, 1);
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("marks rows as modified even if they moved") {
- c = _impl::CollectionChangeBuilder::calculate({3, 5}, {5, 3}, all_modified);
- REQUIRE_INDICES(c.deletions, 1);
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.modifications, 0, 1);
- }
-
- SECTION("does not mark rows as modified if they are new") {
- c = _impl::CollectionChangeBuilder::calculate({3}, {3, 5}, all_modified);
- REQUIRE_INDICES(c.modifications, 0);
- }
-
- SECTION("reports inserts/deletes for simple reorderings") {
- auto calc = [&](std::vector<size_t> old_rows, std::vector<size_t> new_rows) {
- return _impl::CollectionChangeBuilder::calculate(old_rows, new_rows, none_modified);
- };
-
- c = calc({1, 2, 3}, {1, 2, 3});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
-
- c = calc({1, 2, 3}, {1, 3, 2});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({1, 2, 3}, {2, 1, 3});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({1, 2, 3}, {2, 3, 1});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({1, 2, 3}, {3, 1, 2});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({1, 2, 3}, {3, 2, 1});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({1, 3, 2}, {1, 2, 3});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({1, 3, 2}, {1, 3, 2});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
-
- c = calc({1, 3, 2}, {2, 1, 3});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({1, 3, 2}, {2, 3, 1});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({1, 3, 2}, {3, 1, 2});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({1, 3, 2}, {3, 2, 1});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({2, 1, 3}, {1, 2, 3});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({2, 1, 3}, {1, 3, 2});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({2, 1, 3}, {2, 1, 3});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
-
- c = calc({2, 1, 3}, {2, 3, 1});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({2, 1, 3}, {3, 1, 2});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({2, 1, 3}, {3, 2, 1});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({2, 3, 1}, {1, 2, 3});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({2, 3, 1}, {1, 3, 2});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({2, 3, 1}, {2, 1, 3});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({2, 3, 1}, {2, 3, 1});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
-
- c = calc({2, 3, 1}, {3, 1, 2});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({2, 3, 1}, {3, 2, 1});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({3, 1, 2}, {1, 2, 3});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({3, 1, 2}, {1, 3, 2});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({3, 1, 2}, {2, 1, 3});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({3, 1, 2}, {2, 3, 1});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({3, 1, 2}, {3, 1, 2});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
-
- c = calc({3, 1, 2}, {3, 2, 1});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({3, 2, 1}, {1, 2, 3});
- REQUIRE_INDICES(c.insertions, 0, 1);
- REQUIRE_INDICES(c.deletions, 1, 2);
-
- c = calc({3, 2, 1}, {1, 3, 2});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({3, 2, 1}, {2, 1, 3});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.deletions, 0);
-
- c = calc({3, 2, 1}, {2, 3, 1});
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
-
- c = calc({3, 2, 1}, {3, 1, 2});
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.deletions, 2);
-
- c = calc({3, 2, 1}, {3, 2, 1});
- REQUIRE(c.insertions.empty());
- REQUIRE(c.deletions.empty());
- }
-
- SECTION("prefers to produce diffs where modified rows are the ones to move when it is ambiguous") {
- auto two_modified = [](size_t ndx) { return ndx == 2; };
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 2}, two_modified);
- REQUIRE_INDICES(c.deletions, 1);
- REQUIRE_INDICES(c.insertions, 2);
-
- auto three_modified = [](size_t ndx) { return ndx == 3; };
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 2}, three_modified);
- REQUIRE_INDICES(c.deletions, 2);
- REQUIRE_INDICES(c.insertions, 1);
- }
-
- SECTION("prefers smaller diffs over larger diffs moving only modified rows") {
- auto two_modified = [](size_t ndx) { return ndx == 2; };
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {2, 3, 1}, two_modified);
- REQUIRE_INDICES(c.deletions, 0);
- REQUIRE_INDICES(c.insertions, 2);
- }
-
- SECTION("supports duplicate indices") {
- c = _impl::CollectionChangeBuilder::calculate({1, 1, 2, 2, 3, 3},
- {1, 2, 3, 1, 2, 3},
- all_modified);
- REQUIRE_INDICES(c.deletions, 3, 5);
- REQUIRE_INDICES(c.insertions, 1, 2);
- }
-
- SECTION("deletes and inserts the last option when any in a range could be deleted") {
- c = _impl::CollectionChangeBuilder::calculate({3, 2, 1, 1, 2, 3},
- {1, 1, 2, 2, 3, 3},
- all_modified);
- REQUIRE_INDICES(c.deletions, 0, 1);
- REQUIRE_INDICES(c.insertions, 3, 5);
- }
-
- SECTION("reports insertions/deletions when the number of duplicate entries changes") {
- c = _impl::CollectionChangeBuilder::calculate({1, 1, 1, 1, 2, 3},
- {1, 2, 3, 1},
- all_modified);
- REQUIRE_INDICES(c.deletions, 1, 2, 3);
- REQUIRE_INDICES(c.insertions, 3);
-
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3, 1},
- {1, 1, 1, 1, 2, 3},
- all_modified);
- REQUIRE_INDICES(c.deletions, 3);
- REQUIRE_INDICES(c.insertions, 1, 2, 3);
- }
-
- SECTION("properly recurses into smaller subblocks") {
- std::vector<size_t> prev = {10, 1, 2, 11, 3, 4, 5, 12, 6, 7, 13};
- std::vector<size_t> next = {13, 1, 2, 12, 3, 4, 5, 11, 6, 7, 10};
- c = _impl::CollectionChangeBuilder::calculate(prev, next, all_modified);
- REQUIRE_INDICES(c.deletions, 0, 3, 7, 10);
- REQUIRE_INDICES(c.insertions, 0, 3, 7, 10);
- }
-
- SECTION("produces diffs which let merge collapse insert -> move -> delete to no-op") {
- auto four_modified = [](size_t ndx) { return ndx == 4; };
- for (int insert_pos = 0; insert_pos < 4; ++insert_pos) {
- for (int move_to_pos = 0; move_to_pos < 4; ++move_to_pos) {
- if (insert_pos == move_to_pos)
- continue;
- CAPTURE(insert_pos);
- CAPTURE(move_to_pos);
-
- std::vector<size_t> after_insert = {1, 2, 3};
- after_insert.insert(after_insert.begin() + insert_pos, 4);
- c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, after_insert, four_modified);
-
- std::vector<size_t> after_move = {1, 2, 3};
- after_move.insert(after_move.begin() + move_to_pos, 4);
- c.merge(_impl::CollectionChangeBuilder::calculate(after_insert, after_move, four_modified));
-
- c.merge(_impl::CollectionChangeBuilder::calculate(after_move, {1, 2, 3}, four_modified));
- REQUIRE(c.empty());
- }
- }
- }
-}
-
-TEST_CASE("collection_change: merge()") {
- _impl::CollectionChangeBuilder c;
-
- SECTION("is a no-op if the new set is empty") {
- c = {{1, 2, 3}, {4, 5}, {6, 7}, {{8, 9}}};
- c.columns = {{0, {6}}, {1, {7}}};
- c.merge({});
- REQUIRE_INDICES(c.deletions, 1, 2, 3, 8);
- REQUIRE_INDICES(c.insertions, 4, 5, 9);
- REQUIRE_INDICES(c.modifications, 6, 7);
- REQUIRE_COLUMN_INDICES(c.columns, 0, 6);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 7);
- REQUIRE_MOVES(c, {8, 9});
- }
-
- SECTION("replaces the set with the new set if the old set is empty") {
- c.merge({{1, 2, 3}, {4, 5}, {6, 7}, {{8, 9}}});
- REQUIRE_INDICES(c.deletions, 1, 2, 3, 8);
- REQUIRE_INDICES(c.insertions, 4, 5, 9);
- REQUIRE_INDICES(c.modifications, 6, 7);
- REQUIRE_MOVES(c, {8, 9});
- }
-
- SECTION("shifts deletions by previous deletions") {
- c = {{5}, {}, {}, {}};
- c.merge({{3}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 3, 5);
-
- c = {{5}, {}, {}, {}};
- c.merge({{4}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 4, 5);
-
- c = {{5}, {}, {}, {}};
- c.merge({{5}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 5, 6);
-
- c = {{5}, {}, {}, {}};
- c.merge({{6}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 5, 7);
- }
-
- SECTION("shifts deletions by previous insertions") {
- c = {{}, {5}, {}, {}};
- c.merge({{4}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 4);
-
- c = {{}, {5}, {}, {}};
- c.merge({{6}, {}, {}, {}});
- REQUIRE_INDICES(c.deletions, 5);
- }
-
- SECTION("shifts previous insertions by deletions") {
- c = {{}, {2, 3}, {}, {}};
- c.merge({{1}, {}, {}, {}});
- REQUIRE_INDICES(c.insertions, 1, 2);
- }
-
- SECTION("removes previous insertions for newly deleted rows") {
- c = {{}, {1, 2}, {}, {}};
- c.merge({{2}, {}, {}, {}});
- REQUIRE_INDICES(c.insertions, 1);
- }
-
- SECTION("removes previous modifications for newly deleted rows") {
- c = {{}, {}, {2, 3}, {}};
- c.merge({{2}, {}, {}, {}});
- REQUIRE_INDICES(c.modifications, 2);
- }
-
- SECTION("shifts previous modifications for deletions of other rows") {
- c = {{}, {}, {2, 3}, {}};
- c.merge({{1}, {}, {}, {}});
- REQUIRE_INDICES(c.modifications, 1, 2);
- }
-
- SECTION("removes moves to rows which have been deleted") {
- c = {{}, {}, {}, {{2, 3}}};
- c.merge({{3}, {}, {}, {}});
- REQUIRE(c.moves.empty());
- }
-
- SECTION("shifts destinations of previous moves to reflect new deletions") {
- c = {{}, {}, {}, {{2, 5}}};
- c.merge({{3}, {}, {}, {}});
- REQUIRE_MOVES(c, {2, 4});
- }
-
- SECTION("does not modify old deletions based on new insertions") {
- c = {{1, 3}, {}, {}, {}};
- c.merge({{}, {1, 2, 3}, {}, {}});
- REQUIRE_INDICES(c.deletions, 1, 3);
- REQUIRE_INDICES(c.insertions, 1, 2, 3);
- }
-
- SECTION("shifts previous insertions to reflect new insertions") {
- c = {{}, {1, 5}, {}, {}};
- c.merge({{}, {1, 4}, {}, {}});
- REQUIRE_INDICES(c.insertions, 1, 2, 4, 7);
- }
-
- SECTION("shifts previous modifications to reflect new insertions") {
- c = {{}, {}, {1, 5}, {}};
- c.merge({{}, {1, 4}, {}, {}});
- REQUIRE_INDICES(c.modifications, 2, 7);
- REQUIRE_INDICES(c.insertions, 1, 4);
- }
-
- SECTION("shifts previous move destinations to reflect new insertions") {
- c = {{}, {}, {}, {{2, 5}}};
- c.merge({{}, {3}, {}});
- REQUIRE_MOVES(c, {2, 6});
- }
-
- SECTION("does not modify old deletions based on new modifications") {
- c = {{1, 2, 3}, {}, {}, {}};
- c.merge({{}, {}, {2}});
- REQUIRE_INDICES(c.deletions, 1, 2, 3);
- REQUIRE_INDICES(c.modifications, 2);
- }
-
- SECTION("tracks modifications made to previously inserted rows") {
- c = {{}, {2}, {}, {}};
- c.merge({{}, {}, {1, 2, 3}});
- REQUIRE_INDICES(c.insertions, 2);
- REQUIRE_INDICES(c.modifications, 1, 2, 3);
- }
-
- SECTION("unions modifications with old modifications") {
- c = {{}, {}, {2}, {}};
- c.merge({{}, {}, {1, 2, 3}});
- REQUIRE_INDICES(c.modifications, 1, 2, 3);
- }
-
- SECTION("tracks modifications for previous moves") {
- c = {{}, {}, {}, {{1, 2}}};
- c.merge({{}, {}, {2, 3}});
- REQUIRE_INDICES(c.modifications, 2, 3);
- }
-
- SECTION("updates new move sources to reflect previous inserts and deletes") {
- c = {{1}, {}, {}, {}};
- c.merge({{}, {}, {}, {{2, 3}}});
- REQUIRE_MOVES(c, {3, 3});
-
- c = {{}, {1}, {}, {}};
- c.merge({{}, {}, {}, {{2, 3}}});
- REQUIRE_MOVES(c, {1, 3});
-
- c = {{2}, {4}, {}, {}};
- c.merge({{}, {}, {}, {{5, 10}}});
- REQUIRE_MOVES(c, {5, 10});
- }
-
- SECTION("updates the row modified for rows moved after a modification") {
- c = {{}, {}, {1}, {}};
- c.merge({{}, {}, {}, {{1, 3}}});
- REQUIRE_INDICES(c.modifications, 3);
- REQUIRE_MOVES(c, {1, 3});
- }
-
- SECTION("updates the row modified for chained moves") {
- c = {{}, {}, {1}, {}};
- c.merge({{}, {}, {}, {{1, 3}}});
- c.merge({{}, {}, {}, {{3, 5}}});
- REQUIRE_INDICES(c.modifications, 5);
- REQUIRE_MOVES(c, {1, 5});
- }
-
- SECTION("updates the row inserted for moves of previously new rows") {
- c = {{}, {1}, {}, {}};
- c.merge({{}, {}, {}, {{1, 3}}});
- REQUIRE(c.moves.empty());
- REQUIRE_INDICES(c.insertions, 3);
- }
-
- SECTION("updates old moves when the destination is moved again") {
- c = {{}, {}, {}, {{1, 3}}};
- c.merge({{}, {}, {}, {{3, 5}}});
- REQUIRE_MOVES(c, {1, 5});
- }
-
- SECTION("shifts destination of previous moves to reflect new moves like an insert/delete pair would") {
- c = {{}, {}, {}, {{1, 3}}};
- c.merge({{}, {}, {}, {{2, 5}}});
- REQUIRE_MOVES(c, {1, 2}, {3, 5});
-
- c = {{}, {}, {}, {{1, 10}}};
- c.merge({{}, {}, {}, {{2, 5}}});
- REQUIRE_MOVES(c, {1, 10}, {3, 5});
-
- c = {{}, {}, {}, {{5, 10}}};
- c.merge({{}, {}, {}, {{12, 2}}});
- REQUIRE_MOVES(c, {5, 11}, {12, 2});
- }
-
- SECTION("moves shift previous inserts like an insert/delete pair would") {
- c = {{}, {5}};
- c.merge({{}, {}, {}, {{2, 6}}});
- REQUIRE_INDICES(c.insertions, 4, 6);
- }
-
- SECTION("moves shift previous modifications like an insert/delete pair would") {
- c = {{}, {}, {5}};
- c.merge({{}, {}, {}, {{2, 6}}});
- REQUIRE_INDICES(c.modifications, 4);
- }
-
- SECTION("moves are shifted by previous deletions like an insert/delete pair would") {
- c = {{5}};
- c.merge({{}, {}, {}, {{2, 6}}});
- REQUIRE_MOVES(c, {2, 6});
-
- c = {{5}};
- c.merge({{}, {}, {}, {{6, 2}}});
- REQUIRE_MOVES(c, {7, 2});
- }
-
- SECTION("leapfrogging rows collapse to an empty changeset") {
- c = {{1}, {0}, {}, {{1, 0}}};
- c.merge({{1}, {0}, {}, {{1, 0}}});
- REQUIRE(c.empty());
- }
-
- SECTION("modify -> move -> unmove leaves row marked as modified") {
- c = {{}, {}, {1}};
- c.merge({{1}, {2}, {}, {{1, 2}}});
- c.merge({{1}});
-
- REQUIRE_INDICES(c.deletions, 2);
- REQUIRE(c.insertions.empty());
- REQUIRE(c.moves.empty());
- REQUIRE_INDICES(c.modifications, 1);
- }
-
- SECTION("modifying a previously moved row which stops being a move due to more deletions") {
- // Make it stop being a move in the same transaction as the modify
- c = {{1, 2}, {0, 1}, {}, {{1, 0}, {2, 1}}};
- c.merge({{0, 2}, {1}, {0}, {}});
-
- REQUIRE_INDICES(c.deletions, 0, 1);
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.modifications, 0);
- REQUIRE(c.moves.empty());
-
- // Same net change, but make it no longer a move in the transaction after the modify
- c = {{1, 2}, {0, 1}, {}, {{1, 0}, {2, 1}}};
- c.merge({{}, {}, {1}, {}});
- c.merge({{0, 2}, {0}, {}, {{2, 0}}});
- c.merge({{0}, {1}, {}, {}});
-
- REQUIRE_INDICES(c.deletions, 0, 1);
- REQUIRE_INDICES(c.insertions, 1);
- REQUIRE_INDICES(c.modifications, 0);
- REQUIRE(c.moves.empty());
- }
-
- SECTION("column-level modifications") {
- _impl::CollectionChangeBuilder c2;
- c = {{}, {}, {1, 2, 3}, {}};
- c.columns = {{0, {1}}, {1, {2, 3}}};
-
- SECTION("preserved on no-op merges") {
- c.merge({});
- REQUIRE_COLUMN_INDICES(c.columns, 0, 1);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 2, 3);
-
- c2.merge(std::move(c));
- REQUIRE_COLUMN_INDICES(c2.columns, 0, 1);
- REQUIRE_COLUMN_INDICES(c2.columns, 1, 2, 3);
- }
-
- SECTION("merged with other column-level modifications") {
- c2.modifications = {0, 4};
- c2.columns = {{0, {1, 2}}, {2, {4}}};
- c.merge(std::move(c2));
-
- REQUIRE_COLUMN_INDICES(c.columns, 0, 1, 2);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 2, 3);
- REQUIRE_COLUMN_INDICES(c.columns, 2, 4);
- }
-
- SECTION("removed by deletions") {
- c2.deletions = {2};
- c.merge(std::move(c2));
- REQUIRE_COLUMN_INDICES(c.columns, 0, 1);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 2);
- }
-
- SECTION("shifted by insertions") {
- c2.insertions = {3};
- c.merge(std::move(c2));
- REQUIRE_COLUMN_INDICES(c.columns, 0, 1);
- REQUIRE_COLUMN_INDICES(c.columns, 1, 2, 4);
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/index_set.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/index_set.cpp
deleted file mode 100644
index 2fb78009c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/index_set.cpp
+++ /dev/null
@@ -1,610 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "index_set.hpp"
-
-#include "util/index_helpers.hpp"
-
-TEST_CASE("index_set: contains()") {
- SECTION("returns false if the index is before the first entry in the set") {
- realm::IndexSet set = {1, 2, 5};
- REQUIRE_FALSE(set.contains(0));
- }
-
- SECTION("returns false if the index is after the last entry in the set") {
- realm::IndexSet set = {1, 2, 5};
- REQUIRE_FALSE(set.contains(6));
- }
-
- SECTION("returns false if the index is between ranges in the set") {
- realm::IndexSet set = {1, 2, 5};
- REQUIRE_FALSE(set.contains(4));
- }
-
- SECTION("returns true if the index is in the set") {
- realm::IndexSet set = {1, 2, 5};
- REQUIRE(set.contains(1));
- REQUIRE(set.contains(2));
- REQUIRE(set.contains(5));
- }
-}
-
-TEST_CASE("index_set: count()") {
- SECTION("returns the number of indices in the set in the given range") {
- realm::IndexSet set = {1, 2, 3, 5};
- REQUIRE(set.count(0, 6) == 4);
- REQUIRE(set.count(0, 5) == 3);
- REQUIRE(set.count(0, 4) == 3);
- REQUIRE(set.count(0, 3) == 2);
- REQUIRE(set.count(0, 2) == 1);
- REQUIRE(set.count(0, 1) == 0);
- REQUIRE(set.count(0, 0) == 0);
-
- REQUIRE(set.count(0, 6) == 4);
- REQUIRE(set.count(1, 6) == 4);
- REQUIRE(set.count(2, 6) == 3);
- REQUIRE(set.count(3, 6) == 2);
- REQUIRE(set.count(4, 6) == 1);
- REQUIRE(set.count(5, 6) == 1);
- REQUIRE(set.count(6, 6) == 0);
- }
-
- SECTION("includes full ranges in the middle") {
- realm::IndexSet set = {1, 3, 4, 5, 10};
- REQUIRE(set.count(0, 11) == 5);
- }
-
- SECTION("truncates ranges at the beginning and end") {
- realm::IndexSet set = {1, 2, 3, 5, 6, 7, 8, 9};
- REQUIRE(set.count(3, 9) == 5);
- }
-
- SECTION("handles full chunks well") {
- size_t count = realm::_impl::ChunkedRangeVector::max_size * 4;
- realm::IndexSet set;
- for (size_t i = 0; i < count; ++i) {
- set.add(i * 3);
- set.add(i * 3 + 1);
- }
-
- for (size_t i = 0; i < count * 3; ++i) {
- REQUIRE(set.count(i) == 2 * count - (i + 1) * 2 / 3);
- REQUIRE(set.count(0, i) == (i + 1) / 3 + (i + 2) / 3);
- }
- }
-}
-
-TEST_CASE("index_set: add()") {
- realm::IndexSet set;
-
- SECTION("extends existing ranges when next to an edge") {
- set.add(1);
- REQUIRE_INDICES(set, 1);
-
- set.add(2);
- REQUIRE_INDICES(set, 1, 2);
-
- set.add(0);
- REQUIRE_INDICES(set, 0, 1, 2);
- }
-
- SECTION("does not extend ranges over gaps") {
- set.add(0);
- REQUIRE_INDICES(set, 0);
-
- set.add(2);
- REQUIRE_INDICES(set, 0, 2);
- }
-
- SECTION("does nothing when the index is already in the set") {
- set.add(0);
- set.add(0);
- REQUIRE_INDICES(set, 0);
- }
-
- SECTION("merges existing ranges when adding the index between them") {
- set = {0, 2, 4};
-
- set.add(1);
- REQUIRE_INDICES(set, 0, 1, 2, 4);
- }
-
- SECTION("combines multiple index sets without any shifting") {
- set = {0, 2, 6};
-
- set.add({1, 4, 5});
- REQUIRE_INDICES(set, 0, 1, 2, 4, 5, 6);
- }
-
- SECTION("handles front additions of ranges") {
- for (size_t i = 20; i > 0; i -= 2)
- set.add(i);
- REQUIRE_INDICES(set, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20);
- }
-
- SECTION("merges ranges even when they are in different chunks") {
- realm::IndexSet set2;
- for (int i = 0; i < 20; ++i) {
- set.add(i * 2);
- set2.add(i);
- set2.add(i * 2);
- }
- set.add(set2);
- REQUIRE(set.count() == 30);
- }
-}
-
-TEST_CASE("index_set: add_shifted()") {
- realm::IndexSet set;
-
- SECTION("on an empty set is just add()") {
- set.add_shifted(5);
- REQUIRE_INDICES(set, 5);
- }
-
- SECTION("before the first range is just add()") {
- set = {10};
- set.add_shifted(5);
- REQUIRE_INDICES(set, 5, 10);
- }
-
- SECTION("on first index of a range extends the range") {
- set = {5};
-
- set.add_shifted(5);
- REQUIRE_INDICES(set, 5, 6);
-
- set.add_shifted(5);
- REQUIRE_INDICES(set, 5, 6, 7);
- }
-
- SECTION("in the middle of a range is shifted by that range") {
- set = {5, 6, 7};
- set.add_shifted(6);
- REQUIRE_INDICES(set, 5, 6, 7, 9);
- }
-
- SECTION("after the last range adds the total count to the index to be added") {
- set = {5};
-
- set.add_shifted(6);
- REQUIRE_INDICES(set, 5, 7);
-
- set.add_shifted(10);
- REQUIRE_INDICES(set, 5, 7, 12);
- }
-
- SECTION("in between ranges can be bumped into the next range") {
- set = {5, 7};
- set.add_shifted(6);
- REQUIRE_INDICES(set, 5, 7, 8);
- }
-}
-
-TEST_CASE("index_set: add_shifted_by()") {
- realm::IndexSet set;
-
- SECTION("does nothing given an empty set to add") {
- set = {5, 6, 7};
- set.add_shifted_by({5, 6}, {});
- REQUIRE_INDICES(set, 5, 6, 7);
- }
-
- SECTION("does nothing if values is a subset of shifted_by") {
- set = {5, 6, 7};
- set.add_shifted_by({3, 4}, {3, 4});
- REQUIRE_INDICES(set, 5, 6, 7);
- }
-
- SECTION("just adds the indices when they are all before the old indices and the shifted-by set is empty") {
- set = {5, 6};
- set.add_shifted_by({}, {3, 4});
- REQUIRE_INDICES(set, 3, 4, 5, 6);
- }
-
- SECTION("adds the indices shifted by the old count when they are all after the old indices and the shifted-by set is empty") {
- set = {5, 6};
- set.add_shifted_by({}, {7, 9, 11, 13});
- REQUIRE_INDICES(set, 5, 6, 9, 11, 13, 15);
- }
-
- SECTION("acts like bulk add_shifted() when shifted_by is empty") {
- set = {5, 10, 15, 20, 25};
- set.add_shifted_by({}, {4, 5, 11});
- REQUIRE_INDICES(set, 4, 5, 6, 10, 13, 15, 20, 25);
- }
-
- SECTION("shifts indices in values back by the number of indices in shifted_by before them") {
- set = {5};
- set.add_shifted_by({0, 2, 3}, {6});
- REQUIRE_INDICES(set, 3, 5);
-
- set = {5};
- set.add_shifted_by({1, 3}, {4});
- REQUIRE_INDICES(set, 2, 5);
- }
-
- SECTION("discards indices in both shifted_by and values") {
- set = {5};
- set.add_shifted_by({2}, {2, 4});
- REQUIRE_INDICES(set, 3, 5);
- }
-}
-
-TEST_CASE("index_set: set()") {
- realm::IndexSet set;
-
- SECTION("clears the existing indices and replaces with the range [0, value)") {
- set = {8, 9};
- set.set(5);
- REQUIRE_INDICES(set, 0, 1, 2, 3, 4);
- }
-}
-
-TEST_CASE("index_set: insert_at()") {
- realm::IndexSet set;
-
- SECTION("on an empty set is add()") {
- set.insert_at(5);
- REQUIRE_INDICES(set, 5);
-
- set = {};
- set.insert_at({1, 3, 5});
- REQUIRE_INDICES(set, 1, 3, 5);
- }
-
- SECTION("with an empty set is a no-op") {
- set = {5, 6};
- set.insert_at(realm::IndexSet{});
- REQUIRE_INDICES(set, 5, 6);
- }
-
- SECTION("extends ranges containing the target range") {
- set = {5, 6};
-
- set.insert_at(5);
- REQUIRE_INDICES(set, 5, 6, 7);
-
- set.insert_at(6, 2);
- REQUIRE_INDICES(set, 5, 6, 7, 8, 9);
-
- set.insert_at({5, 7, 11});
- REQUIRE_INDICES(set, 5, 6, 7, 8, 9, 10, 11, 12);
- }
-
- SECTION("shifts ranges after the insertion point") {
- set = {5, 6};
-
- set.insert_at(3);
- REQUIRE_INDICES(set, 3, 6, 7);
-
- set.insert_at(0, 2);
- REQUIRE_INDICES(set, 0, 1, 5, 8, 9);
- }
-
- SECTION("does not shift ranges before the insertion point") {
- set = {5, 6};
-
- set.insert_at(10);
- REQUIRE_INDICES(set, 5, 6, 10);
-
- set.insert_at({15, 16});
- REQUIRE_INDICES(set, 5, 6, 10, 15, 16);
- }
-
- SECTION("can not join ranges") {
- set = {5, 7};
- set.insert_at(6);
- REQUIRE_INDICES(set, 5, 6, 8);
- }
-
- SECTION("adds later ranges after shifting for previous insertions") {
- set = {5, 10};
- set.insert_at({5, 10});
- REQUIRE_INDICES(set, 5, 6, 10, 12);
- }
-}
-
-TEST_CASE("index_set: shift_for_insert_at()") {
- realm::IndexSet set;
-
- SECTION("does nothing given an empty set of insertion points") {
- set = {5, 8};
- set.shift_for_insert_at(realm::IndexSet{});
- REQUIRE_INDICES(set, 5, 8);
- }
-
- SECTION("does nothing when called on an empty set") {
- set = {};
- set.shift_for_insert_at({5, 8});
- REQUIRE(set.empty());
- }
-
- SECTION("does nothing when the insertion points are all after the current indices") {
- set = {10, 20};
- set.shift_for_insert_at({30, 40});
- REQUIRE_INDICES(set, 10, 20);
- }
-
- SECTION("does shift when the insertion points are all before the current indices") {
- set = {10, 20};
- set.shift_for_insert_at({2, 4});
- REQUIRE_INDICES(set, 12, 22);
- }
-
- SECTION("shifts indices at or after the insertion points") {
- set = {5};
-
- set.shift_for_insert_at(4);
- REQUIRE_INDICES(set, 6);
-
- set.shift_for_insert_at(6);
- REQUIRE_INDICES(set, 7);
-
- set.shift_for_insert_at({3, 8});
- REQUIRE_INDICES(set, 9);
- }
-
- SECTION("shifts indices by the count specified") {
- set = {5};
- set.shift_for_insert_at(3, 10);
- REQUIRE_INDICES(set, 15);
- }
-
- SECTION("does not shift indices before the insertion points") {
- set = {5};
-
- set.shift_for_insert_at(6);
- REQUIRE_INDICES(set, 5);
-
- set.shift_for_insert_at({3, 8});
- REQUIRE_INDICES(set, 6);
- }
-
- SECTION("splits ranges containing the insertion points") {
- set = {5, 6, 7, 8};
-
- set.shift_for_insert_at(6);
- REQUIRE_INDICES(set, 5, 7, 8, 9);
-
- set.shift_for_insert_at({8, 10, 12});
- REQUIRE_INDICES(set, 5, 7, 9, 11);
- }
-}
-
-TEST_CASE("index_set: erase_at()") {
- realm::IndexSet set;
-
- SECTION("is a no-op on an empty set") {
- set.erase_at(10);
- REQUIRE(set.empty());
-
- set.erase_at({1, 5, 8});
- REQUIRE(set.empty());
- }
-
- SECTION("does nothing when given an empty set") {
- set = {5};
- set.erase_at(realm::IndexSet{});
- REQUIRE_INDICES(set, 5);
- }
-
- SECTION("removes the specified indices") {
- set = {5};
- set.erase_at(5);
- REQUIRE(set.empty());
-
- set = {4, 7};
- set.erase_at({4, 7});
- REQUIRE(set.empty());
- }
-
- SECTION("does not modify indices before the removed one") {
- set = {5, 8};
- set.erase_at(8);
- REQUIRE_INDICES(set, 5);
-
- set = {5, 8, 9};
- set.erase_at({8, 9});
- REQUIRE_INDICES(set, 5);
- }
-
- SECTION("shifts indices after the removed one") {
- set = {5, 8};
- set.erase_at(5);
- REQUIRE_INDICES(set, 7);
-
- set = {5, 10, 15, 20};
- set.erase_at({5, 10});
- REQUIRE_INDICES(set, 13, 18);
- }
-
- SECTION("shrinks ranges when used on one of the edges of them") {
- set = {5, 6, 7, 8};
- set.erase_at(8);
- REQUIRE_INDICES(set, 5, 6, 7);
- set.erase_at(5);
- REQUIRE_INDICES(set, 5, 6);
-
- set = {5, 6, 7, 8};
- set.erase_at({5, 8});
- REQUIRE_INDICES(set, 5, 6);
- }
-
- SECTION("shrinks ranges when used in the middle of them") {
- set = {5, 6, 7, 8};
- set.erase_at(7);
- REQUIRE_INDICES(set, 5, 6, 7);
-
- set = {5, 6, 7, 8};
- set.erase_at({6, 7});
- REQUIRE_INDICES(set, 5, 6);
- }
-
- SECTION("merges ranges when the gap between them is deleted") {
- set = {3, 5};
- set.erase_at(4);
- REQUIRE_INDICES(set, 3, 4);
-
- set = {3, 5, 7};
- set.erase_at({4, 6});
- REQUIRE_INDICES(set, 3, 4, 5);
- }
-}
-
-TEST_CASE("index_set: erase_or_unshift()") {
- realm::IndexSet set;
-
- SECTION("removes the given index") {
- set = {1, 2};
- set.erase_or_unshift(2);
- REQUIRE_INDICES(set, 1);
- }
-
- SECTION("shifts indexes after the given index") {
- set = {1, 5};
- set.erase_or_unshift(2);
- REQUIRE_INDICES(set, 1, 4);
- }
-
- SECTION("returns npos for indices in the set") {
- set = {1, 3, 5};
- REQUIRE(realm::IndexSet(set).erase_or_unshift(1) == realm::IndexSet::npos);
- REQUIRE(realm::IndexSet(set).erase_or_unshift(3) == realm::IndexSet::npos);
- REQUIRE(realm::IndexSet(set).erase_or_unshift(5) == realm::IndexSet::npos);
- }
-
- SECTION("returns the number of indices in the set before the index for ones not in the set") {
- set = {1, 3, 5, 6};
- REQUIRE(realm::IndexSet(set).erase_or_unshift(0) == 0);
- REQUIRE(realm::IndexSet(set).erase_or_unshift(2) == 1);
- REQUIRE(realm::IndexSet(set).erase_or_unshift(4) == 2);
- REQUIRE(realm::IndexSet(set).erase_or_unshift(7) == 3);
- }
-
-}
-
-TEST_CASE("index_set: remove()") {
- realm::IndexSet set;
-
- SECTION("is a no-op if the set is empty") {
- set.remove(4);
- REQUIRE(set.empty());
-
- set.remove({1, 2, 3});
- REQUIRE(set.empty());
- }
-
- SECTION("is a no-op if the set to remove is empty") {
- set = {5};
- set.remove(realm::IndexSet{});
- REQUIRE_INDICES(set, 5);
- }
-
- SECTION("is a no-op if the index to remove is not in the set") {
- set = {5};
- set.remove(4);
- set.remove(6);
- set.remove({4, 6});
- REQUIRE_INDICES(set, 5);
- }
-
- SECTION("removes one-element ranges") {
- set = {5};
- set.remove(5);
- REQUIRE(set.empty());
-
- set = {5};
- set.remove({3, 4, 5});
- REQUIRE(set.empty());
- }
-
- SECTION("shrinks ranges beginning with the index") {
- set = {5, 6, 7};
- set.remove(5);
- REQUIRE_INDICES(set, 6, 7);
-
- set = {5, 6, 7};
- set.remove({3, 5});
- REQUIRE_INDICES(set, 6, 7);
- }
-
- SECTION("shrinks ranges ending with the index") {
- set = {5, 6, 7};
- set.remove(7);
- REQUIRE_INDICES(set, 5, 6);
-
- set = {5, 6, 7};
- set.remove({3, 7});
- REQUIRE_INDICES(set, 5, 6);
- }
-
- SECTION("splits ranges containing the index") {
- set = {5, 6, 7};
- set.remove(6);
- REQUIRE_INDICES(set, 5, 7);
-
- set = {5, 6, 7};
- set.remove({3, 6});
- REQUIRE_INDICES(set, 5, 7);
- }
-
- SECTION("does not shift other indices and uses unshifted positions") {
- set = {5, 6, 7, 10, 11, 12, 13, 15};
- set.remove({6, 11, 13});
- REQUIRE_INDICES(set, 5, 7, 10, 12, 15);
- }
-}
-
-TEST_CASE("index_set: shift()") {
- realm::IndexSet set;
-
- SECTION("is ind + count(0, ind), but adds the count-so-far to the stop index") {
- set = {1, 3, 5, 6};
- REQUIRE(set.shift(0) == 0);
- REQUIRE(set.shift(1) == 2);
- REQUIRE(set.shift(2) == 4);
- REQUIRE(set.shift(3) == 7);
- REQUIRE(set.shift(4) == 8);
- }
-}
-
-TEST_CASE("index_set: unshift()") {
- realm::IndexSet set;
-
- SECTION("is index - count(0, index)") {
- set = {1, 3, 5, 6};
- REQUIRE(set.unshift(0) == 0);
- REQUIRE(set.unshift(2) == 1);
- REQUIRE(set.unshift(4) == 2);
- REQUIRE(set.unshift(7) == 3);
- REQUIRE(set.unshift(8) == 4);
- }
-}
-
-TEST_CASE("index_set: clear()") {
- realm::IndexSet set;
-
- SECTION("removes all indices from the set") {
- set = {1, 2, 3};
- set.clear();
- REQUIRE(set.empty());
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp
deleted file mode 100644
index 54f7741df..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp
+++ /dev/null
@@ -1,912 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "catch2/catch.hpp"
-
-#include "util/test_file.hpp"
-#include "util/index_helpers.hpp"
-
-#include "binding_context.hpp"
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "impl/object_accessor_impl.hpp"
-
-#include <realm/version.hpp>
-#include <realm/db.hpp>
-
-#include <cstdint>
-
-using namespace realm;
-
-TEST_CASE("list") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"origin", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"array", PropertyType::Array|PropertyType::Object, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }},
- {"other_origin", {
- {"array", PropertyType::Array|PropertyType::Object, "other_target"}
- }},
- {"other_target", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto& coordinator = *_impl::RealmCoordinator::get_coordinator(config.path);
-
- auto origin = r->read_group().get_table("class_origin");
- auto target = r->read_group().get_table("class_target");
- auto other_origin = r->read_group().get_table("class_other_origin");
- auto other_target = r->read_group().get_table("class_other_target");
- ColKey col_link = origin->get_column_key("array");
- ColKey col_value = target->get_column_key("value");
- ColKey other_col_link = other_origin->get_column_key("array");
- ColKey other_col_value = other_target->get_column_key("value");
-
- r->begin_transaction();
-
- std::vector<ObjKey> target_keys;
- target->create_objects(10, target_keys);
- for (int i = 0; i < 10; ++i)
- target->get_object(target_keys[i]).set_all(i);
-
- Obj obj = origin->create_object();
- auto lv = obj.get_linklist_ptr(col_link);
- for (int i = 0; i < 10; ++i)
- lv->add(target_keys[i]);
- auto lv2 = origin->create_object().get_linklist_ptr(col_link);
- for (int i = 0; i < 10; ++i)
- lv2->add(target_keys[i]);
-
- ObjKeys other_target_keys({3, 5, 7, 9, 11, 13, 15, 17, 19, 21});
- other_target->create_objects(other_target_keys);
- for (int i = 0; i < 10; ++i)
- other_target->get_object(other_target_keys[i]).set_all(i);
-
- Obj other_obj = other_origin->create_object();
- auto other_lv = other_obj.get_linklist_ptr(other_col_link);
- for (int i = 0; i < 10; ++i)
- other_lv->add(other_target_keys[i]);
-
- r->commit_transaction();
-
- auto r2 = coordinator.get_realm();
- auto r2_lv = r2->read_group().get_table("class_origin")->get_object(0).get_linklist_ptr(col_link);
-
- SECTION("add_notification_block()") {
- CollectionChangeSet change;
- List lst(r, obj, col_link);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- advance_and_notify(*r);
- };
-
- auto require_change = [&] {
- auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
- advance_and_notify(*r);
- return token;
- };
-
- auto require_no_change = [&] {
- bool first = true;
- auto token = lst.add_notification_callback([&, first](CollectionChangeSet, std::exception_ptr) mutable {
- REQUIRE(first);
- first = false;
- });
- advance_and_notify(*r);
- return token;
- };
-
- SECTION("modifying the list sends a change notifications") {
- auto token = require_change();
- write([&] { if (lv2->size() > 5) lst.remove(5); });
- REQUIRE_INDICES(change.deletions, 5);
- }
-
- SECTION("modifying a different list doesn't send a change notification") {
- auto token = require_no_change();
- write([&] { if (lv2->size() > 5) lv2->remove(5); });
- }
-
- SECTION("deleting the list sends a change notification") {
- auto token = require_change();
- write([&] { obj.remove(); });
- REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
-
- // Should not resend delete all notification after another commit
- change = {};
- write([&] { target->create_object(); });
- REQUIRE(change.empty());
- }
-
- SECTION("deleting list before first run of notifier reports deletions") {
- auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
- advance_and_notify(*r);
- write([&] { origin->begin()->remove(); });
- REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("modifying one of the target rows sends a change notification") {
- auto token = require_change();
- write([&] { lst.get(5).set(col_value, 6); });
- REQUIRE_INDICES(change.modifications, 5);
- }
-
- SECTION("deleting a target row sends a change notification") {
- auto token = require_change();
- write([&] { target->remove_object(target_keys[5]); });
- REQUIRE_INDICES(change.deletions, 5);
- }
-
- SECTION("adding a row and then modifying the target row does not mark the row as modified") {
- auto token = require_change();
- write([&] {
- Obj obj = target->get_object(target_keys[5]);
- lst.add(obj);
- obj.set(col_value, 10);
- });
- REQUIRE_INDICES(change.insertions, 10);
- REQUIRE_INDICES(change.modifications, 5);
- }
-
- SECTION("modifying and then moving a row reports move/insert but not modification") {
- auto token = require_change();
- write([&] {
- target->get_object(target_keys[5]).set(col_value, 10);
- lst.move(5, 8);
- });
- REQUIRE_INDICES(change.insertions, 8);
- REQUIRE_INDICES(change.deletions, 5);
- REQUIRE_MOVES(change, {5, 8});
- REQUIRE(change.modifications.empty());
- }
-
- SECTION("modifying a row which appears multiple times in a list marks them all as modified") {
- r->begin_transaction();
- lst.add(target_keys[5]);
- r->commit_transaction();
-
- auto token = require_change();
- write([&] { target->get_object(target_keys[5]).set(col_value, 10); });
- REQUIRE_INDICES(change.modifications, 5, 10);
- }
-
- SECTION("deleting a row which appears multiple times in a list marks them all as modified") {
- r->begin_transaction();
- lst.add(target_keys[5]);
- r->commit_transaction();
-
- auto token = require_change();
- write([&] { target->remove_object(target_keys[5]); });
- REQUIRE_INDICES(change.deletions, 5, 10);
- }
-
- SECTION("clearing the target table sends a change notification") {
- auto token = require_change();
- write([&] { target->clear(); });
- REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("moving a target row does not send a change notification") {
- // Remove a row from the LV so that we have one to delete that's not in the list
- r->begin_transaction();
- if (lv->size() > 2)
- lv->remove(2);
- r->commit_transaction();
-
- auto token = require_no_change();
- write([&] { target->remove_object(target_keys[2]); });
- }
-
- SECTION("multiple LinkViews for the same LinkList can get notifications") {
- r->begin_transaction();
- target->clear();
- std::vector<ObjKey> keys;
- target->create_objects(5, keys);
- r->commit_transaction();
-
- auto get_list = [&] {
- auto r = Realm::get_shared_realm(config);
- auto obj = r->read_group().get_table("class_origin")->get_object(0);
- return List(r, obj, col_link);
- };
- auto change_list = [&] {
- r->begin_transaction();
- if (lv->size()) {
- target->get_object(lv->size() - 1).set(col_value, int64_t(lv->size()));
- }
- lv->add(keys[lv->size()]);
- r->commit_transaction();
- };
-
- List lists[3];
- NotificationToken tokens[3];
- CollectionChangeSet changes[3];
-
- for (int i = 0; i < 3; ++i) {
- lists[i] = get_list();
- tokens[i] = lists[i].add_notification_callback([i, &changes](CollectionChangeSet c, std::exception_ptr) {
- changes[i] = std::move(c);
- });
- change_list();
- }
-
- // Each of the Lists now has a different source version and state at
- // that version, so they should all see different changes despite
- // being for the same LinkList
- for (auto& list : lists)
- advance_and_notify(*list.get_realm());
-
- REQUIRE_INDICES(changes[0].insertions, 0, 1, 2);
- REQUIRE(changes[0].modifications.empty());
-
- REQUIRE_INDICES(changes[1].insertions, 1, 2);
- REQUIRE_INDICES(changes[1].modifications, 0);
-
- REQUIRE_INDICES(changes[2].insertions, 2);
- REQUIRE_INDICES(changes[2].modifications, 1);
-
- // After making another change, they should all get the same notification
- change_list();
- for (auto& list : lists)
- advance_and_notify(*list.get_realm());
-
- for (int i = 0; i < 3; ++i) {
- REQUIRE_INDICES(changes[i].insertions, 3);
- REQUIRE_INDICES(changes[i].modifications, 2);
- }
- }
-
- SECTION("multiple callbacks for the same Lists can be skipped individually") {
- auto token = require_no_change();
- auto token2 = require_change();
-
- r->begin_transaction();
- lv->add(target_keys[0]);
- token.suppress_next();
- r->commit_transaction();
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.insertions, 10);
- }
-
- SECTION("multiple Lists for the same LinkView can be skipped individually") {
- auto token = require_no_change();
-
- List list2(r, obj, col_link);
- auto token2 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
- advance_and_notify(*r);
-
- r->begin_transaction();
- lv->add(target_keys[0]);
- token.suppress_next();
- r->commit_transaction();
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.insertions, 10);
- }
-
- SECTION("skipping only effects the current transaction even if no notification would occur anyway") {
- auto token = require_change();
-
- // would not produce a notification even if it wasn't skipped because no changes were made
- r->begin_transaction();
- token.suppress_next();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(change.empty());
-
- // should now produce a notification
- r->begin_transaction();
- lv->add(target_keys[0]);
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE_INDICES(change.insertions, 10);
- }
-
- SECTION("modifying a different table does not send a change notification") {
- auto token = require_no_change();
- write([&] { other_lv->add(other_target_keys[0]); });
- }
-
- SECTION("changes are reported correctly for multiple tables") {
- List list2(r, *other_lv);
- CollectionChangeSet other_changes;
- auto token1 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- other_changes = std::move(c);
- });
- auto token2 = require_change();
-
- write([&] {
- lv->add(target_keys[1]);
-
- other_origin->create_object();
- if (other_lv->size() > 0)
- other_lv->insert(1, other_target_keys[0]);
-
- lv->add(target_keys[2]);
- });
- REQUIRE_INDICES(change.insertions, 10, 11);
- REQUIRE_INDICES(other_changes.insertions, 1);
-
- write([&] {
- lv->add(target_keys[3]);
- other_obj.remove();
- lv->add(target_keys[4]);
- });
- REQUIRE_INDICES(change.insertions, 12, 13);
- REQUIRE_INDICES(other_changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
-
- write([&] {
- lv->add(target_keys[5]);
- other_origin->clear();
- lv->add(target_keys[6]);
- });
- REQUIRE_INDICES(change.insertions, 14, 15);
- }
-
- SECTION("tables-of-interest are tracked properly for multiple source versions") {
- // Add notifiers for different tables at different versions to verify
- // that the tables of interest are updated correctly as we process
- // new notifiers
- CollectionChangeSet changes1, changes2;
- auto token1 = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- changes1 = std::move(c);
- });
-
- r2->begin_transaction();
- r2->read_group().get_table("class_target")->get_object(target_keys[0]).set(col_value, 10);
- r2->read_group().get_table("class_other_target")->get_object(other_target_keys[1]).set(other_col_value, 10);
- r2->commit_transaction();
-
- List list2(r2, r2->read_group().get_table("class_other_origin")->get_object(0), other_col_link);
- auto token2 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- changes2 = std::move(c);
- });
-
- auto r3 = coordinator.get_realm();
- r3->begin_transaction();
- r3->read_group().get_table("class_target")->get_object(target_keys[2]).set(col_value, 10);
- r3->read_group().get_table("class_other_target")->get_object(other_target_keys[3]).set(other_col_value, 10);
- r3->commit_transaction();
-
- advance_and_notify(*r);
- advance_and_notify(*r2);
-
- REQUIRE_INDICES(changes1.modifications, 0, 2);
- REQUIRE_INDICES(changes2.modifications, 3);
- }
-
- SECTION("modifications are reported for rows that are moved and then moved back in a second transaction") {
- auto token = require_change();
-
- r2->begin_transaction();
- r2_lv->get_object(5).set(col_value, 10);
- r2_lv->get_object(1).set(col_value, 10);
- r2_lv->move(5, 8);
- r2_lv->move(1, 2);
- r2->commit_transaction();
-
- coordinator.on_change();
-
- r2->begin_transaction();
- if (r2_lv->size() > 8)
- r2_lv->move(8, 5);
- r2->commit_transaction();
- advance_and_notify(*r);
-
- REQUIRE_INDICES(change.deletions, 1);
- REQUIRE_INDICES(change.insertions, 2);
- REQUIRE_INDICES(change.modifications, 5);
- REQUIRE_MOVES(change, {1, 2});
- }
-
- SECTION("changes are sent in initial notification") {
- auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
- r2->begin_transaction();
- r2_lv->remove(5);
- r2->commit_transaction();
- advance_and_notify(*r);
- REQUIRE_INDICES(change.deletions, 5);
- }
-
- SECTION("changes are sent in initial notification after removing and then re-adding callback") {
- auto token = lst.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- REQUIRE(false);
- });
- token = {};
-
- auto write = [&] {
- r2->begin_transaction();
- r2_lv->remove(5);
- r2->commit_transaction();
- };
-
- SECTION("add new callback before transaction") {
- token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
-
- write();
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.deletions, 5);
- }
-
- SECTION("add new callback after transaction") {
- write();
-
- token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.deletions, 5);
- }
-
- SECTION("add new callback after transaction and after changeset was calculated") {
- write();
- coordinator.on_change();
-
- token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.deletions, 5);
- }
- }
- }
-
- SECTION("sorted add_notification_block()") {
- List lst(r, *lv);
- Results results = lst.sort({{{col_value}}, {false}});
-
- int notification_calls = 0;
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- ++notification_calls;
- });
-
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- advance_and_notify(*r);
- };
- SECTION("add duplicates") {
- write([&] {
- lst.add(target_keys[5]);
- lst.add(target_keys[5]);
- lst.add(target_keys[5]);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 5, 6, 7);
- }
-
- SECTION("change order by modifying target") {
- write([&] {
- lst.get(5).set(col_value, 15);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 4);
- REQUIRE_INDICES(change.insertions, 0);
- }
-
- SECTION("swap") {
- write([&] {
- lst.swap(1, 2);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("move") {
- write([&] {
- lst.move(5, 3);
- });
- REQUIRE(notification_calls == 1);
- }
- }
-
- SECTION("filtered add_notification_block()") {
- List lst(r, *lv);
- Results results = lst.filter(target->where().less(col_value, 9));
-
- int notification_calls = 0;
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- ++notification_calls;
- });
-
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- advance_and_notify(*r);
- };
- SECTION("add duplicates") {
- write([&] {
- lst.add(target_keys[5]);
- lst.add(target_keys[5]);
- lst.add(target_keys[5]);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 9, 10, 11);
- }
-
- SECTION("swap") {
- write([&] {
- lst.swap(1, 2);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 2);
- REQUIRE_INDICES(change.insertions, 1);
-
- write([&] {
- lst.swap(5, 8);
- });
- REQUIRE(notification_calls == 3);
- REQUIRE_INDICES(change.deletions, 5, 8);
- REQUIRE_INDICES(change.insertions, 5, 8);
- }
-
- SECTION("move") {
- write([&] {
- lst.move(5, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 5);
- REQUIRE_INDICES(change.insertions, 3);
- }
-
- SECTION("move non-matching entry") {
- write([&] {
- lst.move(9, 3);
- });
- REQUIRE(notification_calls == 1);
- }
- }
-
- SECTION("sort()") {
- auto objectschema = &*r->schema().find("target");
- List list(r, *lv);
- auto results = list.sort({{{col_value}}, {false}});
-
- REQUIRE(&results.get_object_schema() == objectschema);
- REQUIRE(results.get_mode() == Results::Mode::LinkList);
- REQUIRE(results.size() == 10);
-
- // Aggregates don't inherently have to convert to TableView, but do
- // because aggregates aren't implemented for LinkView
- REQUIRE(results.sum(col_value) == 45);
- REQUIRE(results.get_mode() == Results::Mode::TableView);
-
- // Reset to LinkView mode to test implicit conversion to TableView on get()
- results = list.sort({{{col_value}}, {false}});
- for (size_t i = 0; i < 10; ++i)
- REQUIRE(results.get(i).get_key() == target_keys[9 - i]);
- REQUIRE_THROWS_WITH(results.get(10), "Requested index 10 greater than max 9");
- REQUIRE(results.get_mode() == Results::Mode::TableView);
-
- // Zero sort columns should leave it in LinkView mode
- results = list.sort(SortDescriptor());
- for (size_t i = 0; i < 10; ++i)
- REQUIRE(results.get(i).get_key() == target_keys[i]);
- REQUIRE_THROWS_WITH(results.get(10), "Requested index 10 greater than max 9");
- REQUIRE(results.get_mode() == Results::Mode::LinkList);
- }
-
- SECTION("filter()") {
- auto objectschema = &*r->schema().find("target");
- List list(r, *lv);
- auto results = list.filter(target->where().greater(col_value, 5));
-
- REQUIRE(&results.get_object_schema() == objectschema);
- REQUIRE(results.get_mode() == Results::Mode::Query);
- REQUIRE(results.size() == 4);
-
- for (size_t i = 0; i < 4; ++i) {
- REQUIRE(results.get(i).get_key().value == i + 6);
- }
- }
-
- SECTION("snapshot()") {
- auto objectschema = &*r->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_valid());
- }
- for (size_t i = 0; i < 5; ++i) {
- target->remove_object(target_keys[i]);
- }
- REQUIRE(snapshot.size() == 10);
- for (size_t i = 0; i < 5; ++i) {
- REQUIRE(!snapshot.get(i).is_valid());
- }
- for (size_t i = 5; i < 10; ++i) {
- REQUIRE(snapshot.get(i).is_valid());
- }
- list.add(target_keys[5]);
- REQUIRE(snapshot.size() == 10);
- }
-
- SECTION("get_object_schema()") {
- List list(r, *lv);
- auto objectschema = &*r->schema().find("target");
- REQUIRE(&list.get_object_schema() == objectschema);
- }
-
- SECTION("delete_at()") {
- List list(r, *lv);
- r->begin_transaction();
- auto initial_view_size = lv->size();
- auto initial_target_size = target->size();
- list.delete_at(1);
- REQUIRE(lv->size() == initial_view_size - 1);
- REQUIRE(target->size() == initial_target_size - 1);
- r->cancel_transaction();
- }
-
- SECTION("delete_all()") {
- List list(r, *lv);
- r->begin_transaction();
- list.delete_all();
- REQUIRE(lv->size() == 0);
- REQUIRE(target->size() == 0);
- r->cancel_transaction();
- }
-
- SECTION("as_results().clear()") {
- List list(r, *lv);
- r->begin_transaction();
- list.as_results().clear();
- REQUIRE(lv->size() == 0);
- REQUIRE(target->size() == 0);
- r->cancel_transaction();
- }
-
- SECTION("snapshot().clear()") {
- List list(r, *lv);
- r->begin_transaction();
- auto snapshot = list.snapshot();
- snapshot.clear();
- REQUIRE(snapshot.size() == 10);
- REQUIRE(list.size() == 0);
- REQUIRE(lv->size() == 0);
- REQUIRE(target->size() == 0);
- r->cancel_transaction();
- }
-
- SECTION("add(RowExpr)") {
- List list(r, *lv);
- r->begin_transaction();
- SECTION("adds rows from the correct table") {
- list.add(target_keys[5]);
- REQUIRE(list.size() == 11);
- REQUIRE(list.get(10).get_key() == target_keys[5]);
- }
-
- SECTION("throws for rows from the wrong table") {
- REQUIRE_THROWS(list.add(obj));
- }
- r->cancel_transaction();
- }
-
- SECTION("insert(RowExpr)") {
- List list(r, *lv);
- r->begin_transaction();
-
- SECTION("insert rows from the correct table") {
- list.insert(0, target_keys[5]);
- REQUIRE(list.size() == 11);
- REQUIRE(list.get(0).get_key() == target_keys[5]);
- }
-
- SECTION("throws for rows from the wrong table") {
- REQUIRE_THROWS(list.insert(0, obj));
- }
-
- SECTION("throws for out of bounds insertions") {
- REQUIRE_THROWS(list.insert(11, target_keys[5]));
- REQUIRE_NOTHROW(list.insert(10, target_keys[5]));
- }
- r->cancel_transaction();
- }
-
- SECTION("set(RowExpr)") {
- List list(r, *lv);
- r->begin_transaction();
-
- SECTION("assigns for rows from the correct table") {
- list.set(0, target_keys[5]);
- REQUIRE(list.size() == 10);
- REQUIRE(list.get(0).get_key() == target_keys[5]);
- }
-
- SECTION("throws for rows from the wrong table") {
- REQUIRE_THROWS(list.set(0, obj));
- }
-
- SECTION("throws for out of bounds sets") {
- REQUIRE_THROWS(list.set(10, target_keys[5]));
- }
- r->cancel_transaction();
- }
-
- SECTION("find(RowExpr)") {
- List list(r, *lv);
- Obj obj1 = target->get_object(target_keys[1]);
- Obj obj5 = target->get_object(target_keys[5]);
-
- SECTION("returns index in list for values in the list") {
- REQUIRE(list.find(obj5) == 5);
- }
-
- SECTION("returns index in list and not index in table") {
- r->begin_transaction();
- list.remove(1);
- REQUIRE(list.find(obj5) == 4);
- REQUIRE(list.as_results().index_of(obj5) == 4);
- r->cancel_transaction();
- }
-
- SECTION("returns npos for values not in the list") {
- r->begin_transaction();
- list.remove(1);
- REQUIRE(list.find(obj1) == npos);
- REQUIRE(list.as_results().index_of(obj1) == npos);
- r->cancel_transaction();
- }
-
- SECTION("throws for row in wrong table") {
- REQUIRE_THROWS(list.find(obj));
- REQUIRE_THROWS(list.as_results().index_of(obj));
- }
- }
-
- SECTION("find(Query)") {
- List list(r, *lv);
-
- SECTION("returns index in list for values in the list") {
- REQUIRE(list.find(std::move(target->where().equal(col_value, 5))) == 5);
- }
-
- SECTION("returns index in list and not index in table") {
- r->begin_transaction();
- list.remove(1);
- REQUIRE(list.find(std::move(target->where().equal(col_value, 5))) == 4);
- r->cancel_transaction();
- }
-
- SECTION("returns npos for values not in the list") {
- REQUIRE(list.find(std::move(target->where().equal(col_value, 11))) == npos);
- }
- }
-
- SECTION("add(Context)") {
- List list(r, *lv);
- CppContext ctx(r, &list.get_object_schema());
- r->begin_transaction();
-
- SECTION("adds boxed RowExpr") {
- list.add(ctx, util::Any(target->get_object(target_keys[5])));
- REQUIRE(list.size() == 11);
- REQUIRE(list.get(10).get_key().value == 5);
- }
-
- SECTION("adds boxed realm::Object") {
- realm::Object obj(r, list.get_object_schema(), target->get_object(target_keys[5]));
- list.add(ctx, util::Any(obj));
- REQUIRE(list.size() == 11);
- REQUIRE(list.get(10).get_key() == target_keys[5]);
- }
-
- SECTION("creates new object for dictionary") {
- list.add(ctx, util::Any(AnyDict{{"value", INT64_C(20)}}));
- REQUIRE(list.size() == 11);
- REQUIRE(target->size() == 11);
- REQUIRE(list.get(10).get<Int>(col_value) == 20);
- }
-
- SECTION("throws for object in wrong table") {
- REQUIRE_THROWS(list.add(ctx, util::Any(origin->get_object(0))));
- realm::Object object(r, *r->schema().find("origin"), origin->get_object(0));
- REQUIRE_THROWS(list.add(ctx, util::Any(object)));
- }
-
- r->cancel_transaction();
- }
-
- SECTION("find(Context)") {
- List list(r, *lv);
- CppContext ctx(r, &list.get_object_schema());
-
- SECTION("returns index in list for boxed RowExpr") {
- REQUIRE(list.find(ctx, util::Any(target->get_object(target_keys[5]))) == 5);
- }
-
- SECTION("returns index in list for boxed Object") {
- realm::Object obj(r, *r->schema().find("origin"), target->get_object(target_keys[5]));
- REQUIRE(list.find(ctx, util::Any(obj)) == 5);
- }
-
- SECTION("does not insert new objects for dictionaries") {
- REQUIRE(list.find(ctx, util::Any(AnyDict{{"value", INT64_C(20)}})) == npos);
- REQUIRE(target->size() == 10);
- }
-
- SECTION("throws for object in wrong table") {
- REQUIRE_THROWS(list.find(ctx, util::Any(obj)));
- }
- }
-
- SECTION("get(Context)") {
- List list(r, *lv);
- CppContext ctx(r, &list.get_object_schema());
-
- Object obj;
- REQUIRE_NOTHROW(obj = any_cast<Object&&>(list.get(ctx, 1)));
- REQUIRE(obj.is_valid());
- REQUIRE(obj.obj().get_key() == target_keys[1]);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp
deleted file mode 100644
index fbaf79d28..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define CATCH_CONFIG_RUNNER
-#include "catch2/catch.hpp"
-
-#include <limits.h>
-
-#ifdef _MSC_VER
-#include <Shlwapi.h>
-#pragma comment(lib, "Shlwapi.lib")
-#else
-#include <libgen.h>
-#endif
-
-int main(int argc, char** argv) {
-#ifdef _MSC_VER
- char path[MAX_PATH];
- if (GetModuleFileNameA(NULL, path, sizeof(path)) == 0) {
- fprintf(stderr, "Failed to retrieve path to exectuable.\n");
- return 1;
- }
- PathRemoveFileSpecA(path);
- SetCurrentDirectoryA(path);
-#else
- char executable[PATH_MAX];
- if (realpath(argv[0], executable) == NULL) {
- fprintf(stderr, "Failed to resolve path to exectuable.\n");
- return 1;
- }
- const char* directory = dirname(executable);
- chdir(directory);
-#endif
-
- int result = Catch::Session().run(argc, argv);
- return result < 0xff ? result : 0xff;
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp
deleted file mode 100644
index 5f4ca60b5..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp
+++ /dev/null
@@ -1,2059 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/test_file.hpp"
-
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-
-#include "impl/object_accessor_impl.hpp"
-
-#include <realm/group.hpp>
-#include <realm/table.hpp>
-#include <realm/util/scope_exit.hpp>
-
-#ifdef _WIN32
-#include <Windows.h>
-#endif
-
-using namespace realm;
-
-#define VERIFY_SCHEMA(r, m) verify_schema((r), __LINE__, m)
-
-#define REQUIRE_UPDATE_SUCCEEDS(r, s, version) do { \
- REQUIRE_NOTHROW((r).update_schema(s, version)); \
- VERIFY_SCHEMA(r, false); \
- REQUIRE((r).schema() == s); \
-} while (0)
-
-#define REQUIRE_NO_MIGRATION_NEEDED(r, schema1, schema2) do { \
- REQUIRE_UPDATE_SUCCEEDS(r, schema1, 0); \
- REQUIRE_UPDATE_SUCCEEDS(r, schema2, 0); \
-} while (0)
-
-#define REQUIRE_MIGRATION_NEEDED(r, schema1, schema2) do { \
- REQUIRE_UPDATE_SUCCEEDS(r, schema1, 0); \
- REQUIRE_THROWS((r).update_schema(schema2)); \
- REQUIRE((r).schema() == schema1); \
- REQUIRE_UPDATE_SUCCEEDS(r, schema2, 1); \
-} while (0)
-
-namespace {
-void verify_schema(Realm& r, int line, bool in_migration)
-{
- CAPTURE(line);
- for (auto&& object_schema : r.schema()) {
- auto table = r.read_group().get_table(object_schema.table_key);
- REQUIRE(table);
- REQUIRE(std::string(table->get_name()) == ObjectStore::table_name_for_object_type(object_schema.name));
- CAPTURE(object_schema.name);
- std::string primary_key;
- if (!in_migration) {
- primary_key = ObjectStore::get_primary_key_for_object(r.read_group(), object_schema.name);
- REQUIRE(primary_key == object_schema.primary_key);
- }
- else {
- primary_key = object_schema.primary_key;
- }
- for (auto&& prop : object_schema.persisted_properties) {
- auto col = table->get_column_key(prop.name);
- CAPTURE(prop.name);
- REQUIRE(col);
- REQUIRE(col == prop.column_key);
- REQUIRE(to_underlying(ObjectSchema::from_core_type(*table, col)) ==
- to_underlying(prop.type));
- REQUIRE(table->has_search_index(col) == prop.requires_index());
- REQUIRE(bool(prop.is_primary) == (prop.name == primary_key));
- }
- }
-}
-
-TableRef get_table(std::shared_ptr<Realm> const& realm, StringData object_type)
-{
- return ObjectStore::table_for_object_type(realm->read_group(), object_type);
-}
-
-// Helper functions for modifying Schema objects, mostly for the sake of making
-// it clear what exactly is different about the 2+ schema objects used in
-// various tests
-Schema add_table(Schema const& schema, ObjectSchema object_schema)
-{
- std::vector<ObjectSchema> new_schema(schema.begin(), schema.end());
- new_schema.push_back(std::move(object_schema));
- return new_schema;
-}
-
-Schema remove_table(Schema const& schema, StringData object_name)
-{
- std::vector<ObjectSchema> new_schema;
- std::remove_copy_if(schema.begin(), schema.end(), std::back_inserter(new_schema),
- [&](auto&& object_schema) { return object_schema.name == object_name; });
- return new_schema;
-}
-
-Schema add_property(Schema schema, StringData object_name, Property property)
-{
- schema.find(object_name)->persisted_properties.push_back(std::move(property));
- return schema;
-}
-
-Schema remove_property(Schema schema, StringData object_name, StringData property_name)
-{
- auto& properties = schema.find(object_name)->persisted_properties;
- properties.erase(find_if(begin(properties), end(properties),
- [&](auto&& prop) { return prop.name == property_name; }));
- return schema;
-}
-
-Schema set_indexed(Schema schema, StringData object_name, StringData property_name, bool value)
-{
- schema.find(object_name)->property_for_name(property_name)->is_indexed = value;
- return schema;
-}
-
-Schema set_optional(Schema schema, StringData object_name, StringData property_name, bool value)
-{
- auto& prop = *schema.find(object_name)->property_for_name(property_name);
- if (value)
- prop.type |= PropertyType::Nullable;
- else
- prop.type &= ~PropertyType::Nullable;
- return schema;
-}
-
-Schema set_type(Schema schema, StringData object_name, StringData property_name, PropertyType value)
-{
- schema.find(object_name)->property_for_name(property_name)->type = value;
- return schema;
-}
-
-Schema set_target(Schema schema, StringData object_name, StringData property_name, StringData new_target)
-{
- schema.find(object_name)->property_for_name(property_name)->object_type = new_target;
- return schema;
-}
-
-Schema set_primary_key(Schema schema, StringData object_name, StringData new_primary_property)
-{
- auto& object_schema = *schema.find(object_name);
- if (auto old_primary = object_schema.primary_key_property()) {
- old_primary->is_primary = false;
- }
- if (new_primary_property.size()) {
- object_schema.property_for_name(new_primary_property)->is_primary = true;
- }
- object_schema.primary_key = new_primary_property;
- return schema;
-}
-auto create_objects(Table& table, size_t count) {
- std::vector<ObjKey> keys;
- table.create_objects(count, keys);
- return keys;
-};
-} // anonymous namespace
-
-TEST_CASE("migration: Automatic") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- SECTION("no migration required") {
- SECTION("add object schema") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema1 = {};
- Schema schema2 = add_table(schema1, {"object", {
- {"value", PropertyType::Int}
- }});
- Schema schema3 = add_table(schema2, {"object2", {
- {"value", PropertyType::Int}
- }});
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema1, 0);
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema2, 0);
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema3, 0);
- }
-
- SECTION("remove object schema") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int}
- }},
- {"object2", {
- {"value", PropertyType::Int}
- }},
- };
- Schema schema2 = remove_table(schema1, "object2");
- Schema schema3 = remove_table(schema2, "object");
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema3, 0);
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema2, 0);
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema1, 0);
- }
-
- SECTION("add index") {
- auto realm = Realm::get_shared_realm(config);
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
- REQUIRE_NO_MIGRATION_NEEDED(*realm, schema, set_indexed(schema, "object", "value", true));
- }
-
- SECTION("remove index") {
- auto realm = Realm::get_shared_realm(config);
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}}
- }},
- };
- REQUIRE_NO_MIGRATION_NEEDED(*realm, schema, set_indexed(schema, "object", "value", false));
- }
-
- SECTION("reordering properties") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema1 = {
- {"object", {
- {"col1", PropertyType::Int},
- {"col2", PropertyType::Int},
- }},
- };
- Schema schema2 = {
- {"object", {
- {"col2", PropertyType::Int},
- {"col1", PropertyType::Int},
- }},
- };
- REQUIRE_NO_MIGRATION_NEEDED(*realm, schema1, schema2);
- }
- }
-
- SECTION("migration required") {
- SECTION("add property to existing object schema") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema1 = {
- {"object", {
- {"col1", PropertyType::Int},
- }},
- };
- auto schema2 = add_property(schema1, "object",
- {"col2", PropertyType::Int});
- REQUIRE_MIGRATION_NEEDED(*realm, schema1, schema2);
- }
-
- SECTION("remove property from existing object schema") {
- auto realm = Realm::get_shared_realm(config);
- Schema schema = {
- {"object", {
- {"col1", PropertyType::Int},
- {"col2", PropertyType::Int},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, remove_property(schema, "object", "col2"));
- }
-
- SECTION("migratation which replaces a persisted property with a computed one") {
- auto realm = Realm::get_shared_realm(config);
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- {"link", PropertyType::Object|PropertyType::Nullable, "object2"},
- }},
- {"object2", {
- {"value", PropertyType::Int},
- {"inverse", PropertyType::Object|PropertyType::Nullable, "object"},
- }},
- };
- Schema schema2 = remove_property(schema1, "object", "link");
- Property new_property{"link", PropertyType::LinkingObjects|PropertyType::Array, "object2", "inverse"};
- schema2.find("object")->computed_properties.emplace_back(new_property);
-
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema1, 0);
- REQUIRE_THROWS((*realm).update_schema(schema2));
- REQUIRE((*realm).schema() == schema1);
- REQUIRE_NOTHROW((*realm).update_schema(schema2, 1,
- [](SharedRealm, SharedRealm, Schema&) { /* empty but present migration handler */ }));
- VERIFY_SCHEMA(*realm, false);
- REQUIRE((*realm).schema() == schema2);
- }
-
- SECTION("change property type") {
- auto realm = Realm::get_shared_realm(config);
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_type(schema, "object", "value", PropertyType::Float));
- }
-
- SECTION("make property nullable") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_optional(schema, "object", "value", true));
- }
-
- SECTION("make property required") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int|PropertyType::Nullable},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_optional(schema, "object", "value", false));
- }
-
- SECTION("change link target") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"target 1", {
- {"value", PropertyType::Int},
- }},
- {"target 2", {
- {"value", PropertyType::Int},
- }},
- {"origin", {
- {"value", PropertyType::Object|PropertyType::Nullable, "target 1"},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_target(schema, "origin", "value", "target 2"));
- }
-
- SECTION("add pk") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_primary_key(schema, "object", "value"));
- }
-
- SECTION("remove pk") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{true}},
- }},
- };
- REQUIRE_MIGRATION_NEEDED(*realm, schema, set_primary_key(schema, "object", ""));
- }
-
- SECTION("adding column and table in same migration doesn't add duplicate columns") {
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema1 = {
- {"object", {
- {"col1", PropertyType::Int},
- }},
- };
- auto schema2 = add_table(add_property(schema1, "object", {"col2", PropertyType::Int}),
- {"object2", {{"value", PropertyType::Int}}});
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema1, 0);
- REQUIRE_UPDATE_SUCCEEDS(*realm, schema2, 1);
-
- auto& table = *get_table(realm, "object2");
- REQUIRE(table.get_column_count() == 1);
- }
- }
-
- SECTION("migration block invocations") {
- SECTION("not called for initial creation of schema") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 5, [](SharedRealm, SharedRealm, Schema&) { REQUIRE(false); });
- }
-
- SECTION("not called when schema version is unchanged even if there are schema changes") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- Schema schema2 = add_table(schema1, {"second object", {
- {"value", PropertyType::Int},
- }});
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema1, 1);
- realm->update_schema(schema2, 1, [](SharedRealm, SharedRealm, Schema&) { REQUIRE(false); });
- }
-
- SECTION("called when schema version is bumped even if there are no schema changes") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema);
- bool called = false;
- realm->update_schema(schema, 5, [&](SharedRealm, SharedRealm, Schema&) { called = true; });
- REQUIRE(called);
- }
- }
-
- SECTION("migration errors") {
- SECTION("schema version cannot go down") {
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema({}, 1);
- realm->update_schema({}, 2);
- REQUIRE_THROWS(realm->update_schema({}, 0));
- }
-
- SECTION("insert duplicate keys for existing PK during migration") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{true}},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
- REQUIRE_THROWS(realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- create_objects(*table, 2);
- }));
- }
-
- SECTION("add pk to existing table with duplicate keys") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->begin_transaction();
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- create_objects(*table, 2);
- realm->commit_transaction();
-
- schema = set_primary_key(schema, "object", "value");
- REQUIRE_THROWS(realm->update_schema(schema, 2, nullptr));
- }
-
- SECTION("throwing an exception from migration function rolls back all changes") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- Schema schema2 = add_property(schema1, "object",
- {"value2", PropertyType::Int});
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema1, 1);
-
- REQUIRE_THROWS(realm->update_schema(schema2, 2, [](SharedRealm, SharedRealm realm, Schema&) {
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- table->create_object();
- throw 5;
- }));
-
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- REQUIRE(table->size() == 0);
- REQUIRE(realm->schema_version() == 1);
- REQUIRE(realm->schema() == schema1);
- }
- }
-
- SECTION("valid migrations") {
- SECTION("changing all columns does not lose row count") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->begin_transaction();
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- create_objects(*table, 10);
- realm->commit_transaction();
-
- schema = set_type(schema, "object", "value", PropertyType::Float);
- realm->update_schema(schema, 2);
- REQUIRE(table->size() == 10);
- }
-
- SECTION("values for required properties are copied when converitng to nullable") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->begin_transaction();
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto key = table->get_column_key("value");
- create_objects(*table, 10);
- for (int i = 0; i < 10; ++i)
- table->get_object(i).set(key, i);
- realm->commit_transaction();
-
- realm->update_schema(set_optional(schema, "object", "value", true), 2);
- key = table->get_column_key("value");
- for (int i = 0; i < 10; ++i)
- REQUIRE(table->get_object(i).get<util::Optional<int64_t>>(key) == i);
- }
-
- SECTION("values for nullable properties are discarded when converitng to required") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int|PropertyType::Nullable},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->begin_transaction();
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto key = table->get_column_key("value");
- create_objects(*table, 10);
- for (int i = 0; i < 10; ++i)
- table->get_object(i).set(key, i);
- realm->commit_transaction();
-
- realm->update_schema(set_optional(schema, "object", "value", false), 2);
- key = table->get_column_key("value");
- for (size_t i = 0; i < 10; ++i)
- REQUIRE(table->get_object(i).get<int64_t>(key) == 0);
- }
-
- SECTION("deleting table removed from the schema deletes it") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int|PropertyType::Nullable},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->update_schema({}, 2, [](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::delete_data_for_object(realm->read_group(), "object");
- });
- REQUIRE_FALSE(ObjectStore::table_for_object_type(realm->read_group(), "object"));
- }
-
- SECTION("deleting table still in the schema recreates it with no rows") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int|PropertyType::Nullable},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- realm->begin_transaction();
- ObjectStore::table_for_object_type(realm->read_group(), "object")->create_object();
- realm->commit_transaction();
-
- realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::delete_data_for_object(realm->read_group(), "object");
- });
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- REQUIRE(table);
- REQUIRE(table->size() == 0);
- }
-
- SECTION("deleting table which doesn't exist does nothing") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int|PropertyType::Nullable},
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema, 1);
-
- REQUIRE_NOTHROW(realm->update_schema({}, 2, [](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::delete_data_for_object(realm->read_group(), "foo");
- }));
- }
- }
-
- SECTION("schema correctness during migration") {
- InMemoryTestFile config;
- config.schema_mode = SchemaMode::Automatic;
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"optional", PropertyType::Int|PropertyType::Nullable},
- }},
- {"link origin", {
- {"not a pk", PropertyType::Int},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Array|PropertyType::Object, "object"},
- }}
- };
- realm->update_schema(schema);
-
-#define VERIFY_SCHEMA_IN_MIGRATION(target_schema) do { \
- Schema new_schema = (target_schema); \
- realm->update_schema(new_schema, 1, [&](SharedRealm old_realm, SharedRealm new_realm, Schema&) { \
- REQUIRE(old_realm->schema_version() == 0); \
- REQUIRE(old_realm->schema() == schema); \
- REQUIRE(new_realm->schema_version() == 1); \
- REQUIRE(new_realm->schema() == new_schema); \
- VERIFY_SCHEMA(*old_realm, true); \
- VERIFY_SCHEMA(*new_realm, true); \
- }); \
- REQUIRE(realm->schema() == new_schema); \
- VERIFY_SCHEMA(*realm, false); \
-} while (false)
-
- SECTION("add new table") {
- VERIFY_SCHEMA_IN_MIGRATION(add_table(schema, {"new table", {
- {"value", PropertyType::Int},
- }}));
- }
- SECTION("add property to table") {
- VERIFY_SCHEMA_IN_MIGRATION(add_property(schema, "object", {"new", PropertyType::Int}));
- }
- SECTION("remove property from table") {
- VERIFY_SCHEMA_IN_MIGRATION(remove_property(schema, "object", "value"));
- }
- SECTION("remove multiple properties from table") {
- VERIFY_SCHEMA_IN_MIGRATION(remove_property(remove_property(schema, "object", "value"), "object", "optional"));
- }
- SECTION("add primary key to table") {
- VERIFY_SCHEMA_IN_MIGRATION(set_primary_key(schema, "link origin", "not a pk"));
- }
- SECTION("remove primary key from table") {
- VERIFY_SCHEMA_IN_MIGRATION(set_primary_key(schema, "object", ""));
- }
- SECTION("change primary key") {
- VERIFY_SCHEMA_IN_MIGRATION(set_primary_key(schema, "object", "value"));
- }
- SECTION("change property type") {
- VERIFY_SCHEMA_IN_MIGRATION(set_type(schema, "object", "value", PropertyType::Date));
- }
- SECTION("change link target") {
- VERIFY_SCHEMA_IN_MIGRATION(set_target(schema, "link origin", "object", "link origin"));
- }
- SECTION("change linklist target") {
- VERIFY_SCHEMA_IN_MIGRATION(set_target(schema, "link origin", "array", "link origin"));
- }
- SECTION("make property optional") {
- VERIFY_SCHEMA_IN_MIGRATION(set_optional(schema, "object", "value", true));
- }
- SECTION("make property required") {
- VERIFY_SCHEMA_IN_MIGRATION(set_optional(schema, "object", "optional", false));
- }
- SECTION("add index") {
- VERIFY_SCHEMA_IN_MIGRATION(set_indexed(schema, "object", "optional", true));
- }
- SECTION("remove index") {
- VERIFY_SCHEMA_IN_MIGRATION(set_indexed(schema, "object", "value", false));
- }
- SECTION("reorder properties") {
- auto schema2 = schema;
- auto& properties = schema2.find("object")->persisted_properties;
- std::swap(properties[0], properties[1]);
- VERIFY_SCHEMA_IN_MIGRATION(schema2);
- }
- }
-
- SECTION("object accessors inside migrations") {
- using namespace std::string_literals;
-
- Schema schema{
- {"all types", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"bool", PropertyType::Bool},
- {"int", PropertyType::Int},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"data", PropertyType::Data},
- {"date", PropertyType::Date},
- {"object", PropertyType::Object|PropertyType::Nullable, "link target"},
- {"array", PropertyType::Object|PropertyType::Array, "array target"},
- }},
- {"link target", {
- {"value", PropertyType::Int},
- }, {
- {"origin", PropertyType::LinkingObjects|PropertyType::Array, "all types", "object"},
- }},
- {"array target", {
- {"value", PropertyType::Int},
- }},
- {"int pk", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"value", PropertyType::Int},
- }},
- {"string pk", {
- {"pk", PropertyType::String, Property::IsPrimary{true}},
- {"value", PropertyType::Int},
- }},
- };
-
- InMemoryTestFile config;
- config.schema_mode = SchemaMode::Automatic;
- config.schema = schema;
- auto realm = Realm::get_shared_realm(config);
-
- CppContext ctx(realm);
- util::Any values = AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
- {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
- };
- realm->begin_transaction();
- Object::create(ctx, realm, *realm->schema().find("all types"), values);
- realm->commit_transaction();
-
- SECTION("read values from old realm") {
- Schema schema{
- {"all types", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- }},
- };
- realm->update_schema(schema, 2, [](auto old_realm, auto new_realm, Schema&) {
- CppContext ctx(old_realm);
- Object obj = Object::get_for_primary_key(ctx, old_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
-
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(ctx, "bool")) == true);
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "int")) == 5);
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(ctx, "float")) == 2.2f);
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(ctx, "double")) == 3.3);
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "string")) == "hello");
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "data")) == "olleh");
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(ctx, "date")) == Timestamp(10, 20));
-
- auto link = any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object"));
- REQUIRE(link.is_valid());
- REQUIRE(any_cast<int64_t>(link.get_property_value<util::Any>(ctx, "value")) == 10);
-
- auto list = any_cast<List>(obj.get_property_value<util::Any>(ctx, "array"));
- REQUIRE(list.size() == 1);
-
- CppContext list_ctx(ctx, *obj.get_object_schema().property_for_name("array"));
- link = any_cast<Object>(list.get(list_ctx, 0));
- REQUIRE(link.is_valid());
- REQUIRE(any_cast<int64_t>(link.get_property_value<util::Any>(list_ctx, "value")) == 20);
-
- CppContext ctx2(new_realm);
- obj = Object::get_for_primary_key(ctx, new_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
- REQUIRE_THROWS(obj.get_property_value<util::Any>(ctx, "bool"));
- });
- }
-
- SECTION("cannot mutate old realm") {
- realm->update_schema(schema, 2, [](auto old_realm, auto, Schema&) {
- CppContext ctx(old_realm);
- Object obj = Object::get_for_primary_key(ctx, old_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
- REQUIRE_THROWS(obj.set_property_value(ctx, "bool", util::Any(false)));
- REQUIRE_THROWS(old_realm->begin_transaction());
- });
- }
-
- SECTION("cannot read values for removed properties from new realm") {
- Schema schema{
- {"all types", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- }},
- };
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- Object obj = Object::get_for_primary_key(ctx, new_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
- REQUIRE_THROWS(obj.get_property_value<util::Any>(ctx, "bool"));
- REQUIRE_THROWS(obj.get_property_value<util::Any>(ctx, "object"));
- REQUIRE_THROWS(obj.get_property_value<util::Any>(ctx, "array"));
- });
- }
-
- SECTION("read values from new object") {
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- Object obj = Object::get_for_primary_key(ctx, new_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
-
-
- auto link = any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object"));
- REQUIRE(link.is_valid());
- REQUIRE(any_cast<int64_t>(link.get_property_value<util::Any>(ctx, "value")) == 10);
-
- auto list = any_cast<List>(obj.get_property_value<util::Any>(ctx, "array"));
- REQUIRE(list.size() == 1);
-
- CppContext list_ctx(ctx, *obj.get_object_schema().property_for_name("array"));
- link = any_cast<Object>(list.get(list_ctx, 0));
- REQUIRE(link.is_valid());
- REQUIRE(any_cast<int64_t>(link.get_property_value<util::Any>(list_ctx, "value")) == 20);
- });
- }
-
- SECTION("read and write values in new object") {
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- Object obj = Object::get_for_primary_key(ctx, new_realm, "all types",
- util::Any(INT64_C(1)));
- REQUIRE(obj.is_valid());
-
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(ctx, "bool")) == true);
- obj.set_property_value(ctx, "bool", util::Any(false));
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(ctx, "bool")) == false);
-
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "int")) == 5);
- obj.set_property_value(ctx, "int", util::Any(INT64_C(6)));
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "int")) == 6);
-
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(ctx, "float")) == 2.2f);
- obj.set_property_value(ctx, "float", util::Any(1.23f));
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(ctx, "float")) == 1.23f);
-
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(ctx, "double")) == 3.3);
- obj.set_property_value(ctx, "double", util::Any(1.23));
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(ctx, "double")) == 1.23);
-
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "string")) == "hello");
- obj.set_property_value(ctx, "string", util::Any("abc"s));
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "string")) == "abc");
-
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "data")) == "olleh");
- obj.set_property_value(ctx, "data", util::Any("abc"s));
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(ctx, "data")) == "abc");
-
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(ctx, "date")) == Timestamp(10, 20));
- obj.set_property_value(ctx, "date", util::Any(Timestamp(1, 2)));
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(ctx, "date")) == Timestamp(1, 2));
-
- Object linked_obj(new_realm, "link target", 0);
- Object new_obj(new_realm, get_table(new_realm, "link target")->create_object());
-
- auto linking = any_cast<Results>(linked_obj.get_property_value<util::Any>(ctx, "origin"));
- REQUIRE(linking.size() == 1);
-
- REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object")).obj().get_key()
- == linked_obj.obj().get_key());
- obj.set_property_value(ctx, "object", util::Any(new_obj));
- REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object")).obj().get_key()
- == new_obj.obj().get_key());
-
- REQUIRE(linking.size() == 0);
- });
- }
-
- SECTION("create object in new realm") {
- realm->update_schema(schema, 2, [&values](auto, auto new_realm, Schema&) {
- REQUIRE(new_realm->is_in_transaction());
-
- CppContext ctx(new_realm);
- any_cast<AnyDict&>(values)["pk"] = INT64_C(2);
- Object obj = Object::create(ctx, new_realm, "all types", values);
-
- REQUIRE(get_table(new_realm, "all types")->size() == 2);
- REQUIRE(get_table(new_realm, "link target")->size() == 2);
- REQUIRE(get_table(new_realm, "array target")->size() == 2);
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "pk")) == 2);
- });
- }
-
- SECTION("upsert in new realm") {
- realm->update_schema(schema, 2, [&values](auto, auto new_realm, Schema&) {
- REQUIRE(new_realm->is_in_transaction());
- CppContext ctx(new_realm);
- any_cast<AnyDict&>(values)["bool"] = false;
- Object obj = Object::create(ctx, new_realm, "all types", values, CreatePolicy::UpdateAll);
- REQUIRE(get_table(new_realm, "all types")->size() == 1);
- REQUIRE(get_table(new_realm, "link target")->size() == 2);
- REQUIRE(get_table(new_realm, "array target")->size() == 2);
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(ctx, "bool")) == false);
- });
- }
-
- SECTION("upsert in new realm after modifying primary key") {
- realm->update_schema(schema, 2, [&values](auto, auto new_realm, Schema&) {
- get_table(new_realm, "all types")->set_primary_key_column(ColKey());
- REQUIRE(new_realm->is_in_transaction());
- CppContext ctx(new_realm);
- any_cast<AnyDict&>(values)["bool"] = false;
- Object obj = Object::create(ctx, new_realm, "all types", values, CreatePolicy::UpdateAll);
- REQUIRE(get_table(new_realm, "all types")->size() == 1);
- REQUIRE(get_table(new_realm, "link target")->size() == 2);
- REQUIRE(get_table(new_realm, "array target")->size() == 2);
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(ctx, "bool")) == false);
- });
- }
-
- SECTION("change primary key property type") {
- schema = set_type(schema, "all types", "pk", PropertyType::String);
- realm->update_schema(schema, 2, [](auto, auto new_realm, auto&) {
- Object obj(new_realm, "all types", 0);
-
- CppContext ctx(new_realm);
- obj.set_property_value(ctx, "pk", util::Any("1"s));
- });
- }
-
- SECTION("set primary key to duplicate values in migration") {
- auto bad_migration = [&](auto, auto new_realm, Schema&) {
- // shoud be able to create a new object with the same PK
- REQUIRE_NOTHROW(Object::create(ctx, new_realm, "all types", values));
- REQUIRE(get_table(new_realm, "all types")->size() == 2);
-
- // but it'll fail at the end
- };
- REQUIRE_THROWS_AS(realm->update_schema(schema, 2, bad_migration), DuplicatePrimaryKeyValueException);
- REQUIRE(get_table(realm, "all types")->size() == 1);
-
- auto good_migration = [&](auto, auto new_realm, Schema&) {
- REQUIRE_NOTHROW(Object::create(ctx, new_realm, "all types", values));
-
- // Change the old object's PK to elminate the duplication
- Object old_obj(new_realm, "all types", 0);
- CppContext ctx(new_realm);
- old_obj.set_property_value(ctx, "pk", util::Any(INT64_C(5)));
- };
- REQUIRE_NOTHROW(realm->update_schema(schema, 2, good_migration));
- REQUIRE(get_table(realm, "all types")->size() == 2);
- }
-
- SECTION("modify existing int primary key values in migration") {
- // Create several more objects to increase the chance of things
- // actually breaking if we're doing invalid things
- CppContext ctx(realm);
- auto object_schema = realm->schema().find("all types");
- realm->begin_transaction();
- for (int i = 1; i < 10; ++i) {
- any_cast<AnyDict&>(values)["pk"] = INT64_C(1) + i;
- any_cast<AnyDict&>(values)["int"] = INT64_C(5) + i;
- Object::create(ctx, realm, *object_schema, values);
- }
- realm->commit_transaction();
-
- // Increase the PK of each object by one in a migration
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- Results results(new_realm, get_table(new_realm, "all types"));
- for (size_t i = 0, count = results.size(); i < count; ++i) {
- Object obj(new_realm, results.get<Obj>(i));
- util::Any v = 1 + any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "pk"));
- obj.set_property_value(ctx, "pk", v);
- }
- });
-
- // Create a new object with the no-longer-used pk of 1
- realm->begin_transaction();
- any_cast<AnyDict&>(values)["pk"] = INT64_C(1);
- any_cast<AnyDict&>(values)["int"] = INT64_C(4);
- object_schema = realm->schema().find("all types");
- Object::create(ctx, realm, *object_schema, values);
- realm->commit_transaction();
-
- // Verify results
- auto table = get_table(realm, "all types");
- REQUIRE(table->size() == 11);
- REQUIRE(table->get_primary_key_column() == table->get_column_key("pk"));
- for (int i = 0; i < 10; ++i) {
- auto obj = table->get_object(i);
- REQUIRE(obj.get<int64_t>("pk") == i + 2);
- REQUIRE(obj.get<int64_t>("int") == i + 5);
- }
- auto obj = table->get_object(10);
- REQUIRE(obj.get<int64_t>("pk") == 1);
- REQUIRE(obj.get<int64_t>("int") == 4);
- }
-
- SECTION("modify existing string primary key values in migration") {
- // Create several objects to increase the chance of things
- // actually breaking if we're doing invalid things
- CppContext ctx(realm);
- auto object_schema = realm->schema().find("string pk");
- realm->begin_transaction();
- for (int64_t i = 0; i < 10; ++i) {
- util::Any values = AnyDict{
- {"pk", util::to_string(i)},
- {"value", i + 1},
- };
- Object::create(ctx, realm, *object_schema, values);
- }
- realm->commit_transaction();
-
- // Increase the PK of each object by one in a migration
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- Results results(new_realm, get_table(new_realm, "string pk"));
- for (size_t i = 0, count = results.size(); i < count; ++i) {
- Object obj(new_realm, results.get<Obj>(i));
- util::Any v = util::to_string(any_cast<int64_t>(obj.get_property_value<util::Any>(ctx, "value")));
- obj.set_property_value(ctx, "pk", v);
- }
- });
-
- // Create a new object with the no-longer-used pk of 0
- realm->begin_transaction();
- util::Any values = AnyDict{
- {"pk", "0"s},
- {"value", INT64_C(0)},
- };
- object_schema = realm->schema().find("string pk");
- Object::create(ctx, realm, *object_schema, values);
- realm->commit_transaction();
-
- // Verify results
- auto table = get_table(realm, "string pk");
- REQUIRE(table->size() == 11);
- REQUIRE(table->get_primary_key_column() == table->get_column_key("pk"));
- for (auto& obj : *table) {
- REQUIRE(util::to_string(obj.get<int64_t>("value")).c_str() == obj.get<StringData>("pk"));
- }
- }
-
- SECTION("create and modify int primary key inside migration") {
- SECTION("with index") {
- realm->begin_transaction();
- auto table = get_table(realm, "int pk");
- table->add_search_index(table->get_column_key("pk"));
- realm->commit_transaction();
- }
- SECTION("no index") {
- }
-
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- for (int64_t i = 0; i < 10; ++i) {
- auto obj = Object::create(ctx, new_realm, *new_realm->schema().find("int pk"),
- util::Any(AnyDict{
- {"pk", INT64_C(0)},
- {"value", i}
- }));
- obj.set_property_value(ctx, "pk", util::Any(i));
- }
- });
-
- auto table = get_table(realm, "int pk");
- REQUIRE(table->size() == 10);
- REQUIRE(table->get_primary_key_column() == table->get_column_key("pk"));
- for (int i = 0; i < 10; ++i) {
- auto obj = table->get_object(i);
- REQUIRE(obj.get<int64_t>("pk") == i);
- REQUIRE(obj.get<int64_t>("value") == i);
- }
- }
-
- SECTION("create and modify string primary key inside migration") {
- SECTION("with index") {
- realm->begin_transaction();
- auto table = get_table(realm, "string pk");
- table->add_search_index(table->get_column_key("pk"));
- realm->commit_transaction();
- }
- SECTION("no index") {
- }
-
- realm->update_schema(schema, 2, [](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- for (int64_t i = 0; i < 10; ++i) {
- auto obj = Object::create(ctx, new_realm, *new_realm->schema().find("string pk"),
- util::Any(AnyDict{
- {"pk", ""s},
- {"value", i}
- }));
- obj.set_property_value(ctx, "pk", util::Any(util::to_string(i)));
- }
- });
-
- auto table = get_table(realm, "string pk");
- REQUIRE(table->size() == 10);
- REQUIRE(table->get_primary_key_column() == table->get_column_key("pk"));
- for (auto& obj : *table)
- REQUIRE(obj.get<StringData>("pk") == util::to_string(obj.get<int64_t>("value")).c_str());
- }
-
- SECTION("create object after adding primary key") {
- schema = set_primary_key(schema, "all types", "");
- realm->update_schema(schema, 2);
- schema = set_primary_key(schema, "all types", "pk");
- REQUIRE_NOTHROW(realm->update_schema(schema, 3, [&](auto, auto new_realm, Schema&) {
- CppContext ctx(new_realm);
- any_cast<AnyDict&>(values)["pk"] = INT64_C(2);
- Object::create(ctx, realm, "all types", values);
- }));
- }
- }
-
- SECTION("property renaming") {
- InMemoryTestFile config;
- config.schema_mode = SchemaMode::Automatic;
- auto realm = Realm::get_shared_realm(config);
-
- struct Rename {
- StringData object_type;
- StringData old_name;
- StringData new_name;
- };
-
- auto apply_renames = [&](std::initializer_list<Rename> renames) -> Realm::MigrationFunction {
- return [=](SharedRealm, SharedRealm realm, Schema& schema) {
- for (auto rename : renames) {
- ObjectStore::rename_property(realm->read_group(), schema,
- rename.object_type, rename.old_name, rename.new_name);
- }
- };
- };
-
-#define FAILED_RENAME(old_schema, new_schema, error, ...) do { \
- realm->update_schema(old_schema, 1); \
- REQUIRE_THROWS_WITH(realm->update_schema(new_schema, 2, apply_renames({__VA_ARGS__})), error); \
-} while (false)
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
-
- SECTION("table does not exist in old schema") {
- auto schema2 = add_table(schema, {"object 2", {
- {"value 2", PropertyType::Int},
- }});
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'object 2.value' because it does not exist.",
- {"object 2", "value", "value 2"});
- }
-
- SECTION("table does not exist in new schema") {
- FAILED_RENAME(schema, {},
- "Cannot rename properties for type 'object' because it has been removed from the Realm.",
- {"object", "value", "value 2"});
- }
-
- SECTION("property does not exist in old schema") {
- auto schema2 = add_property(schema, "object", {"new", PropertyType::Int});
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'object.nonexistent' because it does not exist.",
- {"object", "nonexistent", "new"});
- }
-
- auto rename_value = [](Schema schema) {
- schema.find("object")->property_for_name("value")->name = "new";
- return schema;
- };
-
- SECTION("property does not exist in new schema") {
- FAILED_RENAME(schema, rename_value(schema),
- "Renamed property 'object.nonexistent' does not exist.",
- {"object", "value", "nonexistent"});
- }
-
- SECTION("source propety still exists in the new schema") {
- auto schema2 = add_property(schema, "object",
- {"new", PropertyType::Int});
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'object.value' to 'new' because the source property still exists.",
- {"object", "value", "new"});
- }
-
- SECTION("different type") {
- auto schema2 = rename_value(set_type(schema, "object", "value", PropertyType::Date));
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'object.value' to 'new' because it would change from type 'int' to 'date'.",
- {"object", "value", "new"});
- }
-
- SECTION("different link targets") {
- Schema schema = {
- {"target", {
- {"value", PropertyType::Int},
- }},
- {"origin", {
- {"link", PropertyType::Object|PropertyType::Nullable, "target"},
- }},
- };
- auto schema2 = set_target(schema, "origin", "link", "origin");
- schema2.find("origin")->property_for_name("link")->name = "new";
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'origin.link' to 'new' because it would change from type '<target>' to '<origin>'.",
- {"origin", "link", "new"});
- }
-
- SECTION("different linklist targets") {
- Schema schema = {
- {"target", {
- {"value", PropertyType::Int},
- }},
- {"origin", {
- {"link", PropertyType::Array|PropertyType::Object, "target"},
- }},
- };
- auto schema2 = set_target(schema, "origin", "link", "origin");
- schema2.find("origin")->property_for_name("link")->name = "new";
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'origin.link' to 'new' because it would change from type 'array<target>' to 'array<origin>'.",
- {"origin", "link", "new"});
- }
-
- SECTION("make required") {
- schema = set_optional(schema, "object", "value", true);
- auto schema2 = rename_value(set_optional(schema, "object", "value", false));
- FAILED_RENAME(schema, schema2,
- "Cannot rename property 'object.value' to 'new' because it would change from optional to required.",
- {"object", "value", "new"});
- }
-
- auto init = [&](Schema const& old_schema) {
- realm->update_schema(old_schema, 1);
- realm->begin_transaction();
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- table->create_object().set_all(10);
- realm->commit_transaction();
- };
-
-#define SUCCESSFUL_RENAME(old_schema, new_schema, ...) do { \
- init(old_schema); \
- REQUIRE_NOTHROW(realm->update_schema(new_schema, 2, apply_renames({__VA_ARGS__}))); \
- REQUIRE(realm->schema() == new_schema); \
- VERIFY_SCHEMA(*realm, false); \
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object"); \
- auto key = table->get_column_keys()[0]; \
- if (table->get_column_attr(key).test(col_attr_Nullable)) \
- REQUIRE(table->begin()->get<util::Optional<int64_t>>(key) == 10); \
- else \
- REQUIRE(table->begin()->get<int64_t>(key) == 10); \
-} while (false)
-
- SECTION("basic valid rename") {
- auto schema2 = rename_value(schema);
- SUCCESSFUL_RENAME(schema, schema2,
- {"object", "value", "new"});
- }
-
- SECTION("chained rename") {
- auto schema2 = rename_value(schema);
- SUCCESSFUL_RENAME(schema, schema2,
- {"object", "value", "a"},
- {"object", "a", "b"},
- {"object", "b", "new"});
- }
-
- SECTION("old is pk, new is not") {
- auto schema2 = rename_value(schema);
- schema = set_primary_key(schema, "object", "value");
- SUCCESSFUL_RENAME(schema, schema2, {"object", "value", "new"});
- }
-
- SECTION("new is pk, old is not") {
- auto schema2 = set_primary_key(rename_value(schema), "object", "new");
- SUCCESSFUL_RENAME(schema, schema2, {"object", "value", "new"});
- }
-
- SECTION("both are pk") {
- schema = set_primary_key(schema, "object", "value");
- auto schema2 = set_primary_key(rename_value(schema), "object", "new");
- SUCCESSFUL_RENAME(schema, schema2, {"object", "value", "new"});
- }
-
- SECTION("make optional") {
- auto schema2 = rename_value(set_optional(schema, "object", "value", true));
- SUCCESSFUL_RENAME(schema, schema2,
- {"object", "value", "new"});
- }
-
- SECTION("add index") {
- auto schema2 = rename_value(set_indexed(schema, "object", "value", true));
- SUCCESSFUL_RENAME(schema, schema2, {"object", "value", "new"});
- }
-
- SECTION("remove index") {
- auto schema2 = rename_value(schema);
- schema = set_indexed(schema, "object", "value", true);
- SUCCESSFUL_RENAME(schema, schema2, {"object", "value", "new"});
- }
-
- SECTION("create object inside migration after renaming pk") {
- schema = set_primary_key(schema, "object", "value");
- auto new_schema = set_primary_key(rename_value(schema), "object", "new");
- init(schema);
- REQUIRE_NOTHROW(realm->update_schema(new_schema, 2, [](auto, auto realm, Schema& schema) {
- ObjectStore::rename_property(realm->read_group(), schema,
- "object", "value", "new");
-
- CppContext ctx(realm);
- util::Any values = AnyDict{{"new", INT64_C(11)}};
- Object::create(ctx, realm, "object", values);
- }));
- REQUIRE(realm->schema() == new_schema);
- VERIFY_SCHEMA(*realm, false);
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto key = table->get_column_keys()[0];
- auto it = table->begin();
- REQUIRE(it->get<int64_t>(key) == 10);
- REQUIRE((++it)->get<int64_t>(key) == 11);
- }
- }
-}
-
-TEST_CASE("migration: Immutable") {
- TestFile config;
-
- auto realm_with_schema = [&](Schema schema) {
- {
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(std::move(schema));
- }
- config.schema_mode = SchemaMode::Immutable;
- return Realm::get_shared_realm(config);
- };
-
- SECTION("allowed schema mismatches") {
- SECTION("index") {
- auto realm = realm_with_schema({
- {"object", {
- {"indexed", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"unindexed", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"indexed", PropertyType::Int},
- {"unindexed", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- REQUIRE(realm->schema() == schema);
- }
-
- SECTION("extra tables") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- }
-
- SECTION("missing tables") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"second object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- REQUIRE(realm->schema() == schema);
-
- auto object_schema = realm->schema().find("object");
- REQUIRE(object_schema->persisted_properties.size() == 1);
- REQUIRE(object_schema->persisted_properties[0].column_key);
-
- object_schema = realm->schema().find("second object");
- REQUIRE(object_schema->persisted_properties.size() == 1);
- REQUIRE(!object_schema->persisted_properties[0].column_key);
- }
-
- SECTION("extra columns in table") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- }
- }
-
- SECTION("disallowed mismatches") {
- SECTION("missing columns in table") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- };
- REQUIRE_THROWS(realm->update_schema(schema));
- }
-
- SECTION("bump schema version") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = realm_with_schema(schema);
- REQUIRE_THROWS(realm->update_schema(schema, 1));
- }
- }
-}
-
-TEST_CASE("migration: ReadOnly") {
- TestFile config;
-
- auto realm_with_schema = [&](Schema schema) {
- {
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(std::move(schema));
- }
- config.schema_mode = SchemaMode::ReadOnlyAlternative;
- return Realm::get_shared_realm(config);
- };
-
- SECTION("allowed schema mismatches") {
- SECTION("index") {
- auto realm = realm_with_schema({
- {"object", {
- {"indexed", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"unindexed", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"indexed", PropertyType::Int},
- {"unindexed", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- REQUIRE(realm->schema() == schema);
- }
-
- SECTION("extra tables") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- }
-
- SECTION("extra columns in table") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- }
-
- SECTION("missing tables") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"second object", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE_NOTHROW(realm->update_schema(schema));
- }
-
- SECTION("bump schema version") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- auto realm = realm_with_schema(schema);
- REQUIRE_NOTHROW(realm->update_schema(schema, 1));
- }
- }
-
- SECTION("disallowed mismatches") {
-
- SECTION("missing columns in table") {
- auto realm = realm_with_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- };
- REQUIRE_THROWS(realm->update_schema(schema));
- }
- }
-}
-
-TEST_CASE("migration: ResetFile") {
- TestFile config;
- config.schema_mode = SchemaMode::ResetFile;
-
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }},
- };
-
-// To verify that the file has actually be deleted and recreated, on
-// non-Windows we need to hold an open file handle to the old file to force
-// using a new inode, but on Windows we *can't*
-#ifdef _WIN32
- auto get_fileid = [&] {
- // this is wrong for non-ascii but it's what core does
- std::wstring ws(config.path.begin(), config.path.end());
- HANDLE handle = CreateFile2(ws.c_str(), GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING,
- nullptr);
- REQUIRE(handle != INVALID_HANDLE_VALUE);
- auto close = util::make_scope_exit([=]() noexcept { CloseHandle(handle); });
-
- BY_HANDLE_FILE_INFORMATION info{};
- REQUIRE(GetFileInformationByHandle(handle, &info));
- return (DWORDLONG)info.nFileIndexHigh + (DWORDLONG)info.nFileIndexLow;
- };
-#else
- auto get_fileid = [&] {
- util::File::UniqueID id;
- util::File::get_unique_id(config.path, id);
- return id.inode;
- };
- File holder(config.path, File::mode_Write);
-#endif
-
- {
- auto realm = Realm::get_shared_realm(config);
- auto ino = get_fileid();
- realm->update_schema(schema);
- REQUIRE(ino == get_fileid());
- realm->begin_transaction();
- ObjectStore::table_for_object_type(realm->read_group(), "object")->create_object();
- realm->commit_transaction();
- }
- auto realm = Realm::get_shared_realm(config);
- auto ino = get_fileid();
-
- SECTION("file is reset when schema version increases") {
- realm->update_schema(schema, 1);
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 0);
- REQUIRE(ino != get_fileid());
- }
-
- SECTION("file is reset when an existing table is modified") {
- realm->update_schema(add_property(schema, "object",
- {"value 2", PropertyType::Int}));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 0);
- REQUIRE(ino != get_fileid());
- }
-
- SECTION("file is not reset when adding a new table") {
- realm->update_schema(add_table(schema, {"object 3", {
- {"value", PropertyType::Int},
- }}));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 1);
- REQUIRE(realm->schema().size() == 3);
- REQUIRE(ino == get_fileid());
- }
-
- SECTION("file is not reset when removing a table") {
- realm->update_schema(remove_table(schema, "object 2"));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 1);
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object 2"));
- REQUIRE(realm->schema().size() == 1);
- REQUIRE(ino == get_fileid());
- }
-
- SECTION("file is not reset when adding an index") {
- realm->update_schema(set_indexed(schema, "object", "value", true));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 1);
- REQUIRE(ino == get_fileid());
- }
-
- SECTION("file is not reset when removing an index") {
- realm->update_schema(set_indexed(schema, "object", "value", true));
- realm->update_schema(schema);
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->size() == 1);
- REQUIRE(ino == get_fileid());
- }
-}
-
-TEST_CASE("migration: Additive") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"value 2", PropertyType::Int|PropertyType::Nullable},
- }},
- };
-
- TestFile config;
- config.schema_mode = SchemaMode::Additive;
- config.schema = schema;
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema(schema);
-
- SECTION("can add new properties to existing tables") {
- REQUIRE_NOTHROW(realm->update_schema(add_property(schema, "object",
- {"value 3", PropertyType::Int})));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->get_column_count() == 3);
- }
-
- SECTION("can add new tables") {
- REQUIRE_NOTHROW(realm->update_schema(add_table(schema, {"object 2", {
- {"value", PropertyType::Int},
- }})));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object"));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object 2"));
- }
-
- SECTION("indexes are updated when schema version is bumped") {
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto col_keys = table->get_column_keys();
- REQUIRE(table->has_search_index(col_keys[0]));
- REQUIRE(!table->has_search_index(col_keys[1]));
-
- REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value", false), 1));
- REQUIRE(!table->has_search_index(col_keys[0]));
-
- REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true), 2));
- REQUIRE(table->has_search_index(col_keys[1]));
- }
-
- SECTION("indexes are not updated when schema version is not bumped") {
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto col_keys = table->get_column_keys();
- REQUIRE(table->has_search_index(col_keys[0]));
- REQUIRE(!table->has_search_index(col_keys[1]));
-
- REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value", false)));
- REQUIRE(table->has_search_index(col_keys[0]));
-
- REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true)));
- REQUIRE(!table->has_search_index(col_keys[1]));
- }
-
- SECTION("can remove properties from existing tables, but column is not removed") {
- auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- REQUIRE_NOTHROW(realm->update_schema(remove_property(schema, "object", "value")));
- REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->get_column_count() == 2);
- auto const& properties = realm->schema().find("object")->persisted_properties;
- REQUIRE(properties.size() == 1);
- auto col_keys = table->get_column_keys();
- REQUIRE(col_keys.size() == 2);
- REQUIRE(properties[0].column_key == col_keys[1]);
- }
-
- SECTION("cannot change existing property types") {
- REQUIRE_THROWS(realm->update_schema(set_type(schema, "object", "value", PropertyType::Float)));
- }
-
- SECTION("cannot change existing property nullability") {
- REQUIRE_THROWS(realm->update_schema(set_optional(schema, "object", "value", true)));
- REQUIRE_THROWS(realm->update_schema(set_optional(schema, "object", "value 2", false)));
- }
-
- SECTION("cannot change existing link targets") {
- REQUIRE_NOTHROW(realm->update_schema(add_table(schema, {"object 2", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }})));
- REQUIRE_THROWS(realm->update_schema(set_target(realm->schema(), "object 2", "link", "object 2")));
- }
-
- SECTION("cannot change primary keys") {
- REQUIRE_THROWS(realm->update_schema(set_primary_key(schema, "object", "value")));
-
- REQUIRE_NOTHROW(realm->update_schema(add_table(schema, {"object 2", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- }})));
-
- REQUIRE_THROWS(realm->update_schema(set_primary_key(realm->schema(), "object 2", "")));
- }
-
- SECTION("schema version is allowed to go down") {
- REQUIRE_NOTHROW(realm->update_schema(schema, 1));
- REQUIRE(realm->schema_version() == 1);
- REQUIRE_NOTHROW(realm->update_schema(schema, 0));
- REQUIRE(realm->schema_version() == 1);
- }
-
- SECTION("migration function is not used") {
- REQUIRE_NOTHROW(realm->update_schema(schema, 1,
- [&](SharedRealm, SharedRealm, Schema&) { REQUIRE(false); }));
- }
-
- SECTION("add new columns from different SG") {
- auto realm2 = Realm::get_shared_realm(config);
- auto& group = realm2->read_group();
- realm2->begin_transaction();
- auto table = ObjectStore::table_for_object_type(group, "object");
- auto col_keys = table->get_column_keys();
- table->add_column(type_Int, "new column");
- realm2->commit_transaction();
-
- REQUIRE_NOTHROW(realm->refresh());
- REQUIRE(realm->schema() == schema);
- REQUIRE(realm->schema().find("object")->persisted_properties[0].column_key == col_keys[0]);
- REQUIRE(realm->schema().find("object")->persisted_properties[1].column_key == col_keys[1]);
- }
-
- SECTION("opening new Realms uses the correct schema after an external change") {
- auto realm2 = Realm::get_shared_realm(config);
- auto& group = realm2->read_group();
- realm2->begin_transaction();
- auto table = ObjectStore::table_for_object_type(group, "object");
- auto col_keys = table->get_column_keys();
- table->add_column(type_Double, "newcol");
- realm2->commit_transaction();
-
- REQUIRE_NOTHROW(realm->refresh());
- REQUIRE(realm->schema() == schema);
- REQUIRE(realm->schema().find("object")->persisted_properties[0].column_key == col_keys[0]);
- REQUIRE(realm->schema().find("object")->persisted_properties[1].column_key == col_keys[1]);
-
- // Gets the schema from the RealmCoordinator
- auto realm3 = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().find("object")->persisted_properties[0].column_key == col_keys[0]);
- REQUIRE(realm->schema().find("object")->persisted_properties[1].column_key == col_keys[1]);
-
- // Close and re-open the file entirely so that the coordinator is recreated
- realm.reset();
- realm2.reset();
- realm3.reset();
-
- realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema() == schema);
- REQUIRE(realm->schema().find("object")->persisted_properties[0].column_key == col_keys[0]);
- REQUIRE(realm->schema().find("object")->persisted_properties[1].column_key == col_keys[1]);
- }
-
- SECTION("can have different subsets of columns in different Realm instances") {
- auto config2 = config;
- config2.schema = add_property(schema, "object",
- {"value 3", PropertyType::Int});
- auto config3 = config;
- config3.schema = remove_property(schema, "object", "value 2");
-
- auto config4 = config;
- config4.schema = util::none;
-
- auto realm2 = Realm::get_shared_realm(config2);
- auto realm3 = Realm::get_shared_realm(config3);
- REQUIRE(realm->schema().find("object")->persisted_properties.size() == 2);
- REQUIRE(realm2->schema().find("object")->persisted_properties.size() == 3);
- REQUIRE(realm3->schema().find("object")->persisted_properties.size() == 1);
-
- realm->refresh();
- realm2->refresh();
- REQUIRE(realm->schema().find("object")->persisted_properties.size() == 2);
- REQUIRE(realm2->schema().find("object")->persisted_properties.size() == 3);
-
- // No schema specified; should see all of them
- auto realm4 = Realm::get_shared_realm(config4);
- REQUIRE(realm4->schema().find("object")->persisted_properties.size() == 3);
- }
-
- SECTION("updating a schema to include already-present column") {
- auto config2 = config;
- config2.schema = add_property(schema, "object",
- {"value 3", PropertyType::Int});
- auto realm2 = Realm::get_shared_realm(config2);
- auto& properties2 = realm2->schema().find("object")->persisted_properties;
-
- REQUIRE_NOTHROW(realm->update_schema(*config2.schema));
- REQUIRE(realm->schema().find("object")->persisted_properties.size() == 3);
- auto& properties = realm->schema().find("object")->persisted_properties;
- REQUIRE(properties[0].column_key == properties2[0].column_key);
- REQUIRE(properties[1].column_key == properties2[1].column_key);
- REQUIRE(properties[2].column_key == properties2[2].column_key);
- }
-
- SECTION("increasing schema version without modifying schema properly leaves the schema untouched") {
- TestFile config1;
- config1.schema = schema;
- config1.schema_mode = SchemaMode::Additive;
- config1.schema_version = 0;
-
- auto realm1 = Realm::get_shared_realm(config1);
- REQUIRE(realm1->schema().size() == 1);
- Schema schema1 = realm1->schema();
- realm1->close();
-
- auto config2 = config1;
- config2.schema_version = 1;
- auto realm2 = Realm::get_shared_realm(config2);
- REQUIRE(realm2->schema() == schema1);
- }
-
- SECTION("invalid schema update leaves the schema untouched") {
- auto config2 = config;
- config2.schema = add_property(schema, "object", {"value 3", PropertyType::Int});
- auto realm2 = Realm::get_shared_realm(config2);
-
- REQUIRE_THROWS(realm->update_schema(add_property(schema, "object", {"value 3", PropertyType::Float})));
- REQUIRE(realm->schema().find("object")->persisted_properties.size() == 2);
- }
-
- SECTION("update_schema() does not begin a write transaction when extra columns are present") {
- realm->begin_transaction();
-
- auto realm2 = Realm::get_shared_realm(config);
- // will deadlock if it tries to start a write transaction
- realm2->update_schema(remove_property(schema, "object", "value"));
- }
-
- SECTION("update_schema() does not begin a write transaction when indexes are changed without bumping schema version") {
- realm->begin_transaction();
-
- auto realm2 = Realm::get_shared_realm(config);
- // will deadlock if it tries to start a write transaction
- realm->update_schema(set_indexed(schema, "object", "value 2", true));
- }
-
- SECTION("update_schema() does not begin a write transaction for invalid schema changes") {
- realm->begin_transaction();
-
- auto realm2 = Realm::get_shared_realm(config);
- auto new_schema = add_property(remove_property(schema, "object", "value"),
- "object", {"value", PropertyType::Float});
- // will deadlock if it tries to start a write transaction
- REQUIRE_THROWS(realm2->update_schema(new_schema));
- }
-}
-
-TEST_CASE("migration: Manual") {
- TestFile config;
- config.schema_mode = SchemaMode::Manual;
- auto realm = Realm::get_shared_realm(config);
-
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"optional", PropertyType::Int|PropertyType::Nullable},
- }},
- {"link origin", {
- {"not a pk", PropertyType::Int},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Array|PropertyType::Object, "object"},
- }}
- };
- realm->update_schema(schema);
- auto col_keys = realm->read_group().get_table("class_object")->get_column_keys();
-
-#define REQUIRE_MIGRATION(schema, migration) do { \
- Schema new_schema = (schema); \
- REQUIRE_THROWS(realm->update_schema(new_schema)); \
- REQUIRE(realm->schema_version() == 0); \
- REQUIRE_THROWS(realm->update_schema(new_schema, 1, [](SharedRealm, SharedRealm, Schema&){})); \
- REQUIRE(realm->schema_version() == 0); \
- REQUIRE_NOTHROW(realm->update_schema(new_schema, 1, migration)); \
- REQUIRE(realm->schema_version() == 1); \
-} while (false)
-
- SECTION("add new table") {
- REQUIRE_MIGRATION(add_table(schema, {"new table", {
- {"value", PropertyType::Int},
- }}), [](SharedRealm, SharedRealm realm, Schema&) {
- realm->read_group().add_table("class_new table")->add_column(type_Int, "value");
- });
- }
- SECTION("add property to table") {
- REQUIRE_MIGRATION(add_property(schema, "object", {"new", PropertyType::Int}),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- get_table(realm, "object")->add_column(type_Int, "new");
- });
- }
- SECTION("remove property from table") {
- REQUIRE_MIGRATION(remove_property(schema, "object", "value"),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- get_table(realm, "object")->remove_column(col_keys[1]);
- });
- }
- SECTION("add primary key to table") {
- REQUIRE_MIGRATION(set_primary_key(schema, "link origin", "not a pk"),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::set_primary_key_for_object(realm->read_group(), "link origin", "not a pk");
- auto table = get_table(realm, "link origin");
- table->add_search_index(table->get_column_key("not a pk"));
- });
- }
- SECTION("remove primary key from table") {
- REQUIRE_MIGRATION(set_primary_key(schema, "object", ""),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "");
- get_table(realm, "object")->remove_search_index(col_keys[0]);
- });
- }
- SECTION("change primary key") {
- REQUIRE_MIGRATION(set_primary_key(schema, "object", "value"),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "value");
- auto table = get_table(realm, "object");
- table->remove_search_index(col_keys[0]);
- table->add_search_index(col_keys[1]);
- });
- }
- SECTION("change property type") {
- REQUIRE_MIGRATION(set_type(schema, "object", "value", PropertyType::Date),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- auto table = get_table(realm, "object");
- table->remove_column(col_keys[1]);
- auto col = table->add_column(type_Timestamp, "value");
- table->add_search_index(col);
- });
- }
- SECTION("change link target") {
- REQUIRE_MIGRATION(set_target(schema, "link origin", "object", "link origin"),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- auto table = get_table(realm, "link origin");
- table->remove_column(table->get_column_keys()[1]);
- table->add_column_link(type_Link, "object", *table);
- });
- }
- SECTION("change linklist target") {
- REQUIRE_MIGRATION(set_target(schema, "link origin", "array", "link origin"),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- auto table = get_table(realm, "link origin");
- table->remove_column(table->get_column_keys()[2]);
- table->add_column_link(type_LinkList, "array", *table);
- });
- }
- SECTION("make property optional") {
- REQUIRE_MIGRATION(set_optional(schema, "object", "value", true),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- auto table = get_table(realm, "object");
- table->remove_column(col_keys[1]);
- auto col = table->add_column(type_Int, "value", true);
- table->add_search_index(col);
- });
- }
- SECTION("make property required") {
- REQUIRE_MIGRATION(set_optional(schema, "object", "optional", false),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- auto table = get_table(realm, "object");
- table->remove_column(col_keys[2]);
- table->add_column(type_Int, "optional", false);
- });
- }
- SECTION("add index") {
- REQUIRE_MIGRATION(set_indexed(schema, "object", "optional", true),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- get_table(realm, "object")->add_search_index(col_keys[2]);
- });
- }
- SECTION("remove index") {
- REQUIRE_MIGRATION(set_indexed(schema, "object", "value", false),
- [&](SharedRealm, SharedRealm realm, Schema&) {
- get_table(realm, "object")->remove_search_index(col_keys[1]);
- });
- }
- SECTION("reorder properties") {
- auto schema2 = schema;
- auto& properties = schema2.find("object")->persisted_properties;
- std::swap(properties[0], properties[1]);
- REQUIRE_NOTHROW(realm->update_schema(schema2));
- }
-
- SECTION("cannot lower schema version") {
- REQUIRE_NOTHROW(realm->update_schema(schema, 1, [](SharedRealm, SharedRealm, Schema&){}));
- REQUIRE(realm->schema_version() == 1);
- REQUIRE_THROWS(realm->update_schema(schema, 0, [](SharedRealm, SharedRealm, Schema&){}));
- REQUIRE(realm->schema_version() == 1);
- }
-
- SECTION("update_schema() does not begin a write transaction when schema version is unchanged") {
- realm->begin_transaction();
-
- auto realm2 = Realm::get_shared_realm(config);
- // will deadlock if it tries to start a write transaction
- REQUIRE_NOTHROW(realm2->update_schema(schema));
- REQUIRE_THROWS(realm2->update_schema(remove_property(schema, "object", "value")));
- }
-
- SECTION("null migration callback should throw SchemaMismatchException") {
- Schema new_schema = remove_property(schema, "object", "value");
- REQUIRE_THROWS_AS(realm->update_schema(new_schema, 1, nullptr), SchemaMismatchException);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/CMakeLists.txt b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/CMakeLists.txt
deleted file mode 100644
index a23d7c163..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-macro(build_fuzzer_variant variant)
- add_executable(${variant} command_file.hpp command_file.cpp ${variant}.cpp)
- target_link_libraries(${variant} realm-object-store)
- set_target_properties(${variant} PROPERTIES
- EXCLUDE_FROM_ALL 1
- EXCLUDE_FROM_DEFAULT_BUILD 1)
-endmacro()
-
-build_fuzzer_variant(fuzzer)
-build_fuzzer_variant(fuzz-sorted-query)
-build_fuzzer_variant(fuzz-unsorted-query)
-build_fuzzer_variant(fuzz-sorted-linkview)
-build_fuzzer_variant(fuzz-unsorted-linkview)
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.cpp
deleted file mode 100644
index 0c8af161f..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "command_file.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "shared_realm.hpp"
-
-#include <realm/link_view.hpp>
-#include <realm/table.hpp>
-
-#include <istream>
-
-using namespace fuzzer;
-using namespace realm;
-
-#if 0
-#define log(...) fprintf(stderr, __VA_ARGS__)
-#else
-#define log(...)
-#endif
-
-template<typename T>
-static T read_value(std::istream& input)
-{
- T ret;
- input >> ret;
- return ret;
-}
-
-template<typename... Args>
-static auto make_reader(void (*fn)(RealmState&, Args...)) {
- return [=](std::istream& input) {
- return std::bind(fn, std::placeholders::_1, read_value<Args>(input)...);
- };
-}
-
-static void run_add(RealmState& state, int64_t value)
-{
- log("add %lld\n", value);
- size_t ndx = state.table.add_empty_row();
- state.table.set_int(0, ndx, state.uid++);
- state.table.set_int(1, ndx, value);
-}
-
-static void run_modify(RealmState& state, size_t index, int64_t value)
-{
- if (index < state.table.size()) {
- log("modify %zu %lld\n", index, value);
- state.table.set_int(1, index, value);
- state.modified.push_back(state.table.get_int(0, index));
- }
-}
-
-static void run_delete(RealmState& state, size_t index)
-{
- if (index < state.table.size()) {
- log("delete %zu (%lld)\n", index, state.table.get_int(1, index));
- state.table.move_last_over(index);
- }
-}
-
-static void run_commit(RealmState& state)
-{
- log("commit\n");
- state.realm.commit_transaction();
- state.coordinator.on_change();
- state.realm.begin_transaction();
-}
-
-static void run_lv_insert(RealmState& state, size_t pos, size_t target)
-{
- if (!state.lv) return;
- if (target < state.table.size() && pos <= state.lv->size()) {
- log("lv insert %zu %zu\n", pos, target);
- state.lv->insert(pos, target);
- }
-}
-
-static void run_lv_set(RealmState& state, size_t pos, size_t target)
-{
- if (!state.lv) return;
- if (target < state.table.size() && pos < state.lv->size()) {
- log("lv set %zu %zu\n", pos, target);
- // We can't reliably detect self-assignment for verification, so don't do it
- if (state.lv->get(pos).get_index() != target)
- state.lv->set(pos, target);
- }
-}
-
-static void run_lv_move(RealmState& state, size_t from, size_t to)
-{
- if (!state.lv) return;
- if (from < state.lv->size() && to < state.lv->size()) {
- log("lv move %zu %zu\n", from, to);
- // FIXME: only do the move if it has an effect to avoid getting a
- // notification which we weren't expecting. This is really urgh.
- for (size_t i = std::min(from, to); i < std::max(from, to); ++i) {
- if (state.lv->get(i).get_index() != state.lv->get(i + 1).get_index()) {
- state.lv->move(from, to);
- break;
- }
- }
- }
-}
-
-static void run_lv_swap(RealmState& state, size_t ndx1, size_t ndx2)
-{
- if (!state.lv) return;
- if (ndx1 < state.lv->size() && ndx2 < state.lv->size()) {
- log("lv swap %zu %zu\n", ndx1, ndx2);
- if (state.lv->get(ndx1).get_index() != state.lv->get(ndx2).get_index()) {
- state.lv->swap(ndx1, ndx2);
- // FIXME: swap() needs to produce moves so that a pair of swaps can
- // be collapsed away. Currently it just marks the rows as modified.
- state.modified.push_back(state.lv->get(ndx1).get_int(0));
- state.modified.push_back(state.lv->get(ndx2).get_int(0));
- }
- }
-}
-
-static void run_lv_remove(RealmState& state, size_t pos)
-{
- if (!state.lv) return;
- if (pos < state.lv->size()) {
- log("lv remove %zu\n", pos);
- state.lv->remove(pos);
- }
-}
-
-static void run_lv_remove_target(RealmState& state, size_t pos)
-{
- if (!state.lv) return;
- if (pos < state.lv->size()) {
- log("lv target remove %zu\n", pos);
- state.lv->remove_target_row(pos);
- }
-}
-
-static std::map<char, std::function<std::function<void (RealmState&)>(std::istream&)>> readers = {
- // Row functions
- {'a', make_reader(run_add)},
- {'c', make_reader(run_commit)},
- {'d', make_reader(run_delete)},
- {'m', make_reader(run_modify)},
-
- // LinkView functions
- {'i', make_reader(run_lv_insert)},
- {'s', make_reader(run_lv_set)},
- {'o', make_reader(run_lv_move)},
- {'w', make_reader(run_lv_swap)},
- {'r', make_reader(run_lv_remove)},
- {'t', make_reader(run_lv_remove_target)},
-};
-
-template<typename T>
-static std::vector<T> read_int_list(std::istream& input_stream)
-{
- std::vector<T> ret;
- std::string line;
- while (std::getline(input_stream, line) && !line.empty()) {
- try {
- ret.push_back(std::stoll(line));
- log("%lld\n", (long long)ret.back());
- }
- catch (std::invalid_argument const&) {
- // not an error
- }
- catch (std::out_of_range const&) {
- // not an error
- }
- }
- log("\n");
- return ret;
-}
-
-CommandFile::CommandFile(std::istream& input)
-: initial_values(read_int_list<int64_t>(input))
-, initial_list_indices(read_int_list<size_t>(input))
-{
- if (!input.good())
- return;
-
- while (input.good()) {
- char op = '\0';
- input >> op;
- if (!input.good())
- break;
-
- auto it = readers.find(op);
- if (it == readers.end())
- continue;
-
- auto fn = it->second(input);
- if (!input.good())
- return;
- commands.push_back(std::move(fn));
- }
-}
-
-void CommandFile::import(RealmState& state)
-{
- auto& table = state.table;
-
- state.realm.begin_transaction();
-
- table.clear();
- size_t ndx = table.add_empty_row(initial_values.size());
- for (auto value : initial_values) {
- table.set_int(0, ndx, state.uid++);
- table.set_int(1, ndx++, value);
- }
-
- state.lv->clear();
- for (auto value : initial_list_indices) {
- if (value < table.size())
- state.lv->add(value);
- }
-
- state.realm.commit_transaction();
-
-}
-
-void CommandFile::run(RealmState& state)
-{
- state.realm.begin_transaction();
- for (auto& command : commands) {
- command(state);
- }
- state.realm.commit_transaction();
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.hpp
deleted file mode 100644
index 7c7321454..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/command_file.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 <realm/link_view_fwd.hpp>
-
-#include <iosfwd>
-#include <functional>
-#include <memory>
-#include <vector>
-
-namespace realm {
- class Table;
- class LinkView;
- class Realm;
- namespace _impl {
- class RealmCoordinator;
- }
-}
-
-namespace fuzzer {
-struct RealmState {
- realm::Realm& realm;
- realm::_impl::RealmCoordinator& coordinator;
-
- realm::Table& table;
- realm::LinkViewRef lv;
- int64_t uid;
- std::vector<int64_t> modified;
-};
-
-struct CommandFile {
- std::vector<int64_t> initial_values;
- std::vector<size_t> initial_list_indices;
- std::vector<std::function<void (RealmState&)>> commands;
-
- CommandFile(std::istream& input);
-
- void import(RealmState& state);
- void run(RealmState& state);
-};
-} \ No newline at end of file
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-linkview.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-linkview.cpp
deleted file mode 100644
index f54078534..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-linkview.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define FUZZ_SORTED 1
-#define FUZZ_LINKVIEW 1
-#include "fuzzer.cpp"
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-query.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-query.cpp
deleted file mode 100644
index 7a719d20a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-sorted-query.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define FUZZ_SORTED 1
-#define FUZZ_LINKVIEW 0
-#include "fuzzer.cpp"
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-linkview.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-linkview.cpp
deleted file mode 100644
index a7f20380e..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-linkview.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define FUZZ_SORTED 0
-#define FUZZ_LINKVIEW 1
-#include "fuzzer.cpp"
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-query.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-query.cpp
deleted file mode 100644
index 18dcb45ed..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzz-unsorted-query.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define FUZZ_SORTED 0
-#define FUZZ_LINKVIEW 0
-#include "fuzzer.cpp"
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp
deleted file mode 100644
index 7bc9089ec..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "command_file.hpp"
-
-#include "list.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "impl/realm_coordinator.hpp"
-
-#include <realm/commit_log.hpp>
-#include <realm/disable_sync_to_disk.hpp>
-#include <realm/group_shared.hpp>
-#include <realm/link_view.hpp>
-
-#include <iostream>
-#include <sstream>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-using namespace realm;
-
-#ifndef FUZZ_SORTED
-#define FUZZ_SORTED 0
-#endif
-
-#ifndef FUZZ_LINKVIEW
-#define FUZZ_LINKVIEW 0
-#endif
-
-#define FUZZ_LOG 0
-
-// Read from a fd until eof into a string
-// Needs to use unbuffered i/o to work properly with afl
-static void read_all(std::string& buffer, int fd)
-{
- buffer.clear();
- size_t offset = 0;
- while (true) {
- buffer.resize(offset + 4096);
- ssize_t bytes_read = read(fd, &buffer[offset], 4096);
- if (bytes_read < 4096) {
- buffer.resize(offset + bytes_read);
- break;
- }
- offset += 4096;
- }
-}
-
-static Query query(fuzzer::RealmState& state)
-{
-#if FUZZ_LINKVIEW
- return state.table.where(state.lv);
-#else
- return state.table.where().greater(1, 100).less(1, 50000);
-#endif
-}
-
-static TableView tableview(fuzzer::RealmState& state)
-{
- auto tv = query(state).find_all();
-#if FUZZ_SORTED
- tv.sort({1, 0}, {true, true});
-#endif
- return tv;
-}
-
-// Apply the changes from the command file and then return whether a change
-// notification should occur
-static bool apply_changes(fuzzer::CommandFile& commands, fuzzer::RealmState& state)
-{
- auto tv = tableview(state);
-#if FUZZ_LOG
- for (size_t i = 0; i < tv.size(); ++i)
- fprintf(stderr, "pre: %lld\n", tv.get_int(0, i));
-#endif
-
- commands.run(state);
-
- auto tv2 = tableview(state);
- if (tv.size() != tv2.size())
- return true;
-
- for (size_t i = 0; i < tv.size(); ++i) {
-#if FUZZ_LOG
- fprintf(stderr, "%lld %lld\n", tv.get_int(0, i), tv2.get_int(0, i));
-#endif
- if (!tv.is_row_attached(i))
- return true;
- if (tv.get_int(0, i) != tv2.get_int(0, i))
- return true;
- if (find(begin(state.modified), end(state.modified), tv.get_int(0, i)) != end(state.modified))
- return true;
- }
-
- return false;
-}
-
-static auto verify(CollectionChangeIndices const& changes, std::vector<int64_t> values, fuzzer::RealmState& state)
-{
- auto tv = tableview(state);
-
- // Apply the changes from the transaction log to our copy of the
- // initial, using UITableView's batching rules (i.e. delete, then
- // insert, then update)
- auto it = util::make_reverse_iterator(changes.deletions.end());
- auto end = util::make_reverse_iterator(changes.deletions.begin());
- for (; it != end; ++it) {
- values.erase(values.begin() + it->first, values.begin() + it->second);
- }
-
- for (auto i : changes.insertions.as_indexes()) {
- values.insert(values.begin() + i, tv.get_int(1, i));
- }
-
- if (values.size() != tv.size()) {
- abort();
- }
-
- for (auto i : changes.modifications.as_indexes()) {
- if (changes.insertions.contains(i))
- abort();
- values[i] = tv.get_int(1, i);
- }
-
-#if FUZZ_SORTED
- if (!std::is_sorted(values.begin(), values.end()))
- abort();
-#endif
-
- for (size_t i = 0; i < values.size(); ++i) {
- if (values[i] != tv.get_int(1, i)) {
-#if FUZZ_LOG
- fprintf(stderr, "%lld %lld\n", values[i], tv.get_int(1, i));
-#endif
- abort();
- }
- }
-
- return values;
-}
-
-static void verify_no_op(CollectionChangeIndices const& changes, std::vector<int64_t> values, fuzzer::RealmState& state)
-{
- auto new_values = verify(changes, values, state);
- if (!std::equal(begin(values), end(values), begin(new_values), end(new_values)))
- abort();
-}
-
-static void test(Realm::Config const& config, SharedRealm& r, SharedRealm& r2, std::istream& input_stream)
-{
- fuzzer::RealmState state = {
- *r,
- *_impl::RealmCoordinator::get_existing_coordinator(r->config().path),
- *r->read_group()->get_table("class_object"),
- r->read_group()->get_table("class_linklist")->get_linklist(0, 0),
- 0,
- {}
- };
-
- fuzzer::CommandFile command(input_stream);
- if (command.initial_values.empty()) {
- return;
- }
- command.import(state);
-
- fuzzer::RealmState state2 = {
- *r2,
- state.coordinator,
- *r2->read_group()->get_table("class_object"),
-#if FUZZ_LINKVIEW
- r2->read_group()->get_table("class_linklist")->get_linklist(0, 0),
-#else
- {},
-#endif
- state.uid,
- {}
- };
-
-#if FUZZ_LINKVIEW && !FUZZ_SORTED
- auto results = List(r, ObjectSchema(), state.lv);
-#else
- auto results = Results(r, ObjectSchema(), query(state))
-#if FUZZ_SORTED
- .sort({{1, 0}, {true, true}})
-#endif
- ;
-#endif // FUZZ_LINKVIEW
-
- std::vector<int64_t> initial_values;
- for (size_t i = 0; i < results.size(); ++i)
- initial_values.push_back(results.get(i).get_int(1));
-
- CollectionChangeIndices changes;
- int notification_calls = 0;
- auto token = results.add_notification_callback([&](CollectionChangeIndices c, std::exception_ptr err) {
- if (notification_calls > 0 && c.empty())
- abort();
- changes = c;
- ++notification_calls;
- });
-
- state.coordinator.on_change(); r->notify();
- if (notification_calls != 1) {
- abort();
- }
-
- bool expect_notification = apply_changes(command, state2);
- state.coordinator.on_change(); r->notify();
-
- if (expect_notification) {
- if (notification_calls != 2)
- abort();
- verify(changes, initial_values, state);
- }
- else {
- if (notification_calls == 2)
- verify_no_op(changes, initial_values, state);
- }
-}
-
-int main(int argc, char** argv) {
- std::ios_base::sync_with_stdio(false);
- realm::disable_sync_to_disk();
-
- Realm::Config config;
- config.path = "fuzzer.realm";
- config.in_memory = true;
- config.automatic_change_notifications = false;
-
- Schema schema{
- {"object", "", {
- {"id", PropertyTypeInt},
- {"value", PropertyTypeInt}
- }},
- {"linklist", "", {
- {"list", PropertyTypeArray, "object"}
- }}
- };
-
- config.schema = std::make_unique<Schema>(schema);
- unlink(config.path.c_str());
-
- auto r = Realm::get_shared_realm(config);
- auto r2 = Realm::get_shared_realm(config);
- auto& coordinator = *_impl::RealmCoordinator::get_existing_coordinator(config.path);
-
- r->begin_transaction();
- r->read_group()->get_table("class_linklist")->add_empty_row();
- r->commit_transaction();
-
- auto test_on = [&](auto& buffer) {
- std::istringstream ss(buffer);
- test(config, r, r2, ss);
- if (r->is_in_transaction())
- r->cancel_transaction();
- r2->invalidate();
- coordinator.on_change();
- };
-
- if (argc > 1) {
- std::string buffer;
- for (int i = 1; i < argc; ++i) {
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0)
- abort();
- read_all(buffer, fd);
- close(fd);
-
- test_on(buffer);
- }
- unlink(config.path.c_str());
- return 0;
- }
-
-#ifdef __AFL_HAVE_MANUAL_CONTROL
- std::string buffer;
- while (__AFL_LOOP(1000)) {
- read_all(buffer, 0);
- test_on(buffer);
- }
-#else
- std::string buffer;
- read_all(buffer, 0);
- test_on(buffer);
-#endif
-
- unlink(config.path.c_str());
- return 0;
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input-lv/0 b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input-lv/0
deleted file mode 100644
index cb6a3bd99..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input-lv/0
+++ /dev/null
@@ -1,38 +0,0 @@
-3
-100
-200
-400
-1000
-2000
-50
-80
-150
-180
-6000
-5000
-60000
-
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-11
-12
-
-a 500
-d 12
-c
-m 11 10000
-a 800
-i 5 13
-s 3 8
-o 2 10
-w 1 6
-r 7
-t 11
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/0 b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/0
deleted file mode 100644
index 675ab1573..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/0
+++ /dev/null
@@ -1,20 +0,0 @@
-3
-100
-200
-400
-1000
-2000
-50
-80
-150
-180
-6000
-5000
-60000
-
-
-a 500
-d 12
-c
-m 11 10000
-a 800
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/1 b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/1
deleted file mode 100644
index 1cbf4855b..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/input/1
+++ /dev/null
@@ -1,34 +0,0 @@
-101
-102
-103
-104
-105
-106
-107
-108
-109
-110
-111
-112
-113
-
-
-a 114
-a 115
-a 116
-a 117
-a 118
-a 119
-a 120
-a 121
-a 122
-c
-m 4 200
-m 3 201
-m 2 202
-m 1 203
-m 5 203
-m 6 204
-m 7 205
-c
-d 11
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp
deleted file mode 100644
index 10de55f30..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp
+++ /dev/null
@@ -1,1017 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "catch2/catch.hpp"
-
-#include "util/event_loop.hpp"
-#include "util/index_helpers.hpp"
-#include "util/test_file.hpp"
-
-#include "feature_checks.hpp"
-#include "collection_notifications.hpp"
-#include "object_accessor.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "impl/object_accessor_impl.hpp"
-
-#include <realm/group.hpp>
-#include <realm/util/any.hpp>
-
-#include <cstdint>
-
-using namespace realm;
-
-namespace {
-using AnyDict = std::map<std::string, util::Any>;
-using AnyVec = std::vector<util::Any>;
-template <class T>
-std::vector<T> get_vector(std::initializer_list<T> list)
-{
- return std::vector<T>(list);
-}
-}
-
-struct TestContext : CppContext {
- std::map<std::string, AnyDict> defaults;
-
- using CppContext::CppContext;
- TestContext(TestContext& parent, realm::Property const& prop)
- : CppContext(parent, prop)
- , defaults(parent.defaults)
- { }
-
- util::Optional<util::Any>
- default_value_for_property(ObjectSchema const& object, Property const& prop)
- {
- auto obj_it = defaults.find(object.name);
- if (obj_it == defaults.end())
- return util::none;
- auto prop_it = obj_it->second.find(prop.name);
- if (prop_it == obj_it->second.end())
- return util::none;
- return prop_it->second;
- }
-
- void will_change(Object const&, Property const&) {}
- void did_change() {}
- std::string print(util::Any) { return "not implemented"; }
- bool allow_missing(util::Any) { return false; }
-};
-
-TEST_CASE("object") {
- using namespace std::string_literals;
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- config.schema = Schema{
- {"table", {
- {"value 1", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- {"all types", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"bool", PropertyType::Bool},
- {"int", PropertyType::Int},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"data", PropertyType::Data},
- {"date", PropertyType::Date},
- {"object", PropertyType::Object|PropertyType::Nullable, "link target"},
-
- {"bool array", PropertyType::Array|PropertyType::Bool},
- {"int array", PropertyType::Array|PropertyType::Int},
- {"float array", PropertyType::Array|PropertyType::Float},
- {"double array", PropertyType::Array|PropertyType::Double},
- {"string array", PropertyType::Array|PropertyType::String},
- {"data array", PropertyType::Array|PropertyType::Data},
- {"date array", PropertyType::Array|PropertyType::Date},
- {"object array", PropertyType::Array|PropertyType::Object, "array target"},
- }},
- {"all optional types", {
- {"pk", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{true}},
- {"bool", PropertyType::Bool|PropertyType::Nullable},
- {"int", PropertyType::Int|PropertyType::Nullable},
- {"float", PropertyType::Float|PropertyType::Nullable},
- {"double", PropertyType::Double|PropertyType::Nullable},
- {"string", PropertyType::String|PropertyType::Nullable},
- {"data", PropertyType::Data|PropertyType::Nullable},
- {"date", PropertyType::Date|PropertyType::Nullable},
-
- {"bool array", PropertyType::Array|PropertyType::Bool|PropertyType::Nullable},
- {"int array", PropertyType::Array|PropertyType::Int|PropertyType::Nullable},
- {"float array", PropertyType::Array|PropertyType::Float|PropertyType::Nullable},
- {"double array", PropertyType::Array|PropertyType::Double|PropertyType::Nullable},
- {"string array", PropertyType::Array|PropertyType::String|PropertyType::Nullable},
- {"data array", PropertyType::Array|PropertyType::Data|PropertyType::Nullable},
- {"date array", PropertyType::Array|PropertyType::Date|PropertyType::Nullable},
- }},
- {"link target", {
- {"value", PropertyType::Int},
- }, {
- {"origin", PropertyType::LinkingObjects|PropertyType::Array, "all types", "object"},
- }},
- {"array target", {
- {"value", PropertyType::Int},
- }},
- {"pk after list", {
- {"array 1", PropertyType::Array|PropertyType::Object, "array target"},
- {"int 1", PropertyType::Int},
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"int 2", PropertyType::Int},
- {"array 2", PropertyType::Array|PropertyType::Object, "array target"},
- }},
- {"nullable int pk", {
- {"pk", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{true}},
- }},
- {"nullable string pk", {
- {"pk", PropertyType::String|PropertyType::Nullable, Property::IsPrimary{true}},
- }},
- {"person", {
- {"name", PropertyType::String, Property::IsPrimary{true}},
- {"age", PropertyType::Int},
- {"scores", PropertyType::Array|PropertyType::Int},
- {"assistant", PropertyType::Object|PropertyType::Nullable, "person"},
- {"team", PropertyType::Array|PropertyType::Object, "person"},
- }},
- };
- config.schema_version = 0;
- auto r = Realm::get_shared_realm(config);
- auto& coordinator = *_impl::RealmCoordinator::get_coordinator(config.path);
-
- SECTION("add_notification_callback()") {
- auto table = r->read_group().get_table("class_table");
- auto col_keys = table->get_column_keys();
- ObjKeys object_keys({3, 4, 7, 9, 10, 21, 24, 34, 42, 50});
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- table->create_object(object_keys[i]).set_all(i);
- r->commit_transaction();
-
- auto r2 = coordinator.get_realm();
-
- CollectionChangeSet change;
- auto obj = *table->begin();
- Object object(r, obj);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- advance_and_notify(*r);
- };
-
- auto require_change = [&] {
- auto token = object.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- });
- advance_and_notify(*r);
- return token;
- };
-
- auto require_no_change = [&] {
- bool first = true;
- auto token = object.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- REQUIRE(first);
- first = false;
- });
- advance_and_notify(*r);
- return token;
- };
-
- SECTION("deleting the object sends a change notification") {
- auto token = require_change();
- write([&] { obj.remove(); });
- REQUIRE_INDICES(change.deletions, 0);
- }
-
- SECTION("clearing the table sends a change notification") {
- auto token = require_change();
- write([&] { table->clear(); });
- REQUIRE_INDICES(change.deletions, 0);
- }
-
- SECTION("clearing the table sends a change notification to the last object") {
- obj = table->get_object(table->size() - 1);
- object = Object(r, obj);
-
- auto token = require_change();
- write([&] { table->clear(); });
- REQUIRE_INDICES(change.deletions, 0);
- }
-
- SECTION("modifying the object sends a change notification") {
- auto token = require_change();
-
- write([&] { obj.set(col_keys[0], 10); });
- REQUIRE_INDICES(change.modifications, 0);
- REQUIRE(change.columns.size() == 1);
- REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
-
- write([&] { obj.set(col_keys[1], 10); });
- REQUIRE_INDICES(change.modifications, 0);
- REQUIRE(change.columns.size() == 1);
- REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
- }
-
- SECTION("modifying a different object") {
- auto token = require_no_change();
- write([&] { table->get_object(1).set(col_keys[0], 10); });
- }
-
- SECTION("multiple write transactions") {
- auto token = require_change();
-
- auto r2row = r2->read_group().get_table("class_table")->get_object(0);
- r2->begin_transaction();
- r2row.set(col_keys[0], 1);
- r2->commit_transaction();
- r2->begin_transaction();
- r2row.set(col_keys[1], 2);
- r2->commit_transaction();
-
- advance_and_notify(*r);
- REQUIRE(change.columns.size() == 2);
- REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
- REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
- }
-
- SECTION("skipping a notification") {
- auto token = require_no_change();
- write([&] {
- obj.set(col_keys[0], 1);
- token.suppress_next();
- });
- }
-
- SECTION("skipping only effects the current transaction even if no notification would occur anyway") {
- auto token = require_change();
-
- // would not produce a notification even if it wasn't skipped because no changes were made
- write([&] {
- token.suppress_next();
- });
- REQUIRE(change.empty());
-
- // should now produce a notification
- write([&] { obj.set(col_keys[0], 1); });
- REQUIRE_INDICES(change.modifications, 0);
- }
-
- SECTION("add notification callback, remove it, then add another notification callback") {
- {
- auto token = object.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- FAIL("This should never happen");
- });
- }
- auto token = require_change();
- write([&] { obj.remove(); });
- REQUIRE_INDICES(change.deletions, 0);
- }
-
- SECTION("observing deleted object throws") {
- write([&] {
- obj.remove();
- });
- REQUIRE_THROWS(require_change());
- }
- }
-
- TestContext d(r);
- auto create = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
- r->begin_transaction();
- auto obj = Object::create(d, r, *r->schema().find("all types"), value, policy);
- r->commit_transaction();
- return obj;
- };
- auto create_sub = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
- r->begin_transaction();
- auto obj = Object::create(d, r, *r->schema().find("link target"), value, policy);
- r->commit_transaction();
- return obj;
- };
- auto create_company = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
- r->begin_transaction();
- auto obj = Object::create(d, r, *r->schema().find("person"), value, policy);
- r->commit_transaction();
- return obj;
- };
-
- SECTION("create object") {
- auto obj = create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
-
- {"bool array", AnyVec{true, false}},
- {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
- {"float array", AnyVec{1.1f, 2.2f}},
- {"double array", AnyVec{3.3, 4.4}},
- {"string array", AnyVec{"a"s, "b"s, "c"s}},
- {"data array", AnyVec{"d"s, "e"s, "f"s}},
- {"date array", AnyVec{Timestamp(10, 20), Timestamp(30, 40)}},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
- });
-
- auto row = obj.obj();
- auto link_target = *r->read_group().get_table("class_link target")->begin();
- auto table = row.get_table();
- auto target_table = link_target.get_table();
- auto array_target_table = r->read_group().get_table("class_array target");
- REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
- REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
- REQUIRE(row.get<Int>(table->get_column_key("int")) == 5);
- REQUIRE(row.get<float>(table->get_column_key("float")) == 2.2f);
- REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
- REQUIRE(row.get<String>(table->get_column_key("string")) == "hello");
- REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
- REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
- REQUIRE(row.get<ObjKey>(table->get_column_key("object")) == link_target.get_key());
-
- REQUIRE(link_target.get<Int>(target_table->get_column_key("value")) == 10);
-
- auto check_array = [&](ColKey col, auto... values) {
- auto vec = get_vector({values...});
- using U = typename decltype(vec)::value_type;
- auto list = row.get_list<U>(col);
- size_t i = 0;
- for (const auto& value : vec) {
- CAPTURE(i);
- REQUIRE(i < list.size());
- REQUIRE(value == list.get(i));
- ++i;
- }
- };
- check_array(table->get_column_key("bool array"), true, false);
- check_array(table->get_column_key("int array"), INT64_C(5), INT64_C(6));
- check_array(table->get_column_key("float array"), 1.1f, 2.2f);
- check_array(table->get_column_key("double array"), 3.3, 4.4);
- check_array(table->get_column_key("string array"), StringData("a"), StringData("b"), StringData("c"));
- check_array(table->get_column_key("data array"), BinaryData("d", 1), BinaryData("e", 1), BinaryData("f", 1));
- check_array(table->get_column_key("date array"), Timestamp(10, 20), Timestamp(30, 40));
-
- auto list = row.get_linklist_ptr(table->get_column_key("object array"));
- REQUIRE(list->size() == 1);
- REQUIRE(list->get_object(0).get<Int>(array_target_table->get_column_key("value")) == 20);
- }
-
- SECTION("create uses defaults for missing values") {
- d.defaults["all types"] = {
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
-
- {"bool array", AnyVec{true, false}},
- {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
- {"float array", AnyVec{1.1f, 2.2f}},
- {"double array", AnyVec{3.3, 4.4}},
- {"string array", AnyVec{"a"s, "b"s, "c"s}},
- {"data array", AnyVec{"d"s, "e"s, "f"s}},
- {"date array", AnyVec{}},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
- };
-
- auto obj = create(AnyDict{
- {"pk", INT64_C(1)},
- {"float", 6.6f},
- });
-
- auto row = obj.obj();
- auto table = row.get_table();
- REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
- REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
- REQUIRE(row.get<Int>(table->get_column_key("int")) == 5);
- REQUIRE(row.get<float>(table->get_column_key("float")) == 6.6f);
- REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
- REQUIRE(row.get<String>(table->get_column_key("string")) == "hello");
- REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
- REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
-
- REQUIRE(row.get_listbase_ptr(table->get_column_key("bool array"))->size() == 2);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("int array"))->size() == 2);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("float array"))->size() == 2);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("double array"))->size() == 2);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("string array"))->size() == 3);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("data array"))->size() == 3);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("date array"))->size() == 0);
- REQUIRE(row.get_listbase_ptr(table->get_column_key("object array"))->size() == 1);
- }
-
- SECTION("create can use defaults for primary key") {
- d.defaults["all types"] = {
- {"pk", INT64_C(10)},
- };
- auto obj = create(AnyDict{
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
- {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
- });
-
- auto row = obj.obj();
- REQUIRE(row.get<Int>(row.get_table()->get_column_key("pk")) == 10);
- }
-
- SECTION("create does not complain about missing values for nullable fields") {
- r->begin_transaction();
- realm::Object obj;
- REQUIRE_NOTHROW(obj = Object::create(d, r, *r->schema().find("all optional types"), util::Any(AnyDict{})));
- r->commit_transaction();
-
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "pk").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "bool").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "int").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "float").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "double").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "string").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "data").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "date").has_value());
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "data array")).size() == 0);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
- }
-
- SECTION("create throws for missing values if there is no default") {
- REQUIRE_THROWS(create(AnyDict{
- {"pk", INT64_C(1)},
- {"float", 6.6f},
- }));
- }
-
- SECTION("create always sets the PK first") {
- AnyDict value{
- {"array 1", AnyVector{AnyDict{{"value", INT64_C(1)}}}},
- {"array 2", AnyVector{AnyDict{{"value", INT64_C(2)}}}},
- {"int 1", INT64_C(0)},
- {"int 2", INT64_C(0)},
- {"pk", INT64_C(7)},
- };
- // Core will throw if the list is populated before the PK is set
- r->begin_transaction();
- REQUIRE_NOTHROW(Object::create(d, r, *r->schema().find("pk after list"), util::Any(value)));
- }
-
- SECTION("create with update") {
- CollectionChangeSet change;
- bool callback_called;
- Object obj = create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
-
- {"bool array", AnyVec{true, false}},
- {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
- {"float array", AnyVec{1.1f, 2.2f}},
- {"double array", AnyVec{3.3, 4.4}},
- {"string array", AnyVec{"a"s, "b"s, "c"s}},
- {"data array", AnyVec{"d"s, "e"s, "f"s}},
- {"date array", AnyVec{}},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
- });
-
- auto token = obj.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- callback_called = true;
- });
- advance_and_notify(*r);
-
- create(AnyDict{
- {"pk", INT64_C(1)},
- {"int", INT64_C(6)},
- {"string", "a"s},
- }, CreatePolicy::UpdateAll);
-
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(callback_called);
- REQUIRE_INDICES(change.modifications, 0);
-
- auto row = obj.obj();
- auto table = row.get_table();
- REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
- REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
- REQUIRE(row.get<Int>(table->get_column_key("int")) == 6);
- REQUIRE(row.get<float>(table->get_column_key("float")) == 2.2f);
- REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
- REQUIRE(row.get<String>(table->get_column_key("string")) == "a");
- REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
- REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
- }
-
- SECTION("create with update - only with diffs") {
- CollectionChangeSet change;
- bool callback_called;
- AnyDict adam {
- {"name", "Adam"s},
- {"age", INT64_C(32)},
- {"scores", AnyVec{INT64_C(1), INT64_C(2)}},
- };
- AnyDict brian {
- {"name", "Brian"s},
- {"age", INT64_C(33)},
- };
- AnyDict charley {
- {"name", "Charley"s},
- {"age", INT64_C(34)},
- {"team", AnyVec{adam, brian}}
- };
- AnyDict donald {
- {"name", "Donald"s},
- {"age", INT64_C(35)},
- };
- AnyDict eddie {
- {"name", "Eddie"s},
- {"age", INT64_C(36)},
- {"assistant", donald},
- {"team", AnyVec{donald, charley}}
- };
- Object obj = create_company(eddie, CreatePolicy::UpdateAll);
-
- auto table = r->read_group().get_table("class_person");
- REQUIRE(table->size() == 5);
- Results result(r, table);
- auto token = result.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- callback_called = true;
- });
- advance_and_notify(*r);
-
- // First update unconditionally
- create_company(eddie, CreatePolicy::UpdateAll);
-
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(callback_called);
- REQUIRE_INDICES(change.modifications, 0, 1, 2, 3, 4);
-
- // Now, only update where differences (there should not be any diffs - so no update)
- create_company(eddie, CreatePolicy::UpdateModified);
-
- REQUIRE(table->size() == 5);
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(!callback_called);
-
- // Now, only update sub-object)
- donald["scores"] = AnyVec{INT64_C(3), INT64_C(4), INT64_C(5)};
- // Insert the new donald
- eddie["assistant"] = donald;
- create_company(eddie, CreatePolicy::UpdateModified);
-
- REQUIRE(table->size() == 5);
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(callback_called);
- REQUIRE_INDICES(change.modifications, 1);
-
- // Shorten list
- donald["scores"] = AnyVec{INT64_C(3), INT64_C(4)};
- eddie["assistant"] = donald;
- create_company(eddie, CreatePolicy::UpdateModified);
-
- REQUIRE(table->size() == 5);
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(callback_called);
- REQUIRE_INDICES(change.modifications, 1);
- }
-
- SECTION("create with update - identical sub-object") {
- Object sub_obj = create_sub(AnyDict{{"value", INT64_C(10)}});
- Object obj = create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", sub_obj},
- });
-
- auto obj_table = r->read_group().get_table("class_all types");
- Results result(r, obj_table);
- bool callback_called;
- bool results_callback_called;
- bool sub_callback_called;
- auto token1 = obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- callback_called = true;
- });
- auto token2 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- results_callback_called = true;
- });
- auto token3 = sub_obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- sub_callback_called = true;
- });
- advance_and_notify(*r);
-
- auto table = r->read_group().get_table("class_link target");
- REQUIRE(table->size() == 1);
-
- create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
- }, CreatePolicy::UpdateModified);
-
- REQUIRE(table->size() == 1);
- callback_called = false;
- results_callback_called = false;
- sub_callback_called = false;
- advance_and_notify(*r);
- REQUIRE(!callback_called);
- REQUIRE(!results_callback_called);
- REQUIRE(!sub_callback_called);
-
- // Now change sub object
- create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(11)}}},
- }, CreatePolicy::UpdateModified);
-
- callback_called = false;
- results_callback_called = false;
- sub_callback_called = false;
- advance_and_notify(*r);
- REQUIRE(!callback_called);
- REQUIRE(results_callback_called);
- REQUIRE(sub_callback_called);
- }
-
- SECTION("create with update - identical array of sub-objects") {
- bool callback_called;
- auto dict = AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}, AnyDict{{"value", INT64_C(21)}}}},
- };
- Object obj = create(dict);
-
- auto obj_table = r->read_group().get_table("class_all types");
- Results result(r, obj_table);
- auto token1 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- callback_called = true;
- });
- advance_and_notify(*r);
-
- create(dict, CreatePolicy::UpdateModified);
-
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(!callback_called);
-
- // Now change list
- dict["object array"] = AnyVec{AnyDict{{"value", INT64_C(23)}}};
- create(dict, CreatePolicy::UpdateModified);
-
- callback_called = false;
- advance_and_notify(*r);
- REQUIRE(callback_called);
- }
-
- for (auto policy : {CreatePolicy::UpdateAll, CreatePolicy::UpdateModified}) {
- SECTION("set existing fields to null with update "s + (policy == CreatePolicy::UpdateModified ? "(diffed)" : "(all)")) {
- AnyDict initial_values{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
-
- {"bool array", AnyVec{true, false}},
- {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
- {"float array", AnyVec{1.1f, 2.2f}},
- {"double array", AnyVec{3.3, 4.4}},
- {"string array", AnyVec{"a"s, "b"s, "c"s}},
- {"data array", AnyVec{"d"s, "e"s, "f"s}},
- {"date array", AnyVec{}},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
- };
- r->begin_transaction();
- auto obj = Object::create(d, r, *r->schema().find("all optional types"), util::Any(initial_values));
-
- // Missing fields in dictionary do not update anything
- Object::create(d, r, *r->schema().find("all optional types"),
- util::Any(AnyDict{{"pk", INT64_C(1)}}), policy);
-
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 2.2f);
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 3.3);
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "hello");
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(10, 20));
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == true);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == 5);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == 1.1f);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == 3.3);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == "a");
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
-
- // Set all properties to null
- AnyDict null_values{
- {"pk", INT64_C(1)},
- {"bool", util::Any()},
- {"int", util::Any()},
- {"float", util::Any()},
- {"double", util::Any()},
- {"string", util::Any()},
- {"data", util::Any()},
- {"date", util::Any()},
-
- {"bool array", AnyVec{util::Any()}},
- {"int array", AnyVec{util::Any()}},
- {"float array", AnyVec{util::Any()}},
- {"double array", AnyVec{util::Any()}},
- {"string array", AnyVec{util::Any()}},
- {"data array", AnyVec{util::Any()}},
- {"date array", AnyVec{Timestamp()}},
- };
- Object::create(d, r, *r->schema().find("all optional types"), util::Any(null_values), policy);
-
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "bool").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "int").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "float").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "double").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "string").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "data").has_value());
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "date").has_value());
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == util::none);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == util::none);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == util::none);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == util::none);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == StringData());
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "data array")).get<BinaryData>(0) == BinaryData());
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).get<Timestamp>(0) == Timestamp());
-
- // Set all properties back to non-null
- Object::create(d, r, *r->schema().find("all optional types"), util::Any(initial_values), policy);
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 2.2f);
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 3.3);
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "hello");
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(10, 20));
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == true);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == 5);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == 1.1f);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == 3.3);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == "a");
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
- }
- }
-
- SECTION("create throws for duplicate pk if update is not specified") {
- create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
- {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
- });
- REQUIRE_THROWS(create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
- {"object", AnyDict{{"value", INT64_C(10)}}},
- {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
- }));
- }
-
- SECTION("create with explicit null pk does not fall back to default") {
- d.defaults["nullable int pk"] = {
- {"pk", INT64_C(10)},
- };
- d.defaults["nullable string pk"] = {
- {"pk", "value"s},
- };
- auto create = [&](util::Any&& value, StringData type) {
- r->begin_transaction();
- auto obj = Object::create(d, r, *r->schema().find(type), value);
- r->commit_transaction();
- return obj;
- };
-
- auto obj = create(AnyDict{{"pk", d.null_value()}}, "nullable int pk");
- auto col_pk_int = r->read_group().get_table("class_nullable int pk")->get_column_key("pk");
- auto col_pk_str = r->read_group().get_table("class_nullable string pk")->get_column_key("pk");
- REQUIRE(obj.obj().is_null(col_pk_int));
- obj = create(AnyDict{{"pk", d.null_value()}}, "nullable string pk");
- REQUIRE(obj.obj().is_null(col_pk_str));
-
- obj = create(AnyDict{{}}, "nullable int pk");
- REQUIRE(obj.obj().get<util::Optional<Int>>(col_pk_int) == 10);
- obj = create(AnyDict{{}}, "nullable string pk");
- REQUIRE(obj.obj().get<String>(col_pk_str) == "value");
- }
-
- SECTION("getters and setters") {
- r->begin_transaction();
-
- auto table = r->read_group().get_table("class_all types");
- table->create_object();
- Object obj(r, *r->schema().find("all types"), *table->begin());
-
- auto link_table = r->read_group().get_table("class_link target");
- link_table->create_object();
- Object linkobj(r, *r->schema().find("link target"), *link_table->begin());
-
- obj.set_property_value(d, "bool", util::Any(true));
- REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
-
- obj.set_property_value(d, "int", util::Any(INT64_C(5)));
- REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
-
- obj.set_property_value(d, "float", util::Any(1.23f));
- REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 1.23f);
-
- obj.set_property_value(d, "double", util::Any(1.23));
- REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 1.23);
-
- obj.set_property_value(d, "string", util::Any("abc"s));
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "abc");
-
- obj.set_property_value(d, "data", util::Any("abc"s));
- REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "data")) == "abc");
-
- obj.set_property_value(d, "date", util::Any(Timestamp(1, 2)));
- REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(1, 2));
-
- REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "object").has_value());
- obj.set_property_value(d, "object", util::Any(linkobj));
- REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(d, "object")).obj().get_key() == linkobj.obj().get_key());
-
- auto linking = any_cast<Results>(linkobj.get_property_value<util::Any>(d, "origin"));
- REQUIRE(linking.size() == 1);
-
- REQUIRE_THROWS(obj.set_property_value(d, "pk", util::Any(INT64_C(5))));
- REQUIRE_THROWS(obj.set_property_value(d, "not a property", util::Any(INT64_C(5))));
-
- r->commit_transaction();
-
- REQUIRE_THROWS(obj.get_property_value<util::Any>(d, "not a property"));
- REQUIRE_THROWS(obj.set_property_value(d, "int", util::Any(INT64_C(5))));
- }
-
- SECTION("list property self-assign is a no-op") {
- auto obj = create(AnyDict{
- {"pk", INT64_C(1)},
- {"bool", true},
- {"int", INT64_C(5)},
- {"float", 2.2f},
- {"double", 3.3},
- {"string", "hello"s},
- {"data", "olleh"s},
- {"date", Timestamp(10, 20)},
-
- {"bool array", AnyVec{true, false}},
- {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
- });
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 2);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "object array")).size() == 1);
-
- r->begin_transaction();
- obj.set_property_value(d, "bool array", obj.get_property_value<util::Any>(d, "bool array"));
- obj.set_property_value(d, "object array", obj.get_property_value<util::Any>(d, "object array"));
- r->commit_transaction();
-
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 2);
- REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "object array")).size() == 1);
- }
-
-#if REALM_ENABLE_SYNC
- if (!util::EventLoop::has_implementation())
- return;
-
- SyncServer server(false);
- SyncTestFile config1(server, "shared");
- config1.schema = config.schema;
- SyncTestFile config2(server, "shared");
- config2.schema = config.schema;
-
- SECTION("defaults do not override values explicitly passed to create()") {
- AnyDict v1{
- {"pk", INT64_C(7)},
- {"array 1", AnyVector{AnyDict{{"value", INT64_C(1)}}}},
- {"array 2", AnyVector{AnyDict{{"value", INT64_C(2)}}}},
- };
- auto v2 = v1;
- v1["int 1"] = INT64_C(1);
- v2["int 2"] = INT64_C(2);
-
- auto r1 = Realm::get_shared_realm(config1);
- auto r2 = Realm::get_shared_realm(config2);
-
- TestContext c1(r1);
- TestContext c2(r2);
-
- c1.defaults["pk after list"] = {
- {"int 1", INT64_C(10)},
- {"int 2", INT64_C(10)},
- };
- c2.defaults = c1.defaults;
-
- r1->begin_transaction();
- r2->begin_transaction();
- auto object1 = Object::create(c1, r1, *r1->schema().find("pk after list"), util::Any(v1));
- auto object2 = Object::create(c2, r2, *r2->schema().find("pk after list"), util::Any(v2));
- r2->commit_transaction();
- r1->commit_transaction();
-
- server.start();
- util::EventLoop::main().run_until([&] {
- return r1->read_group().get_table("class_array target")->size() == 4;
- });
-
- Obj obj = object1.obj();
- REQUIRE(obj.get<Int>("pk") == 7); // pk
- REQUIRE(obj.get_linklist("array 1").size() == 2);
- REQUIRE(obj.get<Int>("int 1") == 1); // non-default from r1
- REQUIRE(obj.get<Int>("int 2") == 2); // non-default from r2
- REQUIRE(obj.get_linklist("array 2").size() == 2);
-
- }
-#endif
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp
deleted file mode 100644
index 8a0fed935..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/test_file.hpp"
-
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-
-#include <realm/string_data.hpp>
-#include <realm/table.hpp>
-
-using namespace realm;
-
-TEST_CASE("ObjectStore: table_name_for_object_type()") {
- SECTION("should work with strings that aren't null-terminated") {
- auto input = StringData("good_no_bad", 4);
- auto result = ObjectStore::table_name_for_object_type(input);
- REQUIRE(result == "class_good");
- }
-}
-
-TEST_CASE("ObjectStore:: property_for_column_index()") {
- SECTION("Property should match the schema") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int},
- {"boolNullable", PropertyType::Bool | PropertyType::Nullable},
- {"stringPK", PropertyType::String, true},
- {"dateNullableIndexed", PropertyType::Date | PropertyType::Nullable, false, true},
- {"floatNullableArray", PropertyType::Float | PropertyType::Nullable | PropertyType::Array},
- {"doubleArray", PropertyType::Double | PropertyType::Array},
- {"object", PropertyType::Object | PropertyType::Nullable, "object"},
- {"objectArray", PropertyType::Object | PropertyType::Array, "object"},
- }}
- };
-
- TestFile config;
- config.schema = schema;
- config.schema_version = 1;
-
- auto realm = Realm::get_shared_realm(config);
- ConstTableRef table = ObjectStore::table_for_object_type(realm->read_group(), "object");
- auto it = realm->schema().find("object");
- REQUIRE_FALSE(it == realm->schema().end());
- ObjectSchema object_schema = *it;
-
- auto all_columns = table->get_column_keys();
- for (auto col : all_columns) {
- auto property = ObjectStore::property_for_column_index(table, col);
- if (!property) {
- FAIL();
- continue;
- }
- auto actual_property = *object_schema.property_for_name(property->name);
- REQUIRE(property.value() == actual_property);
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp
deleted file mode 100644
index bfec4ef09..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp
+++ /dev/null
@@ -1,857 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "catch2/catch.hpp"
-
-#include "util/event_loop.hpp"
-#include "util/index_helpers.hpp"
-#include "util/test_file.hpp"
-
-#include "binding_context.hpp"
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "thread_safe_reference.hpp"
-
-#include "impl/realm_coordinator.hpp"
-#include "impl/object_accessor_impl.hpp"
-
-#include <realm/db.hpp>
-#include <realm/query_expression.hpp>
-#include <realm/version.hpp>
-
-#include <numeric>
-
-using namespace realm;
-
-
-template<PropertyType prop_type, typename T>
-struct Base {
- using Type = T;
- using Wrapped = T;
- using Boxed = T;
- enum { is_optional = false };
-
- static PropertyType property_type() { return prop_type; }
- static util::Any to_any(T value) { return value; }
-
- template<typename Fn>
- static auto unwrap(T value, Fn&& fn) { return fn(value); }
-
- static T min() { abort(); }
- static T max() { abort(); }
- static T sum() { abort(); }
- static double average() { abort(); }
-
- static bool can_sum() { return std::is_arithmetic<T>::value; }
- static bool can_average() { return std::is_arithmetic<T>::value; }
- static bool can_minmax() { return std::is_arithmetic<T>::value; }
-};
-
-struct Int : Base<PropertyType::Int, int64_t> {
- static std::vector<int64_t> values() { return {3, 1, 2}; }
- static int64_t min() { return 1; }
- static int64_t max() { return 3; }
- static int64_t sum() { return 6; }
- static double average() { return 2.0; }
-};
-
-struct Bool : Base<PropertyType::Bool, bool> {
- static std::vector<bool> values() { return {true, false}; }
- static bool can_sum() { return false; }
- static bool can_average() { return false; }
- static bool can_minmax() { return false; }
-};
-
-struct Float : Base<PropertyType::Float, float> {
- static std::vector<float> values() { return {3.3f, 1.1f, 2.2f}; }
- static float min() { return 1.1f; }
- static float max() { return 3.3f; }
- static auto sum() { return Approx(6.6f); }
- static auto average() { return Approx(2.2f); }
-};
-
-struct Double : Base<PropertyType::Double, double> {
- static std::vector<double> values() { return {3.3, 1.1, 2.2}; }
- static double min() { return 1.1; }
- static double max() { return 3.3; }
- static auto sum() { return Approx(6.6); }
- static auto average() { return Approx(2.2); }
-};
-
-struct String : Base<PropertyType::String, StringData> {
- using Boxed = std::string;
- static std::vector<StringData> values() { return {"c", "a", "b"}; }
- static util::Any to_any(StringData value) { return value ? std::string(value) : util::Any(); }
-};
-
-struct Binary : Base<PropertyType::Data, BinaryData> {
- using Boxed = std::string;
- static util::Any to_any(BinaryData value) { return value ? std::string(value) : util::Any(); }
- static std::vector<BinaryData> values()
- {
- return {BinaryData("c", 1), BinaryData("a", 1), BinaryData("b", 1)};
- }
-};
-
-struct Date : Base<PropertyType::Date, Timestamp> {
- static std::vector<Timestamp> values()
- {
- return {Timestamp(3, 3), Timestamp(1, 1), Timestamp(2, 2)};
- }
- static bool can_minmax() { return true; }
- static Timestamp min() { return Timestamp(1, 1); }
- static Timestamp max() { return Timestamp(3, 3); }
-};
-
-template<typename BaseT>
-struct BoxedOptional : BaseT {
- using Type = util::Optional<typename BaseT::Type>;
- using Boxed = Type;
- enum { is_optional = true };
-
- static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
- static std::vector<Type> values()
- {
- std::vector<Type> ret;
- for (auto v : BaseT::values())
- ret.push_back(Type(v));
- ret.push_back(util::none);
- return ret;
- }
- static auto unwrap(Type value) { return *value; }
- static util::Any to_any(Type value) { return value ? util::Any(*value) : util::Any(); }
-
- template<typename Fn>
- static auto unwrap(Type value, Fn&& fn) { return value ? fn(*value) : fn(null()); }
-};
-
-template<typename BaseT>
-struct UnboxedOptional : BaseT {
- enum { is_optional = true };
- static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
- static auto values() -> decltype(BaseT::values())
- {
- auto ret = BaseT::values();
- ret.push_back(typename BaseT::Type());
- return ret;
- }
-};
-
-template<typename T>
-T get(Mixed) { abort(); }
-
-template<> int64_t get(Mixed m) { return m.get_int(); }
-template<> float get(Mixed m) { return m.get_type() == type_Float ? m.get_float() : static_cast<float>(m.get_double()); }
-template<> double get(Mixed m) { return m.get_double(); }
-template<> Timestamp get(Mixed m) { return m.get_timestamp(); }
-
-namespace realm {
-template<typename T>
-bool operator==(List const& list, std::vector<T> const& values) {
- if (list.size() != values.size())
- return false;
- for (size_t i = 0; i < values.size(); ++i) {
- if (list.get<T>(i) != values[i])
- return false;
- }
- return true;
-}
-
-template<typename T>
-bool operator==(Results const& results, std::vector<T> const& values) {
- // FIXME: this is only necessary because Results::size() and ::get() are not const
- Results copy{results};
- if (copy.size() != values.size())
- return false;
- for (size_t i = 0; i < values.size(); ++i) {
- if (copy.get<T>(i) != values[i])
- return false;
- }
- return true;
-}
-
-}
-
-struct StringifyingContext {
- template<typename T>
- std::string box(T value)
- {
- std::stringstream ss;
- ss << value;
- return ss.str();
- }
-
- std::string box(Obj obj) { return util::to_string(obj.get_key().value); }
-};
-
-namespace Catch {
-template<>
-struct StringMaker<List> {
- static std::string convert(List const& list)
- {
- std::stringstream ss;
- auto type = list.get_type();
- ss << string_for_property_type(type & ~PropertyType::Flags);
- if (is_nullable(type))
- ss << "?";
- ss << "{";
-
- StringifyingContext ctx;
- for (size_t i = 0, count = list.size(); i < count; ++i)
- ss << list.get(ctx, i) << ", ";
- auto str = ss.str();
- str.pop_back();
- str.back() = '}';
- return str;
- }
-};
-template<>
-struct StringMaker<Results> {
- static std::string convert(Results const& r)
- {
- auto& results = const_cast<Results&>(r);
- std::stringstream ss;
- auto type = results.get_type();
- ss << string_for_property_type(type & ~PropertyType::Flags);
- if (is_nullable(type))
- ss << "?";
- ss << "{";
-
- StringifyingContext ctx;
- for (size_t i = 0, count = results.size(); i < count; ++i)
- ss << results.get(ctx, i) << ", ";
- auto str = ss.str();
- str.pop_back();
- str.back() = '}';
- return str;
- }
-};
-template<>
-struct StringMaker<util::None> {
- static std::string convert(util::None)
- {
- return "[none]";
- }
-};
-} // namespace Catch
-
-struct less {
- template<class T, class U>
- auto operator()(T&& a, U&& b) const noexcept { return a < b; }
-};
-struct greater {
- template<class T, class U>
- auto operator()(T&& a, U&& b) const noexcept { return a > b; }
-};
-
-template<>
-auto less::operator()<Timestamp&, Timestamp&>(Timestamp& a, Timestamp& b) const noexcept
-{
- if (b.is_null())
- return false;
- if (a.is_null())
- return true;
- return a < b;
-}
-
-template<>
-auto greater::operator()<Timestamp&, Timestamp&>(Timestamp& a, Timestamp& b) const noexcept
-{
- if (a.is_null())
- return false;
- if (b.is_null())
- return true;
- return a > b;
-}
-
-TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::Double, ::String, ::Binary, ::Date,
- BoxedOptional<::Int>, BoxedOptional<::Bool>, BoxedOptional<::Float>, BoxedOptional<::Double>,
- UnboxedOptional<::String>, UnboxedOptional<::Binary>, UnboxedOptional<::Date>)
-{
- auto values = TestType::values();
- using T = typename TestType::Type;
- using W = typename TestType::Wrapped;
- using Boxed = typename TestType::Boxed;
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Array|TestType::property_type()}
- }},
- };
- auto r = Realm::get_shared_realm(config);
- auto r2 = Realm::get_shared_realm(config);
-
- auto table = r->read_group().get_table("class_object");
- auto table2 = r2->read_group().get_table("class_object");
- r->begin_transaction();
- Obj obj = table->create_object();
- ColKey col = table->get_column_key("value");
-
- List list(r, obj, col);
- auto results = list.as_results();
- CppContext ctx(r);
-
- SECTION("get_realm()") {
- REQUIRE(list.get_realm() == r);
- REQUIRE(results.get_realm() == r);
- }
-#if 0
- SECTION("get_query()") {
- REQUIRE(list.get_query().count() == 0);
- REQUIRE(results.get_query().count() == 0);
- list.add(static_cast<T>(values[0]));
- REQUIRE(list.get_query().count() == 1);
- REQUIRE(results.get_query().count() == 1);
- }
-#endif
- SECTION("get_origin_row_index()") {
- REQUIRE(list.get_parent_object_key() == obj.get_key());
- table->create_object();
- REQUIRE(list.get_parent_object_key() == obj.get_key());
- }
-
- SECTION("get_type()") {
- REQUIRE(list.get_type() == TestType::property_type());
- REQUIRE(results.get_type() == TestType::property_type());
- }
-
- SECTION("get_object_type()") {
- REQUIRE(results.get_object_type() == StringData());
- }
-
- SECTION("is_valid()") {
- REQUIRE(list.is_valid());
- REQUIRE(results.is_valid());
-
- SECTION("invalidate") {
- r->invalidate();
- REQUIRE_FALSE(list.is_valid());
- REQUIRE_FALSE(results.is_valid());
- }
-
- SECTION("close") {
- r->close();
- REQUIRE_FALSE(list.is_valid());
- REQUIRE_FALSE(results.is_valid());
- }
-
- SECTION("delete row") {
- obj.remove();
- REQUIRE_FALSE(list.is_valid());
- REQUIRE_FALSE(results.is_valid());
- }
-
- SECTION("rollback transaction creating list") {
- r->cancel_transaction();
- REQUIRE_FALSE(list.is_valid());
- REQUIRE_FALSE(results.is_valid());
- }
- }
-
- SECTION("verify_attached()") {
- REQUIRE_NOTHROW(list.verify_attached());
-
- SECTION("invalidate") {
- r->invalidate();
- REQUIRE_THROWS(list.verify_attached());
- }
-
- SECTION("close") {
- r->close();
- REQUIRE_THROWS(list.verify_attached());
- }
-
- SECTION("delete row") {
- obj.remove();
- REQUIRE_THROWS(list.verify_attached());
- }
-
- SECTION("rollback transaction creating list") {
- r->cancel_transaction();
- REQUIRE_THROWS(list.verify_attached());
- }
- }
-
- SECTION("verify_in_transaction()") {
- REQUIRE_NOTHROW(list.verify_in_transaction());
-
- SECTION("invalidate") {
- r->invalidate();
- REQUIRE_THROWS(list.verify_in_transaction());
- }
-
- SECTION("close") {
- r->close();
- REQUIRE_THROWS(list.verify_in_transaction());
- }
-
- SECTION("delete row") {
- obj.remove();
- REQUIRE_THROWS(list.verify_in_transaction());
- }
-
- SECTION("end write") {
- r->commit_transaction();
- REQUIRE_THROWS(list.verify_in_transaction());
- }
- }
-
- if (!list.is_valid() || !r->is_in_transaction())
- return;
-
- for (T value : values)
- list.add(value);
-
- SECTION("move()") {
- if (list.size() < 3)
- return;
-
- list.move(1, 2);
- std::swap(values[1], values[2]);
- REQUIRE(list == values);
- REQUIRE(results == values);
-
- list.move(2, 1);
- std::swap(values[1], values[2]);
- REQUIRE(list == values);
- REQUIRE(results == values);
-
- list.move(0, 2);
- std::rotate(values.begin(), values.begin() + 1, values.begin() + 3);
- REQUIRE(list == values);
- REQUIRE(results == values);
-
- list.move(2, 0);
- std::rotate(values.begin(), values.begin() + 2, values.begin() + 3);
- REQUIRE(list == values);
- REQUIRE(results == values);
- }
-
- SECTION("remove()") {
- if (list.size() < 3)
- return;
-
- list.remove(1);
- values.erase(values.begin() + 1);
- REQUIRE(list == values);
- REQUIRE(results == values);
- }
-
- SECTION("remove_all()") {
- list.remove_all();
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 0);
- }
-
- SECTION("swap()") {
- if (list.size() < 3)
- return;
-
- list.swap(0, 2);
- std::swap(values[0], values[2]);
- REQUIRE(list == values);
- REQUIRE(results == values);
- }
-
- SECTION("delete_all()") {
- list.delete_all();
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 0);
- }
-
- SECTION("clear()") {
- results.clear();
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 0);
- }
-
- SECTION("get()") {
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- REQUIRE(list.get<T>(i) == values[i]);
- REQUIRE(results.get<T>(i) == values[i]);
- REQUIRE(any_cast<Boxed>(list.get(ctx, i)) == Boxed(values[i]));
- REQUIRE(any_cast<Boxed>(results.get(ctx, i)) == Boxed(values[i]));
- }
- REQUIRE_THROWS(list.get<T>(values.size()));
- REQUIRE_THROWS(results.get<T>(values.size()));
- REQUIRE_THROWS(list.get(ctx, values.size()));
- REQUIRE_THROWS(results.get(ctx, values.size()));
- }
-
- SECTION("first()") {
- REQUIRE(*results.first<T>() == values.front());
- REQUIRE(any_cast<Boxed>(*results.first(ctx)) == Boxed(values.front()));
- list.remove_all();
- REQUIRE(results.first<T>() == util::none);
- }
-
- SECTION("last()") {
- REQUIRE(*results.last<T>() == values.back());
- list.remove_all();
- REQUIRE(results.last<T>() == util::none);
- }
-
- SECTION("set()") {
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- auto rev = values.size() - i - 1;
- list.set(i, static_cast<T>(values[rev]));
- REQUIRE(list.get<T>(i) == values[rev]);
- REQUIRE(results.get<T>(i) == values[rev]);
- }
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- list.set(ctx, i, TestType::to_any(values[i]));
- REQUIRE(list.get<T>(i) == values[i]);
- REQUIRE(results.get<T>(i) == values[i]);
- }
-
- REQUIRE_THROWS(list.set(list.size(), static_cast<T>(values[0])));
- }
-
- SECTION("find()") {
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- REQUIRE(list.find<T>(values[i]) == i);
- REQUIRE(results.index_of<T>(values[i]) == i);
-
- REQUIRE(list.find(ctx, TestType::to_any(values[i])) == i);
- REQUIRE(results.index_of(ctx, TestType::to_any(values[i])) == i);
-#if 0
- auto q = TestType::unwrap(values[i], [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) == v; });
- REQUIRE(list.find(Query(q)) == i);
- REQUIRE(results.index_of(std::move(q)) == i);
-#endif
- }
-
- list.remove(0);
- REQUIRE(list.find(static_cast<T>(values[0])) == npos);
- REQUIRE(results.index_of(static_cast<T>(values[0])) == npos);
-
- REQUIRE(list.find(ctx, TestType::to_any(values[0])) == npos);
- REQUIRE(results.index_of(ctx, TestType::to_any(values[0])) == npos);
- }
- SECTION("sorted index_of()") {
- auto sorted = list.sort({{"self", true}});
- std::sort(begin(values), end(values), less());
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- REQUIRE(sorted.index_of<T>(values[i]) == i);
- }
-
- sorted = list.sort({{"self", false}});
- std::sort(begin(values), end(values), greater());
- for (size_t i = 0; i < values.size(); ++i) {
- CAPTURE(i);
- REQUIRE(sorted.index_of<T>(values[i]) == i);
- }
- }
-
-#if 0
- SECTION("filtered index_of()") {
- REQUIRE_THROWS(results.index_of(table->get(0)));
- auto q = TestType::unwrap(values[0], [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) != v; });
- auto filtered = list.filter(std::move(q));
- for (size_t i = 1; i < values.size(); ++i) {
- CAPTURE(i);
- REQUIRE(filtered.index_of(static_cast<T>(values[i])) == i - 1);
- }
- }
-#endif
- SECTION("sort()") {
- auto unsorted = list.sort(std::vector<std::pair<std::string, bool>>{});
- REQUIRE(unsorted == values);
-
- auto sorted = list.sort(SortDescriptor({{col}}, {true}));
- auto sorted2 = list.sort({{"self", true}});
- std::sort(begin(values), end(values), less());
- REQUIRE(sorted == values);
- REQUIRE(sorted2 == values);
-
- sorted = list.sort(SortDescriptor({{col}}, {false}));
- sorted2 = list.sort({{"self", false}});
- std::sort(begin(values), end(values), greater());
- REQUIRE(sorted == values);
- REQUIRE(sorted2 == values);
-
- auto execption_string =
- util::format("Cannot sort on key path 'not self': arrays of '%1' can only be sorted on 'self'",
- string_for_property_type(TestType::property_type() & ~PropertyType::Flags));
- REQUIRE_THROWS_WITH(list.sort({{"not self", true}}), execption_string);
- REQUIRE_THROWS_WITH(list.sort({{"self", true}, {"self", false}}),
- util::format("Cannot sort array of '%1' on more than one key path",
- string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
- }
-
- SECTION("distinct()") {
- for (T value : values)
- list.add(value);
- auto values2 = values;
- values2.insert(values2.end(), values.begin(), values.end());
-
- auto undistinct = list.as_results().distinct(std::vector<std::string>{});
- REQUIRE(undistinct == values2);
-
- auto distinct = results.distinct(DistinctDescriptor({{col}}));
- auto distinct2 = results.distinct({"self"});
- REQUIRE(distinct == values);
- REQUIRE(distinct2 == values);
-
- REQUIRE_THROWS_WITH(results.distinct({{"not self"}}),
- util::format("Cannot sort on key path 'not self': arrays of '%1' can only be sorted on 'self'",
- string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
- REQUIRE_THROWS_WITH(results.distinct({{"self"}, {"self"}}),
- util::format("Cannot sort array of '%1' on more than one key path",
- string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
- }
-
-#if 0
- SECTION("filter()") {
- T v = values.front();
- values.erase(values.begin());
-
- auto q = TestType::unwrap(v, [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) != v; });
- Results filtered = list.filter(std::move(q));
- REQUIRE(filtered == values);
-
- q = TestType::unwrap(v, [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) == v; });
- filtered = list.filter(std::move(q));
- REQUIRE(filtered.size() == 1);
- REQUIRE(*filtered.first<T>() == v);
- }
-#endif
-
- SECTION("min()") {
- if (!TestType::can_minmax()) {
- REQUIRE_THROWS(list.min());
- REQUIRE_THROWS(results.min());
- return;
- }
-
- REQUIRE(get<W>(*list.min()) == TestType::min());
- REQUIRE(get<W>(*results.min()) == TestType::min());
- list.remove_all();
- REQUIRE(list.min() == util::none);
- REQUIRE(results.min() == util::none);
- }
-
- SECTION("max()") {
- if (!TestType::can_minmax()) {
- REQUIRE_THROWS(list.max());
- REQUIRE_THROWS(results.max());
- return;
- }
-
- REQUIRE(get<W>(*list.max()) == TestType::max());
- REQUIRE(get<W>(*results.max()) == TestType::max());
- list.remove_all();
- REQUIRE(list.max() == util::none);
- REQUIRE(results.max() == util::none);
- }
-
- SECTION("sum()") {
- if (!TestType::can_sum()) {
- REQUIRE_THROWS(list.sum());
- return;
- }
-
- REQUIRE(get<W>(list.sum()) == TestType::sum());
- REQUIRE(get<W>(*results.sum()) == TestType::sum());
- list.remove_all();
- REQUIRE(get<W>(list.sum()) == W{});
- REQUIRE(get<W>(*results.sum()) == W{});
- }
-
- SECTION("average()") {
- if (!TestType::can_average()) {
- REQUIRE_THROWS(list.average());
- return;
- }
-
- REQUIRE(*list.average() == TestType::average());
- REQUIRE(*results.average() == TestType::average());
- list.remove_all();
- REQUIRE(list.average() == util::none);
- REQUIRE(results.average() == util::none);
- }
-
- SECTION("operator==()") {
- Obj obj1 = table->create_object();
- REQUIRE(list == List(r, obj, col));
- REQUIRE_FALSE(list == List(r, obj1, col));
- }
-
- SECTION("hash") {
- Obj obj1 = table->create_object();
- std::hash<List> h;
- REQUIRE(h(list) == h(List(r, obj, col)));
- REQUIRE_FALSE(h(list) == h(List(r, obj1, col)));
- }
-
- SECTION("handover") {
- r->commit_transaction();
-
- auto list2 = ThreadSafeReference(list).resolve<List>(r);
- REQUIRE(list == list2);
- auto results2 = ThreadSafeReference(results).resolve<Results>(r);
- REQUIRE(results2 == values);
- }
-
- SECTION("notifications") {
- r->commit_transaction();
-
- auto sorted = results.sort({{"self", true}});
-
- size_t calls = 0;
- CollectionChangeSet change, rchange, srchange;
- auto token = list.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- change = c;
- ++calls;
- });
- auto rtoken = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- rchange = c;
- ++calls;
- });
- auto srtoken = sorted.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- srchange = c;
- ++calls;
- });
-
- SECTION("add value to list") {
- // Remove the existing copy of this value so that the sorted list
- // doesn't have dupes resulting in an unstable order
- advance_and_notify(*r);
- r->begin_transaction();
- list.remove(0);
- r->commit_transaction();
-
- advance_and_notify(*r);
- r->begin_transaction();
- list.insert(0, static_cast<T>(values[0]));
- r->commit_transaction();
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.insertions, 0);
- REQUIRE_INDICES(rchange.insertions, 0);
- // values[0] is max(), so it ends up at the end of the sorted list
- REQUIRE_INDICES(srchange.insertions, values.size() - 1);
- }
-
- SECTION("remove value from list") {
- advance_and_notify(*r);
- r->begin_transaction();
- list.remove(1);
- r->commit_transaction();
-
- advance_and_notify(*r);
- REQUIRE_INDICES(change.deletions, 1);
- REQUIRE_INDICES(rchange.deletions, 1);
- // values[1] is min(), so it's index 0 for non-optional and 1 for
- // optional (as nulls sort to the front)
- REQUIRE_INDICES(srchange.deletions, TestType::is_optional);
- }
-
- SECTION("clear list") {
- advance_and_notify(*r);
-
- r->begin_transaction();
- list.remove_all();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(change.deletions.count() == values.size());
- REQUIRE(rchange.deletions.count() == values.size());
- REQUIRE(srchange.deletions.count() == values.size());
- }
-
- SECTION("delete containing row") {
- advance_and_notify(*r);
- REQUIRE(calls == 3);
-
- r->begin_transaction();
- obj.remove();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(calls == 6);
- REQUIRE(change.deletions.count() == values.size());
- REQUIRE(rchange.deletions.count() == values.size());
- REQUIRE(srchange.deletions.count() == values.size());
-
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(calls == 6);
- }
-
- SECTION("deleting containing row before first run of notifier") {
- r2->begin_transaction();
- table2->begin()->remove();
- r2->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(change.deletions.count() == values.size());
- }
- }
-
-#if REALM_ENABLE_SYNC && REALM_HAVE_SYNC_STABLE_IDS
- SECTION("sync compatibility") {
- if (!util::EventLoop::has_implementation())
- return;
-
- SyncServer server;
- SyncTestFile sync_config(server, "shared");
- sync_config.schema = config.schema;
- sync_config.schema_version = 0;
-
- {
- auto r = Realm::get_shared_realm(sync_config);
- r->begin_transaction();
-
- CppContext ctx(r);
- auto obj = Object::create(ctx, r, *r->schema().find("object"), util::Any(AnyDict{}));
- auto list = any_cast<List>(obj.get_property_value<util::Any>(ctx, "value"));
- list.add(static_cast<T>(values[0]));
-
- r->commit_transaction();
- wait_for_upload(*r);
- }
-
- util::File::remove(sync_config.path);
-
- {
- auto r = Realm::get_shared_realm(sync_config);
- auto table = r->read_group().get_table("class_object");
-
- util::EventLoop::main().run_until([&] {
- return table->size() == 1;
- });
-
- CppContext ctx(r);
- Object obj(r, "object", 0);
- auto list = any_cast<List>(obj.get_property_value<util::Any>(ctx, "value"));
- REQUIRE(list.get<T>(0) == values[0]);
- }
- }
-#endif
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/query.json b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/query.json
deleted file mode 100644
index 6cbbee55a..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/query.json
+++ /dev/null
@@ -1,381 +0,0 @@
-{
-
-"dateTests" : {
- "schema" : [{
- "name": "DateObject",
- "properties": [{ "name": "date", "type": "date" }]
- }],
- "objects": [
- { "type": "DateObject", "value": [10000] },
- { "type": "DateObject", "value": [10001] },
- { "type": "DateObject", "value": [10002] }
- ],
- "tests": [
- ["QueryCount", 2, "DateObject", "date < $0", [2, "date"]],
- ["QueryCount", 3, "DateObject", "date <= $0", [2, "date"]],
- ["QueryCount", 2, "DateObject", "date > $0", [0, "date"]],
- ["QueryCount", 3, "DateObject", "date >= $0", [0, "date"]],
- ["QueryCount", 1, "DateObject", "date == $0", [0, "date"]],
- ["QueryCount", 2, "DateObject", "date != $0", [0, "date"]],
-
- ["QueryThrows", "DateObject", "date == 'not a date'"],
- ["QueryThrows", "DateObject", "date == 1"],
- ["QueryThrows", "DateObject", "date == $0", 1]
- ]
-},
-
-"boolTests" : {
- "schema" : [{
- "name": "BoolObject",
- "properties": [{ "name": "boolCol", "type": "bool" }]
- }],
- "objects": [
- { "type": "BoolObject", "value": [false] },
- { "type": "BoolObject", "value": [true] },
- { "type": "BoolObject", "value": [true] }
- ],
- "tests": [
- ["QueryCount", 2, "BoolObject", "boolCol == true"],
- ["QueryCount", 1, "BoolObject", "boolCol==false"],
- ["QueryCount", 1, "BoolObject", "boolCol != true"],
- ["QueryCount", 2, "BoolObject", "true == boolCol"],
- ["QueryCount", 2, "BoolObject", "boolCol == TRUE"],
- ["QueryCount", 1, "BoolObject", "boolCol == FALSE"],
- ["QueryCount", 2, "BoolObject", "boolCol == $0", true],
- ["QueryCount", 1, "BoolObject", "boolCol == $0", false],
- ["QueryCount", 0, "BoolObject", "boolCol == true && boolCol == false"],
- ["QueryCount", 3, "BoolObject", "boolCol == true || boolCol == false"],
-
- ["QueryThrows", "BoolObject", "boolCol == 0"],
- ["QueryThrows", "BoolObject", "boolCol == 1"],
- ["QueryThrows", "BoolObject", "boolCol == 'not a bool'"],
- ["QueryThrows", "BoolObject", "boolCol == $0", "not a bool"],
- ["QueryThrows", "BoolObject", "boolCol > true"],
- ["QueryThrows", "BoolObject", "boolCol >= true"],
- ["QueryThrows", "BoolObject", "boolCol < true"],
- ["QueryThrows", "BoolObject", "boolCol <= true"],
- ["QueryThrows", "BoolObject", "boolCol BEGINSWITH true"],
- ["QueryThrows", "BoolObject", "boolCol CONTAINS true"],
- ["QueryThrows", "BoolObject", "boolCol ENDSWITH true"]
- ]
-},
-
-"intTests" : {
- "schema" : [{
- "name": "IntObject",
- "properties": [{ "name": "intCol", "type": "int" }]
- }],
- "objects": [
- { "type": "IntObject", "value": [-1] },
- { "type": "IntObject", "value": [0] },
- { "type": "IntObject", "value": [100] }
- ],
- "tests": [
- ["QueryCount", 1, "IntObject", "intCol == -1"],
- ["QueryCount", 1, "IntObject", "intCol==0"],
- ["QueryCount", 0, "IntObject", "1 == intCol"],
- ["QueryCount", 2, "IntObject", "intCol != 0"],
- ["QueryCount", 2, "IntObject", "intCol > -1"],
- ["QueryCount", 3, "IntObject", "intCol >= -1"],
- ["QueryCount", 2, "IntObject", "intCol < 100"],
- ["QueryCount", 3, "IntObject", "intCol <= 100"],
- ["QueryCount", 1, "IntObject", "intCol > 0x1F"],
- ["QueryCount", 1, "IntObject", "intCol == $0", 100],
-
- ["QueryThrows", "IntObject", "intCol == 'not an int'"],
- ["QueryThrows", "IntObject", "intCol == true"],
- ["QueryThrows", "IntObject", "intCol == $0", "not an int"],
- ["QueryThrows", "IntObject", "intCol BEGINSWITH 1"],
- ["QueryThrows", "IntObject", "intCol CONTAINS 1"],
- ["QueryThrows", "IntObject", "intCol ENDSWITH 1"]
- ]
-},
-
-"floatTests" : {
- "schema" : [{
- "name": "FloatObject",
- "properties": [{ "name": "floatCol", "type": "float" }]
- }],
- "objects": [
- { "type": "FloatObject", "value": [-1.001] },
- { "type": "FloatObject", "value": [0.0] },
- { "type": "FloatObject", "value": [100.2] }
- ],
- "tests": [
- ["QueryCount", 1, "FloatObject", "floatCol == -1.001"],
- ["QueryCount", 1, "FloatObject", "floatCol = 0"],
- ["QueryCount", 0, "FloatObject", "1 == floatCol"],
- ["QueryCount", 2, "FloatObject", "floatCol != 0"],
- ["QueryCount", 2, "FloatObject", "floatCol > -1.001"],
- ["QueryCount", 3, "FloatObject", "floatCol >= -1.001"],
- ["QueryCount", 2, "FloatObject", "floatCol < 100.2"],
- ["QueryCount", 3, "FloatObject", "floatCol <= 100.2"],
- ["QueryCount", 1, "FloatObject", "floatCol > 0x1F"],
- ["QueryCount", 1, "FloatObject", "floatCol == $0", 100.2],
-
- ["QueryThrows", "FloatObject", "floatCol == 'not a float'"],
- ["QueryThrows", "FloatObject", "floatCol == true"],
- ["QueryThrows", "FloatObject", "floatCol == $0", "not a float"],
- ["QueryThrows", "FloatObject", "floatCol BEGINSWITH 1"],
- ["QueryThrows", "FloatObject", "floatCol CONTAINS 1"],
- ["QueryThrows", "FloatObject", "floatCol ENDSWITH 1"],
-
- ["Disabled", "QueryThrows", "FloatObject", "floatCol = 3.5e+38"],
- ["Disabled", "QueryThrows", "FloatObject", "floatCol = -3.5e+38"]
- ]
-},
-
-"doubleTests" : {
- "schema" : [{
- "name": "DoubleObject",
- "properties": [{ "name": "doubleCol", "type": "double" }]
- }],
- "objects": [
- { "type": "DoubleObject", "value": [-1.001] },
- { "type": "DoubleObject", "value": [0.0] },
- { "type": "DoubleObject", "value": [100.2] }
- ],
- "tests": [
- ["QueryCount", 1, "DoubleObject", "doubleCol == -1.001"],
- ["QueryCount", 1, "DoubleObject", "doubleCol == 0"],
- ["QueryCount", 0, "DoubleObject", "1 == doubleCol"],
- ["QueryCount", 2, "DoubleObject", "doubleCol != 0"],
- ["QueryCount", 2, "DoubleObject", "doubleCol > -1.001"],
- ["QueryCount", 3, "DoubleObject", "doubleCol >= -1.001"],
- ["QueryCount", 2, "DoubleObject", "doubleCol < 100.2"],
- ["QueryCount", 3, "DoubleObject", "doubleCol <= 100.2"],
- ["QueryCount", 1, "DoubleObject", "doubleCol > 0x1F"],
- ["QueryCount", 1, "DoubleObject", "doubleCol == $0", 100.2],
-
- ["QueryThrows", "DoubleObject", "doubleCol == 'not a double'"],
- ["QueryThrows", "DoubleObject", "doubleCol == true"],
- ["QueryThrows", "DoubleObject", "doubleCol == $0", "not a double"],
- ["QueryThrows", "DoubleObject", "doubleCol BEGINSWITH 1"],
- ["QueryThrows", "DoubleObject", "doubleCol CONTAINS 1"],
- ["QueryThrows", "DoubleObject", "doubleCol ENDSWITH 1"]
- ]
-},
-
-"stringTests" : {
- "schema" : [{
- "name": "StringObject",
- "properties": [{ "name": "stringCol", "type": "string" }]
- }],
- "objects": [
- { "type": "StringObject", "value": ["A"] },
- { "type": "StringObject", "value": ["a"] },
- { "type": "StringObject", "value": ["a"] },
- { "type": "StringObject", "value": ["C"] },
- { "type": "StringObject", "value": ["c"] },
- { "type": "StringObject", "value": ["abc"] },
- { "type": "StringObject", "value": ["ABC"] },
- { "type": "StringObject", "value": [""] },
- { "type": "StringObject", "value": ["\\\"\\n\\0\\r\\\\'"] }
- ],
- "tests": [
- ["QueryCount", 2, "StringObject", "stringCol == 'a'"],
- ["QueryCount", 1, "StringObject", "'c' == stringCol"],
- ["QueryCount", 2, "StringObject", "stringCol == \"a\""],
- ["QueryCount", 1, "StringObject", "stringCol=='abc'"],
- ["QueryCount", 1, "StringObject", "stringCol == ''"],
- ["QueryCount", 8, "StringObject", "stringCol != ''"],
- ["QueryCount", 1, "StringObject", "stringCol == \"\\\"\\n\\0\\r\\\\'\""],
- ["QueryCount", 3, "StringObject", "stringCol BEGINSWITH 'a'"],
- ["QueryCount", 1, "StringObject", "stringCol beginswith 'ab'"],
- ["QueryCount", 0, "StringObject", "stringCol BEGINSWITH 'abcd'"],
- ["QueryCount", 2, "StringObject", "stringCol BEGINSWITH 'A'"],
- ["QueryCount", 2, "StringObject", "stringCol ENDSWITH 'c'"],
- ["QueryCount", 1, "StringObject", "stringCol endswith 'bc'"],
- ["QueryCount", 9, "StringObject", "stringCol ENDSWITH ''"],
- ["QueryCount", 1, "StringObject", "stringCol CONTAINS 'b'"],
- ["QueryCount", 2, "StringObject", "stringCol contains 'c'"],
- ["QueryCount", 9, "StringObject", "stringCol CONTAINS ''"],
- ["QueryCount", 2, "StringObject", "stringCol == $0", "a"],
- ["QueryCount", 2, "StringObject", "stringCol ENDSWITH $0", "c"],
-
- ["QueryThrows", "StringObject", "stringCol == true"],
- ["QueryThrows", "StringObject", "stringCol == 123"],
- ["QueryThrows", "StringObject", "stringCol CONTAINS $0", 1],
-
- ["QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"],
- ["QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"],
- ["QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"],
- ["QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"]
- ]
-},
-
-"binaryTests" : {
- "schema" : [{
- "name": "BinaryObject",
- "properties": [{ "name": "binaryCol", "type": "data" }]
- }],
- "objects": [
- { "type": "BinaryObject", "value": [[1, 100, 233, 255, 0]] },
- { "type": "BinaryObject", "value": [[1, 100]] },
- { "type": "BinaryObject", "value": [[100]] },
- { "type": "BinaryObject", "value": [[]] },
- { "type": "BinaryObject", "value": [[255, 0]] }
- ],
- "tests": [
- ["QueryCount", 1, "BinaryObject", "binaryCol == $0", [1, "binaryCol"]],
- ["QueryCount", 1, "BinaryObject", "$0 == binaryCol", [2, "binaryCol"]],
- ["QueryCount", 4, "BinaryObject", "binaryCol != $0", [0, "binaryCol"]],
- ["QueryCount", 1, "BinaryObject", "binaryCol BEGINSWITH $0", [0, "binaryCol"]],
- ["QueryCount", 2, "BinaryObject", "binaryCol BEGINSWITH $0", [1, "binaryCol"]],
- ["QueryCount", 2, "BinaryObject", "binaryCol ENDSWITH $0", [4, "binaryCol"]],
- ["QueryCount", 3, "BinaryObject", "binaryCol CONTAINS $0", [2, "binaryCol"]]
- ]
-},
-
-"objectTests" : {
- "schema" : [
- { "name": "IntObject", "properties": [
- { "name": "intCol", "type": "int" }
- ]},
- { "name": "LinkObject", "properties": [
- { "name": "linkCol", "type": "object", "objectType": "IntObject" }
- ]}
- ],
- "objects": [
- { "type": "LinkObject", "value": [[1]] },
- { "type": "LinkObject", "value": [[2]] },
- { "type": "LinkObject", "value": [null] }
- ],
- "tests": [
- ["QueryCount", 1, "LinkObject", "linkCol == $0", [0, "linkCol"]],
- ["QueryCount", 1, "LinkObject", "$0 == linkCol", [1, "linkCol"]],
- ["QueryCount", 2, "LinkObject", "linkCol != $0", [0, "linkCol"]],
- ["QueryCount", 1, "LinkObject", "linkCol = null"],
- ["QueryCount", 2, "LinkObject", "linkCol != NULL"],
- ["QueryCount", 1, "LinkObject", "linkCol = $0", null],
-
- ["QueryThrows", "LinkObject", "linkCol > $0", [0, "linkCol"]],
- ["QueryThrows", "LinkObject", "intCol = $0", [0, "linkCol"]]
- ]
-},
-
-"compoundTests" : {
- "schema" : [
- { "name": "IntObject",
- "properties": [{ "name": "intCol", "type": "int" }],
- "primaryKey" : "intCol" }
- ],
- "objects": [
- { "type": "IntObject", "value": [0] },
- { "type": "IntObject", "value": [1] },
- { "type": "IntObject", "value": [2] },
- { "type": "IntObject", "value": [3] }
- ],
- "tests": [
- ["ObjectSet", [], "IntObject", "intCol == 0 && intCol == 1"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1"],
- ["ObjectSet", [0], "IntObject", "intCol == 0 && intCol != 1"],
- ["ObjectSet", [2, 3], "IntObject", "intCol >= 2 && intCol < 4"],
- ["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"],
- ["ObjectSet", [0, 3], "IntObject", "intCol == 0 || NOT (intCol == 1 || intCol == 2)"],
- ["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"],
- ["ObjectSet", [1], "IntObject", "intCol >= 1 && (intCol == 0 || intCol == 1)"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"],
- ["ObjectSet", [0, 1], "IntObject", "(intCol == 1 && intCol >= 1) || intCol == 0"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"],
- ["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"],
- ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || NOT (intCol == 3 && intCol >= 0) && intCol == 1"]
- ]
-},
-
-"keyPathTests" : {
- "schema" : [
- {
- "name": "BasicTypesObject",
- "properties": [
- { "name": "intCol", "type": "int" },
- { "name": "floatCol", "type": "float" },
- { "name": "doubleCol", "type": "double" },
- { "name": "stringCol", "type": "string" },
- { "name": "dateCol", "type": "date" },
- { "name": "dataCol", "type": "data" }
- ]
- },
- {
- "name": "LinkTypesObject",
- "primaryKey": "primaryKey",
- "properties": [
- { "name": "primaryKey", "type": "int" },
- { "name": "basicLink", "type": "object", "objectType": "BasicTypesObject" },
- { "name": "linkLink", "type": "object", "objectType": "LinkTypesObject" }
- ]
- }],
- "objects": [
- { "type": "LinkTypesObject", "value": [0, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null] },
- { "type": "LinkTypesObject", "value": [1, null, [2, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null]] },
- { "type": "LinkTypesObject", "value": [3, null, [4, [2, 0.2, 0.002, "2", 2, [2, 20, 200]], null]] }
- ],
- "tests": [
- ["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.intCol == 1"],
- ["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.intCol == 1"],
- ["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.intCol > 0"],
- ["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.floatCol == 0.1"],
- ["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.floatCol == 0.1"],
- ["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.floatCol > 0"]
- ]
-},
-
-"optionalTests" : {
- "schema" : [
- {
- "name": "OptionalTypesObject",
- "primaryKey": "primaryKey",
- "properties": [
- { "name": "primaryKey", "type": "int" },
- { "name": "intCol", "type": "int", "optional": true },
- { "name": "floatCol", "type": "float", "optional": true },
- { "name": "doubleCol", "type": "double", "optional": true },
- { "name": "stringCol", "type": "string", "optional": true },
- { "name": "dateCol", "type": "date", "optional": true },
- { "name": "dataCol", "type": "data", "optional": true }
- ]
- },
- {
- "name": "LinkTypesObject",
- "primaryKey": "primaryKey",
- "properties": [
- { "name": "primaryKey", "type": "int" },
- { "name": "basicLink", "type": "object", "objectType": "OptionalTypesObject" }
- ]
- }],
- "objects": [
- { "type": "LinkTypesObject", "value": [0, [0, 1, 0.1, 0.001, "1", 1, [1, 10, 100]]] },
- { "type": "LinkTypesObject", "value": [1, [1, null, null, null, null, null, null]] }
- ],
- "tests": [
- ["ObjectSet", [1], "OptionalTypesObject", "intCol == null"],
- ["ObjectSet", [1], "OptionalTypesObject", "null == intCol"],
- ["ObjectSet", [0], "OptionalTypesObject", "intCol != null"],
- ["ObjectSet", [1], "OptionalTypesObject", "floatCol == null"],
- ["ObjectSet", [0], "OptionalTypesObject", "floatCol != null"],
- ["ObjectSet", [1], "OptionalTypesObject", "doubleCol == null"],
- ["ObjectSet", [0], "OptionalTypesObject", "doubleCol != null"],
- ["ObjectSet", [1], "OptionalTypesObject", "stringCol == null"],
- ["ObjectSet", [0], "OptionalTypesObject", "stringCol != null"],
- ["ObjectSet", [1], "OptionalTypesObject", "dateCol == null"],
- ["ObjectSet", [0], "OptionalTypesObject", "dateCol != null"],
- ["ObjectSet", [1], "OptionalTypesObject", "dataCol == null"],
- ["ObjectSet", [0], "OptionalTypesObject", "dataCol != null"],
-
- ["ObjectSet", [1], "LinkTypesObject", "basicLink.intCol == null"],
- ["ObjectSet", [0], "LinkTypesObject", "basicLink.intCol != null"],
- ["ObjectSet", [1], "LinkTypesObject", "basicLink.floatCol == null"],
- ["ObjectSet", [0], "LinkTypesObject", "basicLink.floatCol != null"],
- ["ObjectSet", [1], "LinkTypesObject", "basicLink.doubleCol == null"],
- ["ObjectSet", [0], "LinkTypesObject", "basicLink.doubleCol != null"],
- ["ObjectSet", [1], "LinkTypesObject", "basicLink.stringCol == null"],
- ["ObjectSet", [0], "LinkTypesObject", "basicLink.stringCol != null"],
- ["ObjectSet", [1], "LinkTypesObject", "basicLink.dateCol == null"],
- ["ObjectSet", [0], "LinkTypesObject", "basicLink.dateCol != null"],
- ["QueryThrows", "LinkTypesObject", "basicLink.dataCol == null"]
- ]
-}
-
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp
deleted file mode 100644
index cb6e88697..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp
+++ /dev/null
@@ -1,1863 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/event_loop.hpp"
-#include "util/test_file.hpp"
-#include "util/test_utils.hpp"
-
-#include "binding_context.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "thread_safe_reference.hpp"
-#include "util/scheduler.hpp"
-
-#include <realm/db.hpp>
-
-#if REALM_ENABLE_SYNC
-#include "sync/async_open_task.hpp"
-#endif
-
-#include <realm/util/scope_exit.hpp>
-
-namespace realm {
-class TestHelper {
-public:
- static DBRef& get_db(SharedRealm const& shared_realm)
- {
- return Realm::Internal::get_db(*shared_realm);
- }
-
- static void begin_read(SharedRealm const& shared_realm, VersionID version)
- {
- Realm::Internal::begin_read(*shared_realm, version);
- }
-};
-}
-
-using namespace realm;
-
-TEST_CASE("SharedRealm: get_shared_realm()") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- SECTION("should validate that the config is sensible") {
- SECTION("bad encryption key") {
- config.encryption_key = std::vector<char>(2, 0);
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("schema without schema version") {
- config.schema_version = ObjectStore::NotVersioned;
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("migration function for immutable") {
- config.schema_mode = SchemaMode::Immutable;
- config.migration_function = [](auto, auto, auto) { };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("migration function for read-only") {
- config.schema_mode = SchemaMode::ReadOnlyAlternative;
- config.migration_function = [](auto, auto, auto) { };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("migration function for additive-only") {
- config.schema_mode = SchemaMode::Additive;
- config.migration_function = [](auto, auto, auto) { };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("initialization function for immutable") {
- config.schema_mode = SchemaMode::Immutable;
- config.initialization_function = [](auto) { };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("initialization function for read-only") {
- config.schema_mode = SchemaMode::ReadOnlyAlternative;
- config.initialization_function = [](auto) { };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
- }
-
- SECTION("should reject mismatched config") {
- SECTION("schema version") {
- auto realm = Realm::get_shared_realm(config);
- config.schema_version = 2;
- REQUIRE_THROWS(Realm::get_shared_realm(config));
-
- config.schema = util::none;
- config.schema_version = ObjectStore::NotVersioned;
- REQUIRE_NOTHROW(Realm::get_shared_realm(config));
- }
-
- SECTION("schema mode") {
- auto realm = Realm::get_shared_realm(config);
- config.schema_mode = SchemaMode::Manual;
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("durability") {
- auto realm = Realm::get_shared_realm(config);
- config.in_memory = true;
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
-
- SECTION("schema") {
- auto realm = Realm::get_shared_realm(config);
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- {"value2", PropertyType::Int}
- }},
- };
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- }
- }
-
-
-// Windows doesn't use fifos
-#ifndef _WIN32
- SECTION("should be able to set a FIFO fallback path") {
- std::string fallback_dir = tmp_dir() + "/fallback/";
- realm::util::try_make_dir(fallback_dir);
- TestFile config;
- config.fifo_files_fallback_path = fallback_dir;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- realm::util::make_dir(config.path + ".note");
- auto realm = Realm::get_shared_realm(config);
- auto fallback_file = util::format("%1realm_%2.note", fallback_dir, std::hash<std::string>()(config.path)); // Mirror internal implementation
- REQUIRE(File::exists(fallback_file));
- realm::util::remove_dir(config.path + ".note");
- realm::util::remove_dir_recursive(fallback_dir);
- }
-
- SECTION("automatically append dir separator to end of fallback path") {
- std::string fallback_dir = tmp_dir() + "/fallback";
- realm::util::try_make_dir(fallback_dir);
- TestFile config;
- config.fifo_files_fallback_path = fallback_dir;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- realm::util::make_dir(config.path + ".note");
- auto realm = Realm::get_shared_realm(config);
- auto fallback_file = util::format("%1/realm_%2.note", fallback_dir, std::hash<std::string>()(config.path)); // Mirror internal implementation
- REQUIRE(File::exists(fallback_file));
- realm::util::remove_dir(config.path + ".note");
- realm::util::remove_dir_recursive(fallback_dir);
- }
-#endif
-
- SECTION("should verify that the schema is valid") {
- config.schema = Schema{
- {"object",
- {{"value", PropertyType::Int}},
- {{"invalid backlink", PropertyType::LinkingObjects|PropertyType::Array, "object", "value"}}
- }
- };
- REQUIRE_THROWS_WITH(Realm::get_shared_realm(config),
- Catch::Matchers::Contains("origin of linking objects property"));
- }
-
- SECTION("should apply the schema if one is supplied") {
- Realm::get_shared_realm(config);
-
- {
- Group g(config.path);
- auto table = ObjectStore::table_for_object_type(g, "object");
- REQUIRE(table);
- REQUIRE(table->get_column_count() == 1);
- REQUIRE(table->get_column_name(*table->get_column_keys().begin()) == "value");
- }
-
- config.schema_version = 2;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- {"value2", PropertyType::Int}
- }},
- };
- bool migration_called = false;
- config.migration_function = [&](SharedRealm old_realm, SharedRealm new_realm, Schema&) {
- migration_called = true;
- REQUIRE(ObjectStore::table_for_object_type(old_realm->read_group(), "object")->get_column_count() == 1);
- REQUIRE(ObjectStore::table_for_object_type(new_realm->read_group(), "object")->get_column_count() == 2);
- };
- Realm::get_shared_realm(config);
- REQUIRE(migration_called);
- }
-
- SECTION("should properly roll back from migration errors") {
- Realm::get_shared_realm(config);
-
- config.schema_version = 2;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- {"value2", PropertyType::Int}
- }},
- };
- bool migration_called = false;
- config.migration_function = [&](SharedRealm old_realm, SharedRealm new_realm, Schema&) {
- REQUIRE(ObjectStore::table_for_object_type(old_realm->read_group(), "object")->get_column_count() == 1);
- REQUIRE(ObjectStore::table_for_object_type(new_realm->read_group(), "object")->get_column_count() == 2);
- if (!migration_called) {
- migration_called = true;
- throw "error";
- }
- };
- REQUIRE_THROWS_WITH(Realm::get_shared_realm(config), "error");
- REQUIRE(migration_called);
- REQUIRE_NOTHROW(Realm::get_shared_realm(config));
- }
-
- SECTION("should read the schema from the file if none is supplied") {
- Realm::get_shared_realm(config);
-
- config.schema = util::none;
- auto realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().size() == 1);
- auto it = realm->schema().find("object");
- auto table = realm->read_group().get_table("class_object");
- REQUIRE(it != realm->schema().end());
- REQUIRE(it->table_key == table->get_key());
- REQUIRE(it->persisted_properties.size() == 1);
- REQUIRE(it->persisted_properties[0].name == "value");
- REQUIRE(it->persisted_properties[0].column_key == table->get_column_key("value"));
- }
-
- SECTION("should read the proper schema from the file if a custom version is supplied") {
- Realm::get_shared_realm(config);
-
- config.schema = util::none;
- config.schema_mode = SchemaMode::Additive;
- config.schema_version = 0;
-
- auto realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().size() == 1);
-
- auto& db = TestHelper::get_db(realm);
- auto rt = db->start_read();
- VersionID old_version = rt->get_version_of_current_transaction();
- rt = nullptr;
- realm->close();
-
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- {"object1", {
- {"value", PropertyType::Int}
- }},
- };
- config.schema_version = 1;
- realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().size() == 2);
-
- config.schema = util::none;
- auto old_realm = Realm::get_shared_realm(config);
- TestHelper::begin_read(old_realm, old_version);
- REQUIRE(old_realm->schema().size() == 1);
- }
-
- SECTION("should sensibly handle opening an uninitialized file without a schema specified") {
- // create an empty file
- File(config.path, File::mode_Write);
-
- // open the empty file, but don't initialize the schema
- Realm::Config config_without_schema = config;
- config_without_schema.schema = util::none;
- config_without_schema.schema_version = ObjectStore::NotVersioned;
- auto realm = Realm::get_shared_realm(config_without_schema);
- REQUIRE(realm->schema().empty());
- REQUIRE(realm->schema_version() == ObjectStore::NotVersioned);
- // verify that we can get another Realm instance
- REQUIRE_NOTHROW(Realm::get_shared_realm(config_without_schema));
-
- // verify that we can also still open the file with a proper schema
- auto realm2 = Realm::get_shared_realm(config);
- REQUIRE_FALSE(realm2->schema().empty());
- REQUIRE(realm2->schema_version() == 1);
- }
-
- SECTION("should populate the table columns in the schema when opening as immutable") {
- Realm::get_shared_realm(config);
-
- config.schema_mode = SchemaMode::Immutable;
- auto realm = Realm::get_shared_realm(config);
- auto it = realm->schema().find("object");
- auto table = realm->read_group().get_table("class_object");
- REQUIRE(it != realm->schema().end());
- REQUIRE(it->table_key == table->get_key());
- REQUIRE(it->persisted_properties.size() == 1);
- REQUIRE(it->persisted_properties[0].name == "value");
- REQUIRE(it->persisted_properties[0].column_key == table->get_column_key("value"));
- }
-
- SECTION("should support using different table subsets on different threads") {
- auto realm1 = Realm::get_shared_realm(config);
-
- config.schema = Schema{
- {"object 2", {
- {"value", PropertyType::Int}
- }},
- };
- auto realm2 = Realm::get_shared_realm(config);
-
- config.schema = util::none;
- auto realm3 = Realm::get_shared_realm(config);
-
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
- auto realm4 = Realm::get_shared_realm(config);
-
- realm1->refresh();
- realm2->refresh();
-
- REQUIRE(realm1->schema().size() == 1);
- REQUIRE(realm1->schema().find("object") != realm1->schema().end());
- REQUIRE(realm2->schema().size() == 1);
- REQUIRE(realm2->schema().find("object 2") != realm2->schema().end());
- REQUIRE(realm3->schema().size() == 2);
- REQUIRE(realm3->schema().find("object") != realm3->schema().end());
- REQUIRE(realm3->schema().find("object 2") != realm3->schema().end());
- REQUIRE(realm4->schema().size() == 1);
- REQUIRE(realm4->schema().find("object") != realm4->schema().end());
- }
-
-// The ExternalCommitHelper implementation on Windows doesn't rely on files
-#ifndef _WIN32
- SECTION("should throw when creating the notification pipe fails") {
- util::try_make_dir(config.path + ".note");
- auto sys_fallback_file = util::format("%1realm_%2.note", DBOptions::get_sys_tmp_dir(), std::hash<std::string>()(config.path)); // Mirror internal implementation
- util::try_make_dir(sys_fallback_file);
- REQUIRE_THROWS(Realm::get_shared_realm(config));
- util::remove_dir(config.path + ".note");
- util::remove_dir(sys_fallback_file);
- }
-#endif
-
- SECTION("should detect use of Realm on incorrect thread") {
- auto realm = Realm::get_shared_realm(config);
- std::thread([&]{
- REQUIRE_THROWS_AS(realm->verify_thread(), IncorrectThreadException);
- }).join();
- }
-
- SECTION("should not modify the schema when fetching from the cache") {
- auto realm = Realm::get_shared_realm(config);
- auto object_schema = &*realm->schema().find("object");
- Realm::get_shared_realm(config);
- REQUIRE(object_schema == &*realm->schema().find("object"));
- }
-}
-
-#if REALM_ENABLE_SYNC
-TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
- TestFile local_config;
- local_config.schema_version = 1;
- local_config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- if (!util::EventLoop::has_implementation())
- return;
-
- TestSyncManager init_sync_manager;
-
- SyncServer server;
- SyncTestFile config(server, "default");
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
- SyncTestFile config2(server, "default");
- config2.schema = config.schema;
-
- std::mutex mutex;
- SECTION("can open synced Realms that don't already exist") {
- std::atomic<bool> called{false};
- auto task = Realm::get_synchronized_realm(config);
- task->start([&](auto ref, auto error) {
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(!error);
- called = true;
-
- REQUIRE(Realm::get_shared_realm(std::move(ref))->read_group().get_table("class_object"));
- });
- util::EventLoop::main().run_until([&]{ return called.load(); });
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(called);
- }
-
- SECTION("downloads Realms which exist on the server") {
- {
- auto realm = Realm::get_shared_realm(config2);
- realm->begin_transaction();
- realm->read_group().get_table("class_object")->create_object();
- realm->commit_transaction();
- wait_for_upload(*realm);
- }
-
- std::atomic<bool> called{false};
- auto task = Realm::get_synchronized_realm(config);
- task->start([&](auto ref, auto error) {
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(!error);
- called = true;
-
- REQUIRE(Realm::get_shared_realm(std::move(ref))->read_group().get_table("class_object"));
- });
- util::EventLoop::main().run_until([&]{ return called.load(); });
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(called);
- }
-
- SECTION("downloads latest state for Realms which already exist locally") {
- wait_for_upload(*Realm::get_shared_realm(config));
-
- {
- auto realm = Realm::get_shared_realm(config2);
- realm->begin_transaction();
- realm->read_group().get_table("class_object")->create_object();
- realm->commit_transaction();
- wait_for_upload(*realm);
- }
-
- std::atomic<bool> called{false};
- auto task = Realm::get_synchronized_realm(config);
- task->start([&](auto ref, auto error) {
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(!error);
- called = true;
-
- REQUIRE(Realm::get_shared_realm(std::move(ref))->read_group().get_table("class_object")->size() == 1);
- });
- util::EventLoop::main().run_until([&]{ return called.load(); });
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(called);
- }
-
- SECTION("can download partial Realms") {
- config.sync_config->is_partial = true;
- config2.sync_config->is_partial = true;
- {
- auto realm = Realm::get_shared_realm(config2);
- realm->begin_transaction();
- realm->read_group().get_table("class_object")->create_object();
- realm->commit_transaction();
- wait_for_upload(*realm);
- }
-
- std::atomic<bool> called{false};
- auto task = Realm::get_synchronized_realm(config);
- task->start([&](auto, auto error) {
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(!error);
- called = true;
- });
- util::EventLoop::main().run_until([&]{ return called.load(); });
- std::lock_guard<std::mutex> lock(mutex);
- REQUIRE(called);
-
- // No subscriptions, so no objects
- REQUIRE(Realm::get_shared_realm(config)->read_group().get_table("class_object")->size() == 0);
- }
-
- SECTION("can download multiple Realms at a time") {
- SyncTestFile config1(server, "realm1");
- SyncTestFile config2(server, "realm2");
- SyncTestFile config3(server, "realm3");
- SyncTestFile config4(server, "realm4");
-
- std::vector<std::shared_ptr<AsyncOpenTask>> tasks = {
- Realm::get_synchronized_realm(config1),
- Realm::get_synchronized_realm(config2),
- Realm::get_synchronized_realm(config3),
- Realm::get_synchronized_realm(config4),
- };
-
- std::atomic<int> completed{0};
- for (auto& task : tasks) {
- task->start([&](auto, auto) {
- ++completed;
- });
- }
- util::EventLoop::main().run_until([&]{ return completed == 4; });
- }
-}
-#endif
-
-TEST_CASE("SharedRealm: notifications") {
- if (!util::EventLoop::has_implementation())
- return;
-
- TestFile config;
- config.schema_version = 0;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- struct Context : BindingContext {
- size_t* change_count;
- Context(size_t* out) : change_count(out) { }
-
- void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
- {
- ++*change_count;
- }
- };
-
- size_t change_count = 0;
- auto realm = Realm::get_shared_realm(config);
- realm->read_group();
- realm->m_binding_context.reset(new Context{&change_count});
- realm->m_binding_context->realm = realm;
-
- SECTION("local notifications are sent synchronously") {
- realm->begin_transaction();
- REQUIRE(change_count == 0);
- realm->commit_transaction();
- REQUIRE(change_count == 1);
- }
-
- SECTION("remote notifications are sent asynchronously") {
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->commit_transaction();
- REQUIRE(change_count == 0);
- util::EventLoop::main().run_until([&]{ return change_count > 0; });
- REQUIRE(change_count == 1);
- }
-
- SECTION("refresh() from within changes_available() refreshes") {
- struct Context : BindingContext {
- Realm& realm;
- Context(Realm& realm) : realm(realm) { }
-
- void changes_available() override
- {
- REQUIRE(realm.refresh());
- }
- };
- realm->m_binding_context.reset(new Context{*realm});
- realm->set_auto_refresh(false);
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->commit_transaction();
- realm->notify();
- // Should return false as the realm was already advanced
- REQUIRE_FALSE(realm->refresh());
- }
-
- SECTION("refresh() from within did_change() is a no-op") {
- struct Context : BindingContext {
- Realm& realm;
- Context(Realm& realm) : realm(realm) { }
-
- void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
- {
- // Create another version so that refresh() could do something
- auto r2 = Realm::get_shared_realm(realm.config());
- r2->begin_transaction();
- r2->commit_transaction();
-
- // Should be a no-op
- REQUIRE_FALSE(realm.refresh());
- }
- };
- realm->m_binding_context.reset(new Context{*realm});
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->commit_transaction();
- REQUIRE(realm->refresh());
-
- auto ver = realm->current_transaction_version();
- realm->m_binding_context.reset();
- // Should advance to the version created in the previous did_change()
- REQUIRE(realm->refresh());
- auto new_ver = realm->current_transaction_version();
- REQUIRE(*new_ver > *ver);
- // No more versions, so returns false
- REQUIRE_FALSE(realm->refresh());
- }
-
- SECTION("begin_write() from within did_change() produces recursive notifications") {
- struct Context : BindingContext {
- Realm& realm;
- size_t calls = 0;
- Context(Realm& realm) : realm(realm) { }
-
- void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
- {
- ++calls;
- if (realm.is_in_transaction())
- return;
-
- // Create another version so that begin_write() advances the version
- auto r2 = Realm::get_shared_realm(realm.config());
- r2->begin_transaction();
- r2->commit_transaction();
-
- realm.begin_transaction();
- realm.cancel_transaction();
- }
- };
- auto context = new Context{*realm};
- realm->m_binding_context.reset(context);
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->commit_transaction();
- REQUIRE(realm->refresh());
- REQUIRE(context->calls == 2);
-
- // Despite not sending a new notification we did advance the version, so
- // no more versions to refresh to
- REQUIRE_FALSE(realm->refresh());
- }
-}
-
-TEST_CASE("SharedRealm: schema updating from external changes") {
- TestFile config;
- config.schema_version = 0;
- config.schema_mode = SchemaMode::Additive;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{true}},
- {"value 2", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }},
- };
-
- SECTION("newly added columns update table columns but are not added to properties") {
- // Does this test add any value when column keys are stable?
- auto r1 = Realm::get_shared_realm(config);
- auto r2 = Realm::get_shared_realm(config);
- auto test = [&] {
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->add_column(type_String, "new col");
- r2->commit_transaction();
-
- auto& object_schema = *r1->schema().find("object");
- REQUIRE(object_schema.persisted_properties.size() == 2);
- ColKey col = object_schema.persisted_properties[0].column_key;
- r1->refresh();
- REQUIRE(object_schema.persisted_properties[0].column_key == col);
- };
- SECTION("with an active read transaction") {
- r1->read_group();
- test();
- }
- SECTION("without an active read transaction") {
- r1->invalidate();
- test();
- }
- }
-
- SECTION("beginning a read transaction checks for incompatible changes") {
- auto r = Realm::get_shared_realm(config);
- r->invalidate();
-
- auto& db = TestHelper::get_db(r);
- WriteTransaction wt(db);
- auto& table = *wt.get_table("class_object");
-
- SECTION("removing a property") {
- table.remove_column(table.get_column_key("value"));
- wt.commit();
- REQUIRE_THROWS_WITH(r->refresh(),
- Catch::Matchers::Contains("Property 'object.value' has been removed."));
- }
-
- SECTION("change property type") {
- table.remove_column(table.get_column_key("value 2"));
- table.add_column(type_Float, "value 2");
- wt.commit();
- REQUIRE_THROWS_WITH(r->refresh(),
- Catch::Matchers::Contains("Property 'object.value 2' has been changed from 'int' to 'float'"));
- }
-
- SECTION("make property optional") {
- table.remove_column(table.get_column_key("value 2"));
- table.add_column(type_Int, "value 2", true);
- wt.commit();
- REQUIRE_THROWS_WITH(r->refresh(),
- Catch::Matchers::Contains("Property 'object.value 2' has been made optional"));
- }
-
- SECTION("recreate column with no changes") {
- table.remove_column(table.get_column_key("value 2"));
- table.add_column(type_Int, "value 2");
- wt.commit();
- REQUIRE_NOTHROW(r->refresh());
- }
-
- SECTION("remove index from non-PK") {
- table.remove_search_index(table.get_column_key("value 2"));
- wt.commit();
- REQUIRE_NOTHROW(r->refresh());
- }
- }
-}
-
-TEST_CASE("SharedRealm: close()") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- {"list", {
- {"list", PropertyType::Object|PropertyType::Array, "object"}
- }},
- };
-
- auto realm = Realm::get_shared_realm(config);
-
- SECTION("all functions throw ClosedRealmException after close") {
- realm->close();
-
- REQUIRE(realm->is_closed());
-
- REQUIRE_THROWS_AS(realm->read_group(), ClosedRealmException);
- REQUIRE_THROWS_AS(realm->begin_transaction(), ClosedRealmException);
- REQUIRE(!realm->is_in_transaction());
- REQUIRE_THROWS_AS(realm->commit_transaction(), InvalidTransactionException);
- REQUIRE_THROWS_AS(realm->cancel_transaction(), InvalidTransactionException);
-
- REQUIRE_THROWS_AS(realm->refresh(), ClosedRealmException);
- REQUIRE_THROWS_AS(realm->invalidate(), ClosedRealmException);
- REQUIRE_THROWS_AS(realm->compact(), ClosedRealmException);
- }
-
- SECTION("fully closes database file even with live notifiers") {
- auto& group = realm->read_group();
- realm->begin_transaction();
- auto obj = ObjectStore::table_for_object_type(group, "list")->create_object();
- realm->commit_transaction();
-
- Results results(realm, ObjectStore::table_for_object_type(group, "object"));
- List list(realm, obj.get_linklist("list"));
- Object object(realm, obj);
-
- auto obj_token = object.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {});
- auto list_token = list.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {});
- auto results_token = results.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {});
-
- // Perform a dummy transaction to ensure the notifiers actually acquire
- // resources that need to be closed
- realm->begin_transaction();
- realm->commit_transaction();
-
- realm->close();
-
- // Verify that we're able to acquire an exclusive lock
- REQUIRE(DB::call_with_lock(config.path, [](auto) {}));
- }
-}
-
-TEST_CASE("ShareRealm: in-memory mode from buffer") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- SECTION("Save and open Realm from in-memory buffer") {
- // Write in-memory copy of Realm to a buffer
- auto realm = Realm::get_shared_realm(config);
- OwnedBinaryData realm_buffer = realm->write_copy();
-
- // Open the buffer as a new (immutable in-memory) Realm
- realm::Realm::Config config2;
- config2.in_memory = true;
- config2.schema_mode = SchemaMode::Immutable;
- config2.realm_data = realm_buffer.get();
-
- auto realm2 = Realm::get_shared_realm(config2);
-
- // Verify that it can read the schema and that it is the same
- REQUIRE(realm->schema().size() == 1);
- auto it = realm->schema().find("object");
- auto table = realm->read_group().get_table("class_object");
- REQUIRE(it != realm->schema().end());
- REQUIRE(it->table_key == table->get_key());
- REQUIRE(it->persisted_properties.size() == 1);
- REQUIRE(it->persisted_properties[0].name == "value");
- REQUIRE(it->persisted_properties[0].column_key == table->get_column_key("value"));
-
- // Test invalid configs
- realm::Realm::Config config3;
- config3.realm_data = realm_buffer.get();
- REQUIRE_THROWS(Realm::get_shared_realm(config3)); // missing in_memory and immutable
-
- config3.in_memory = true;
- config3.schema_mode = SchemaMode::Immutable;
- config3.path = "path";
- REQUIRE_THROWS(Realm::get_shared_realm(config3)); // both buffer and path
-
- config3.path = "";
- config3.encryption_key = {'a'};
- REQUIRE_THROWS(Realm::get_shared_realm(config3)); // both buffer and encryption
- }
-}
-
-TEST_CASE("ShareRealm: realm closed in did_change callback") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
- config.automatic_change_notifications = false;
- auto r1 = Realm::get_shared_realm(config);
-
- r1->begin_transaction();
- auto table = r1->read_group().get_table("class_object");
- table->create_object();
- r1->commit_transaction();
-
- // Cannot be a member var of Context since Realm.close will free the context.
- static SharedRealm* shared_realm;
- shared_realm = &r1;
- struct Context : public BindingContext {
- void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
- {
- (*shared_realm)->close();
- (*shared_realm).reset();
- }
- };
-
- SECTION("did_change") {
- r1->m_binding_context.reset(new Context());
- r1->invalidate();
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
- r2.reset();
-
- r1->notify();
- }
-
- SECTION("did_change with async results") {
- r1->m_binding_context.reset(new Context());
- Results results(r1, table->where());
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- // Should not be called.
- REQUIRE(false);
- });
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
- r2.reset();
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- coordinator->on_change();
-
- r1->notify();
- }
-
- SECTION("refresh") {
- r1->m_binding_context.reset(new Context());
-
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
- r2.reset();
-
- REQUIRE_FALSE(r1->refresh());
- }
-
- shared_realm = nullptr;
-}
-
-TEST_CASE("RealmCoordinator: schema cache") {
- TestFile config;
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
-
- Schema cache_schema;
- uint64_t cache_sv = -1, cache_tv = -1;
-
- Schema schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
- Schema schema2{
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }},
- };
-
- SECTION("valid initial schema sets cache") {
- coordinator->cache_schema(schema, 5, 10);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 10);
- }
-
- SECTION("cache can be updated with newer schema") {
- coordinator->cache_schema(schema, 5, 10);
- coordinator->cache_schema(schema2, 6, 11);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema2);
- REQUIRE(cache_sv == 6);
- REQUIRE(cache_tv == 11);
- }
-
- SECTION("empty schema is ignored") {
- coordinator->cache_schema(Schema{}, 5, 10);
- REQUIRE_FALSE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
-
- coordinator->cache_schema(schema, 5, 10);
- coordinator->cache_schema(Schema{}, 5, 10);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 10);
- }
-
- SECTION("schema for older transaction is ignored") {
- coordinator->cache_schema(schema, 5, 10);
- coordinator->cache_schema(schema2, 4, 8);
-
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 10);
-
- coordinator->advance_schema_cache(10, 20);
- coordinator->cache_schema(schema, 6, 15);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == 20); // should not have dropped to 15
- }
-
- SECTION("advance_schema() from transaction version bumps transaction version") {
- coordinator->cache_schema(schema, 5, 10);
- coordinator->advance_schema_cache(10, 12);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 12);
- }
-
- SECTION("advance_schema() ending before transaction version does nothing") {
- coordinator->cache_schema(schema, 5, 10);
- coordinator->advance_schema_cache(8, 9);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 10);
- }
-
- SECTION("advance_schema() extending over transaction version bumps version") {
- coordinator->cache_schema(schema, 5, 10);
- coordinator->advance_schema_cache(3, 15);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_sv == 5);
- REQUIRE(cache_tv == 15);
- }
-
- SECTION("advance_schema() with no cahced schema does nothing") {
- coordinator->advance_schema_cache(3, 15);
- REQUIRE_FALSE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- }
-}
-
-TEST_CASE("SharedRealm: coordinator schema cache") {
- TestFile config;
- auto r = Realm::get_shared_realm(config);
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
-
- Schema cache_schema;
- uint64_t cache_sv = -1, cache_tv = -1;
-
- Schema schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
- Schema schema2{
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }},
- };
-
- class ExternalWriter {
- private:
- std::shared_ptr<Realm> m_realm;
- public:
- WriteTransaction wt;
- ExternalWriter(Realm::Config const& config)
- : m_realm([&] {
- auto c = config;
- c.scheduler = util::Scheduler::get_frozen();
- return _impl::RealmCoordinator::get_coordinator(c.path)->get_realm(c, util::none);
- }())
- , wt(TestHelper::get_db(m_realm))
- {
- }
- };
-
- auto external_write = [&](Realm::Config const& config, auto&& fn) {
- ExternalWriter wt(config);
- fn(wt.wt);
- wt.wt.commit();
- };
-
- SECTION("is initially empty for uninitialized file") {
- REQUIRE_FALSE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- }
- r->update_schema(schema);
-
- SECTION("is populated after calling update_schema()") {
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_sv == 0);
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_schema.begin()->persisted_properties[0].column_key != ColKey{});
- }
-
- coordinator = nullptr;
- r = nullptr;
- r = Realm::get_shared_realm(config);
- coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
-
- SECTION("is populated after opening an initialized file") {
- REQUIRE(cache_sv == 0);
- REQUIRE(cache_tv == 2); // with in-realm history the version doesn't reset
- REQUIRE(cache_schema == schema);
- REQUIRE(cache_schema.begin()->persisted_properties[0].column_key != ColKey{});
- }
-
- SECTION("transaction version is bumped after a local write") {
- auto tv = cache_tv;
- r->begin_transaction();
- r->commit_transaction();
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1);
- }
-
- SECTION("notify() without a read transaction does not bump transaction version") {
- auto tv = cache_tv;
-
- SECTION("non-schema change") {
- external_write(config, [](auto& wt) {
- wt.get_table("class_object")->create_object();
- });
- }
- SECTION("schema change") {
- external_write(config, [](auto& wt) {
- wt.add_table("class_object 2");
- });
- }
-
- r->notify();
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv);
- REQUIRE(cache_schema == schema);
- }
-
- SECTION("notify() with a read transaction bumps transaction version") {
- r->read_group();
- external_write(config, [](auto& wt) {
- wt.get_table("class_object")->create_object();
- });
-
- r->notify();
- auto tv = cache_tv;
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1);
- }
-
- SECTION("notify() with a read transaction updates schema folloing external schema change") {
- r->read_group();
- external_write(config, [](auto& wt) {
- wt.add_table("class_object 2");
- });
-
- r->notify();
- auto tv = cache_tv;
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1);
- REQUIRE(cache_schema.size() == 2);
- REQUIRE(cache_schema.find("object 2") != cache_schema.end());
- }
-
- SECTION("transaction version is bumped after refresh() following external non-schema write") {
- external_write(config, [](auto& wt) {
- wt.get_table("class_object")->create_object();
- });
-
- r->refresh();
- auto tv = cache_tv;
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1);
- }
-
- SECTION("schema is reread following refresh() over external schema change") {
- external_write(config, [](auto& wt) {
- wt.add_table("class_object 2");
- });
-
- r->refresh();
- auto tv = cache_tv;
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1);
- REQUIRE(cache_schema.size() == 2);
- REQUIRE(cache_schema.find("object 2") != cache_schema.end());
- }
-
- SECTION("update_schema() to version already on disk updates cache") {
- r->read_group();
- external_write(config, [](auto& wt) {
- auto table = wt.add_table("class_object 2");
- table->add_column(type_Int, "value");
- });
-
- auto tv = cache_tv;
- r->update_schema(schema2);
-
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1); // only +1 because update_schema() did not perform a write
- REQUIRE(cache_schema.size() == 2);
- REQUIRE(cache_schema.find("object 2") != cache_schema.end());
- }
-
- SECTION("update_schema() to version already on disk updates cache") {
- r->read_group();
- external_write(config, [](auto& wt) {
- auto table = wt.add_table("class_object 2");
- table->add_column(type_Int, "value");
- });
-
- auto tv = cache_tv;
- r->update_schema(schema2);
-
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1); // only +1 because update_schema() did not perform a write
- REQUIRE(cache_schema.size() == 2);
- REQUIRE(cache_schema.find("object 2") != cache_schema.end());
- }
-
- SECTION("update_schema() to version populated on disk while waiting for the write lock updates cache") {
- r->read_group();
-
- // We want to commit the write while we're waiting on the write lock on
- // this thread, which can't really be done in a properly synchronized manner
- std::chrono::microseconds wait_time{5000};
-#if REALM_ANDROID
- // When running on device or in an emulator we need to wait longer due
- // to them being slow
- wait_time *= 10;
-#endif
-
- bool did_run = false;
- JoiningThread thread([&] {
- ExternalWriter writer(config);
- if (writer.wt.get_table("class_object 2"))
- return;
- did_run = true;
-
- auto table = writer.wt.add_table("class_object 2");
- table->add_column(type_Int, "value");
- std::this_thread::sleep_for(wait_time * 2);
- writer.wt.commit();
- });
- std::this_thread::sleep_for(wait_time);
-
- auto tv = cache_tv;
- r->update_schema(Schema{
- {"object", {{"value", PropertyType::Int}}},
- {"object 2", {{"value", PropertyType::Int}}},
- });
-
- // just skip the test if the timing was wrong to avoid spurious failures
- if (!did_run)
- return;
-
- REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
- REQUIRE(cache_tv == tv + 1); // only +1 because update_schema()'s write was rolled back
- REQUIRE(cache_schema.size() == 2);
- REQUIRE(cache_schema.find("object 2") != cache_schema.end());
- }
-}
-
-TEST_CASE("SharedRealm: dynamic schema mode doesn't invalidate object schema pointers when schema hasn't changed") {
- TestFile config;
-
- // Prepopulate the Realm with the schema.
- Realm::Config config_with_schema = config;
- config_with_schema.schema_version = 1;
- config_with_schema.schema_mode = SchemaMode::Automatic;
- config_with_schema.schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{true}},
- {"value 2", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- auto r1 = Realm::get_shared_realm(config_with_schema);
-
- // Retrieve the object schema in dynamic mode.
- auto r2 = Realm::get_shared_realm(config);
- auto* object_schema = &*r2->schema().find("object");
-
- // Perform an empty write to create a new version, resulting in the other Realm needing to re-read the schema.
- r1->begin_transaction();
- r1->commit_transaction();
-
- // Advance to the latest version, and verify the object schema is at the same location in memory.
- r2->read_group();
- REQUIRE(object_schema == &*r2->schema().find("object"));
-}
-
-TEST_CASE("SharedRealm: SchemaChangedFunction") {
- struct Context : BindingContext {
- size_t* change_count;
- Schema* schema;
- Context(size_t* count_out, Schema* schema_out) : change_count(count_out), schema(schema_out) { }
-
- void schema_did_change(Schema const& changed_schema) override
- {
- ++*change_count;
- *schema = changed_schema;
- }
- };
-
- size_t schema_changed_called = 0;
- Schema changed_fixed_schema;
- TestFile config;
- auto dynamic_config = config;
-
- config.schema = Schema{
- {"object1", {
- {"value", PropertyType::Int},
- }},
- {"object2", {
- {"value", PropertyType::Int},
- }}
- };
- config.schema_version = 1;
- auto r1 = Realm::get_shared_realm(config);
- r1->read_group();
- r1->m_binding_context.reset(new Context(&schema_changed_called, &changed_fixed_schema));
-
- SECTION("Fixed schema") {
- SECTION("update_schema") {
- auto new_schema = Schema{
- {"object3", {
- {"value", PropertyType::Int},
- }}
- };
- r1->update_schema(new_schema, 2);
- REQUIRE(schema_changed_called == 1);
- REQUIRE(changed_fixed_schema.find("object3")->property_for_name("value")->column_key != ColKey{});
- }
-
- SECTION("Open a new Realm instance with same config won't trigger") {
- auto r2 = Realm::get_shared_realm(config);
- REQUIRE(schema_changed_called == 0);
- }
-
- SECTION("Non schema related transaction doesn't trigger") {
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->commit_transaction();
- r1->refresh();
- REQUIRE(schema_changed_called == 0);
- }
-
- SECTION("Schema is changed by another Realm") {
- auto r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- r2->read_group().get_table("class_object1")->add_column(type_String, "new col");
- r2->commit_transaction();
- r1->refresh();
- REQUIRE(schema_changed_called == 1);
- REQUIRE(changed_fixed_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
- }
-
- // This is not a valid use case. m_schema won't be refreshed.
- SECTION("Schema is changed by this Realm won't trigger") {
- r1->begin_transaction();
- r1->read_group().get_table("class_object1")->add_column(type_String, "new col");
- r1->commit_transaction();
- REQUIRE(schema_changed_called == 0);
- }
- }
-
- SECTION("Dynamic schema") {
- size_t dynamic_schema_changed_called = 0;
- Schema changed_dynamic_schema;
- auto r2 = Realm::get_shared_realm(dynamic_config);
- r2->m_binding_context.reset(new Context(&dynamic_schema_changed_called, &changed_dynamic_schema));
-
- SECTION("set_schema_subset") {
- auto new_schema = Schema{
- {"object1", {
- {"value", PropertyType::Int},
- }}
- };
- r2->set_schema_subset(new_schema);
- REQUIRE(schema_changed_called == 0);
- REQUIRE(dynamic_schema_changed_called == 1);
- REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
- }
-
- SECTION("Non schema related transaction will always trigger in dynamic mode") {
- auto r1 = Realm::get_shared_realm(config);
- // An empty transaction will trigger the schema changes always in dynamic mode.
- r1->begin_transaction();
- r1->commit_transaction();
- r2->refresh();
- REQUIRE(dynamic_schema_changed_called == 1);
- REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
- }
-
- SECTION("Schema is changed by another Realm") {
- r1->begin_transaction();
- r1->read_group().get_table("class_object1")->add_column(type_String, "new col");
- r1->commit_transaction();
- r2->refresh();
- REQUIRE(dynamic_schema_changed_called == 1);
- REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
- }
- }
-}
-
-#ifndef _WIN32
-TEST_CASE("SharedRealm: compact on launch") {
- // Make compactable Realm
- TestFile config;
- config.automatic_change_notifications = false;
- int num_opens = 0;
- config.should_compact_on_launch_function = [&](size_t total_bytes, size_t used_bytes) {
- REQUIRE(total_bytes > used_bytes);
- num_opens++;
- return num_opens != 2;
- };
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::String}
- }},
- };
- REQUIRE(num_opens == 0);
- auto r = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 1);
- r->begin_transaction();
- auto table = r->read_group().get_table("class_object");
- size_t count = 1000;
- for (size_t i = 0; i < count; ++i)
- table->create_object().set_all(util::format("Foo_%1", i % 10).c_str());
- r->commit_transaction();
- REQUIRE(table->size() == count);
- r->close();
-
- SECTION("compact reduces the file size") {
- // Confirm expected sizes before and after opening the Realm
- size_t size_before = size_t(File(config.path).get_size());
- r = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 2);
- r->close();
- REQUIRE(size_t(File(config.path).get_size()) == size_before); // File size after returning false
- r = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 3);
- REQUIRE(size_t(File(config.path).get_size()) < size_before); // File size after returning true
-
- // Validate that the file still contains what it should
- REQUIRE(r->read_group().get_table("class_object")->size() == count);
-
- // Registering for a collection notification shouldn't crash when compact on launch is used.
- Results results(r, r->read_group().get_table("class_object"));
- results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) { });
- r->close();
- }
-
- SECTION("compact function does not get invoked if realm is open on another thread") {
- config.scheduler = util::Scheduler::get_frozen();
- r = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 2);
- std::thread([&]{
- auto r2 = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 2);
- }).join();
- r->close();
- std::thread([&]{
- auto r3 = Realm::get_shared_realm(config);
- REQUIRE(num_opens == 3);
- }).join();
- }
-}
-#endif
-
-struct ModeAutomatic {
- static SchemaMode mode() { return SchemaMode::Automatic; }
- static bool should_call_init_on_version_bump() { return false; }
-};
-struct ModeAdditive {
- static SchemaMode mode() { return SchemaMode::Additive; }
- static bool should_call_init_on_version_bump() { return false; }
-};
-struct ModeManual {
- static SchemaMode mode() { return SchemaMode::Manual; }
- static bool should_call_init_on_version_bump() { return false; }
-};
-struct ModeResetFile {
- static SchemaMode mode() { return SchemaMode::ResetFile; }
- static bool should_call_init_on_version_bump() { return true; }
-};
-
-TEMPLATE_TEST_CASE("SharedRealm: update_schema with initialization_function", "[init][update_schema]",
- ModeAutomatic, ModeAdditive, ModeManual, ModeResetFile) {
- TestFile config;
- config.schema_mode = TestType::mode();
- bool initialization_function_called = false;
- uint64_t schema_version_in_callback = -1;
- Schema schema_in_callback;
- auto initialization_function = [&initialization_function_called, &schema_version_in_callback,
- &schema_in_callback](auto shared_realm) {
- REQUIRE(shared_realm->is_in_transaction());
- initialization_function_called = true;
- schema_version_in_callback = shared_realm->schema_version();
- schema_in_callback = shared_realm->schema();
- };
-
- Schema schema{
- {"object", {
- {"value", PropertyType::String}
- }},
- };
-
- SECTION("call initialization function directly by update_schema") {
- // Open in dynamic mode with no schema specified
- auto realm = Realm::get_shared_realm(config);
- REQUIRE_FALSE(initialization_function_called);
-
- realm->update_schema(schema, 0, nullptr, initialization_function);
- REQUIRE(initialization_function_called);
- REQUIRE(schema_version_in_callback == 0);
- REQUIRE(schema_in_callback.compare(schema).size() == 0);
- }
-
- config.schema_version = 0;
- config.schema = schema;
-
- SECTION("initialization function should be called for unversioned realm") {
- config.initialization_function = initialization_function;
- Realm::get_shared_realm(config);
- REQUIRE(initialization_function_called);
- REQUIRE(schema_version_in_callback == 0);
- REQUIRE(schema_in_callback.compare(schema).size() == 0);
- }
-
- SECTION("initialization function for versioned realm") {
- // Initialize v0
- Realm::get_shared_realm(config);
-
- config.schema_version = 1;
- config.initialization_function = initialization_function;
- Realm::get_shared_realm(config);
- REQUIRE(initialization_function_called == TestType::should_call_init_on_version_bump());
- if (TestType::should_call_init_on_version_bump()) {
- REQUIRE(schema_version_in_callback == 1);
- REQUIRE(schema_in_callback.compare(schema).size() == 0);
- }
- }
-}
-
-TEST_CASE("BindingContext is notified about delivery of change notifications") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
-
- SECTION("BindingContext notified even if no callbacks are registered") {
- static int binding_context_start_notify_calls = 0;
- static int binding_context_end_notify_calls = 0;
- struct Context : BindingContext {
- void will_send_notifications() override
- {
- ++binding_context_start_notify_calls;
- }
-
- void did_send_notifications() override
- {
- ++binding_context_end_notify_calls;
- }
- };
- r->m_binding_context.reset(new Context());
-
- SECTION("local commit") {
- binding_context_start_notify_calls = 0;
- binding_context_end_notify_calls = 0;
- coordinator->on_change();
- r->begin_transaction();
- REQUIRE(binding_context_start_notify_calls == 1);
- REQUIRE(binding_context_end_notify_calls == 1);
- r->cancel_transaction();
- }
-
- SECTION("remote commit") {
- binding_context_start_notify_calls = 0;
- binding_context_end_notify_calls = 0;
- JoiningThread([&] {
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- auto table2 = r2->read_group().get_table("class_object");
- table2->create_object();
- r2->commit_transaction();
- });
- advance_and_notify(*r);
- REQUIRE(binding_context_start_notify_calls == 1);
- REQUIRE(binding_context_end_notify_calls == 1);
- }
- }
-
- SECTION("notify BindingContext before and after sending notifications") {
- static int binding_context_start_notify_calls = 0;
- static int binding_context_end_notify_calls = 0;
- static int notification_calls = 0;
-
- auto col = table->get_column_key("value");
- Results results1(r, table->where().greater_equal(col, 0));
- Results results2(r, table->where().less(col, 10));
-
- auto token1 = results1.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- ++notification_calls;
- });
-
- auto token2 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- ++notification_calls;
- });
-
- struct Context : BindingContext {
- void will_send_notifications() override
- {
- REQUIRE(notification_calls == 0);
- REQUIRE(binding_context_end_notify_calls == 0);
- ++binding_context_start_notify_calls;
- }
-
- void did_send_notifications() override
- {
- REQUIRE(notification_calls == 2);
- REQUIRE(binding_context_start_notify_calls == 1);
- ++binding_context_end_notify_calls;
- }
- };
- r->m_binding_context.reset(new Context());
-
- SECTION("local commit") {
- binding_context_start_notify_calls = 0;
- binding_context_end_notify_calls = 0;
- notification_calls = 0;
- coordinator->on_change();
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
- REQUIRE(binding_context_start_notify_calls == 1);
- REQUIRE(binding_context_end_notify_calls == 1);
- }
-
- SECTION("remote commit") {
- binding_context_start_notify_calls = 0;
- binding_context_end_notify_calls = 0;
- notification_calls = 0;
- JoiningThread([&] {
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- auto table2 = r2->read_group().get_table("class_object");
- table2->create_object();
- r2->commit_transaction();
- });
- advance_and_notify(*r);
- REQUIRE(binding_context_start_notify_calls == 1);
- REQUIRE(binding_context_end_notify_calls == 1);
- }
- }
-
- SECTION("did_send() is skipped if the Realm is closed first") {
- Results results(r, table->where());
- bool do_close = true;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- if (do_close)
- r->close();
- });
-
- struct FailOnDidSend : BindingContext {
- void did_send_notifications() override
- {
- FAIL("did_send_notifications() should not have been called");
- }
- };
- struct CloseOnWillChange : FailOnDidSend {
- Realm& realm;
- CloseOnWillChange(Realm& realm) : realm(realm) {}
-
- void will_send_notifications() override
- {
- realm.close();
- }
- };
-
- SECTION("closed in notification callback for notify()") {
- r->m_binding_context.reset(new FailOnDidSend);
- coordinator->on_change();
- r->notify();
- }
-
- SECTION("closed in notification callback for refresh()") {
- do_close = false;
- coordinator->on_change();
- r->notify();
- do_close = true;
-
- JoiningThread([&] {
- auto r = coordinator->get_realm(util::Scheduler::get_frozen());
- r->begin_transaction();
- r->read_group().get_table("class_object")->create_object();
- r->commit_transaction();
- });
-
- r->m_binding_context.reset(new FailOnDidSend);
- coordinator->on_change();
- r->refresh();
- }
-
- SECTION("closed in will_send() for notify()") {
- r->m_binding_context.reset(new CloseOnWillChange(*r));
- coordinator->on_change();
- r->notify();
- }
-
- SECTION("closed in will_send() for refresh()") {
- do_close = false;
- coordinator->on_change();
- r->notify();
- do_close = true;
-
- JoiningThread([&] {
- auto r = coordinator->get_realm(util::Scheduler::get_frozen());
- r->begin_transaction();
- r->read_group().get_table("class_object")->create_object();
- r->commit_transaction();
- });
-
- r->m_binding_context.reset(new CloseOnWillChange(*r));
- coordinator->on_change();
- r->refresh();
- }
- }
-}
-
-TEST_CASE("Statistics on Realms") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- // config.cache = false;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- SECTION("compute_size") {
- auto s = r->read_group().compute_aggregated_byte_size();
- REQUIRE(s > 0);
- }
-}
-
-#if REALM_PLATFORM_APPLE && NOTIFIER_BACKGROUND_ERRORS
-TEST_CASE("BindingContext is notified in case of notifier errors") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- class OpenFileLimiter {
- public:
- OpenFileLimiter()
- {
- // Set the max open files to zero so that opening new files will fail
- getrlimit(RLIMIT_NOFILE, &m_old);
- rlimit rl = m_old;
- rl.rlim_cur = 0;
- setrlimit(RLIMIT_NOFILE, &rl);
- }
-
- ~OpenFileLimiter()
- {
- setrlimit(RLIMIT_NOFILE, &m_old);
- }
-
- private:
- rlimit m_old;
- };
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
- Results results(r, *r->read_group().get_table("class_object"));
- static int binding_context_start_notify_calls = 0;
- static int binding_context_end_notify_calls = 0;
- static bool error_called = false;
- struct Context : BindingContext {
- void will_send_notifications() override
- {
- REQUIRE_FALSE(error_called);
- ++binding_context_start_notify_calls;
- }
-
- void did_send_notifications() override
- {
- REQUIRE(error_called);
- ++binding_context_end_notify_calls;
- }
- };
- r->m_binding_context.reset(new Context());
-
- SECTION("realm on background thread could not be opened") {
- OpenFileLimiter limiter;
-
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(error_called);
- error_called = true;
- });
- advance_and_notify(*r);
- REQUIRE(error_called);
- REQUIRE(binding_context_start_notify_calls == 1);
- REQUIRE(binding_context_end_notify_calls == 1);
- }
-}
-#endif
-
-TEST_CASE("RealmCoordinator: get_unbound_realm()") {
- TestFile config;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int}
- }},
- };
-
- ThreadSafeReference ref;
- std::thread([&] { ref = _impl::RealmCoordinator::get_coordinator(config)->get_unbound_realm(); }).join();
-
- SECTION("checks thread after being resolved") {
- auto realm = Realm::get_shared_realm(std::move(ref));
- REQUIRE_NOTHROW(realm->verify_thread());
- std::thread([&] {
- REQUIRE_THROWS(realm->verify_thread());
- }).join();
- }
-
- SECTION("delivers notifications to the thread it is resolved on") {
- if (!util::EventLoop::has_implementation())
- return;
- auto realm = Realm::get_shared_realm(std::move(ref));
- Results results(realm, ObjectStore::table_for_object_type(realm->read_group(), "object")->where());
- bool called = false;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- called = true;
- });
- util::EventLoop::main().run_until([&] { return called; });
- }
-
- SECTION("resolves to a new Realm if caching is disabled") {
- auto r1 = Realm::get_shared_realm(config);
- auto r2 = Realm::get_shared_realm(std::move(ref));
- REQUIRE(r1 != r2);
-
- // New unbound with cache disabled
- std::thread([&] { ref = _impl::RealmCoordinator::get_coordinator(config)->get_unbound_realm(); }).join();
- auto r3 = Realm::get_shared_realm(std::move(ref));
- REQUIRE(r1 != r3);
- REQUIRE(r2 != r3);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp
deleted file mode 100644
index 5327a050c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp
+++ /dev/null
@@ -1,3261 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/event_loop.hpp"
-#include "util/index_helpers.hpp"
-#include "util/test_file.hpp"
-
-#include "impl/object_accessor_impl.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "binding_context.hpp"
-#include "object_schema.hpp"
-#include "property.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "util/scheduler.hpp"
-
-#include <realm/db.hpp>
-#include <realm/group.hpp>
-#include <realm/query_engine.hpp>
-#include <realm/query_expression.hpp>
-
-#if REALM_ENABLE_SYNC
-#include "sync/sync_manager.hpp"
-#include "sync/sync_session.hpp"
-#endif
-
-namespace realm {
-class TestHelper {
-public:
- static DBRef& get_shared_group(SharedRealm const& shared_realm)
- {
- return Realm::Internal::get_db(*shared_realm);
- }
-};
-}
-
-using namespace realm;
-using namespace std::string_literals;
-
-namespace {
- using AnyDict = std::map<std::string, util::Any>;
- using AnyVec = std::vector<util::Any>;
-}
-
-struct TestContext : CppContext {
- std::map<std::string, AnyDict> defaults;
-
- using CppContext::CppContext;
- TestContext(TestContext& parent, realm::Property const& prop)
- : CppContext(parent, prop)
- , defaults(parent.defaults)
- { }
-
- void will_change(Object const&, Property const&) {}
- void did_change() {}
- std::string print(util::Any) { return "not implemented"; }
- bool allow_missing(util::Any) { return false; }
-};
-
-
-TEST_CASE("notifications: async delivery") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
- auto col = table->get_column_key("value");
-
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- table->create_object().set_all(i * 2);
- r->commit_transaction();
-
- Results results(r, table->where().greater(col, 0).less(col, 10));
-
- int notification_calls = 0;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- ++notification_calls;
- });
-
- auto make_local_change = [&] {
- r->begin_transaction();
- table->begin()->set(col, 4);
- r->commit_transaction();
- };
-
- auto make_remote_change = [&] {
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->begin()->set(col, 5);
- r2->commit_transaction();
- };
-
- SECTION("initial notification") {
- SECTION("is delivered on notify()") {
- REQUIRE(notification_calls == 0);
- advance_and_notify(*r);
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("is delivered on refresh()") {
- coordinator->on_change();
- REQUIRE(notification_calls == 0);
- r->refresh();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("is delivered on begin_transaction()") {
- coordinator->on_change();
- REQUIRE(notification_calls == 0);
- r->begin_transaction();
- REQUIRE(notification_calls == 1);
- r->cancel_transaction();
- }
-
- SECTION("is delivered on notify() even with autorefresh disabled") {
- r->set_auto_refresh(false);
- REQUIRE(notification_calls == 0);
- advance_and_notify(*r);
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("refresh() blocks due to initial results not being ready") {
- REQUIRE(notification_calls == 0);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->refresh();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("begin_transaction() blocks due to initial results not being ready") {
- REQUIRE(notification_calls == 0);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->begin_transaction();
- REQUIRE(notification_calls == 1);
- r->cancel_transaction();
- }
-
- SECTION("notify() does not block due to initial results not being ready") {
- REQUIRE(notification_calls == 0);
- r->notify();
- REQUIRE(notification_calls == 0);
- }
-
- SECTION("is delivered after invalidate()") {
- r->invalidate();
-
- SECTION("notify()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->notify();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("notify() without autorefresh") {
- r->set_auto_refresh(false);
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->notify();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("refresh()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->refresh();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("begin_transaction()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->begin_transaction();
- REQUIRE(notification_calls == 1);
- r->cancel_transaction();
- }
- }
-
- SECTION("is delivered by notify() even if there are later versions") {
- REQUIRE(notification_calls == 0);
- coordinator->on_change();
- make_remote_change();
- r->notify();
- REQUIRE(notification_calls == 1);
- }
- }
-
- advance_and_notify(*r);
-
- SECTION("notifications for local changes") {
- make_local_change();
- coordinator->on_change();
- REQUIRE(notification_calls == 1);
-
- SECTION("notify()") {
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("notify() without autorefresh") {
- r->set_auto_refresh(false);
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh()") {
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("begin_transaction()") {
- r->begin_transaction();
- REQUIRE(notification_calls == 2);
- r->cancel_transaction();
- }
- }
-
- SECTION("notifications for remote changes") {
- make_remote_change();
- coordinator->on_change();
- REQUIRE(notification_calls == 1);
-
- SECTION("notify()") {
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("notify() without autorefresh") {
- r->set_auto_refresh(false);
- r->notify();
- REQUIRE(notification_calls == 1);
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh()") {
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("begin_transaction()") {
- r->begin_transaction();
- REQUIRE(notification_calls == 2);
- r->cancel_transaction();
- }
- }
-
- SECTION("notifications are not delivered when the token is destroyed before they are calculated") {
- make_remote_change();
- REQUIRE(notification_calls == 1);
- token = {};
- advance_and_notify(*r);
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("notifications are not delivered when the token is destroyed before they are delivered") {
- make_remote_change();
- REQUIRE(notification_calls == 1);
- coordinator->on_change();
- token = {};
- r->notify();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("notifications are delivered on the next cycle when a new callback is added from within a callback") {
- NotificationToken token2, token3;
- bool called = false;
- token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = {};
- token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- called = true;
- });
- });
-
- advance_and_notify(*r);
- REQUIRE_FALSE(called);
- advance_and_notify(*r);
- REQUIRE(called);
- }
-
- SECTION("notifications are delivered on the next cycle when a new callback is added from within a callback") {
- auto results2 = results;
- auto results3 = results;
- NotificationToken token2, token3, token4;
-
- bool called = false;
- auto check = [&](Results& outer, Results& inner) {
- token2 = outer.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = {};
- token3 = inner.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- called = true;
- });
- });
-
- advance_and_notify(*r);
- REQUIRE_FALSE(called);
- advance_and_notify(*r);
- REQUIRE(called);
- };
-
- SECTION("same Results") {
- check(results, results);
- }
-
- SECTION("Results which has never had a notifier") {
- check(results, results2);
- }
-
- SECTION("Results which used to have callbacks but no longer does") {
- SECTION("notifier before active") {
- token3 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token3 = {};
- });
- check(results3, results2);
- }
- SECTION("notifier after active") {
- token3 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token3 = {};
- });
- check(results, results2);
- }
- }
-
- SECTION("Results which already has callbacks") {
- SECTION("notifier before active") {
- token4 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
- check(results3, results2);
- }
- SECTION("notifier after active") {
- token4 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
- check(results, results2);
- }
- }
- }
-
- SECTION("remote changes made before adding a callback from within a callback are not reported") {
- NotificationToken token2, token3;
- bool called = false;
- token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = {};
- make_remote_change();
- coordinator->on_change();
- token3 = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- called = true;
- REQUIRE(c.empty());
- REQUIRE(table->begin()->get<int64_t>(col) == 5);
- });
- });
-
- advance_and_notify(*r);
- REQUIRE_FALSE(called);
- advance_and_notify(*r);
- REQUIRE(called);
- }
-
- SECTION("notifications are not delivered when a callback is removed from within a callback") {
- NotificationToken token2, token3;
- token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token3 = {};
- });
- token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- REQUIRE(false);
- });
-
- advance_and_notify(*r);
- }
-
- SECTION("removing the current callback does not stop later ones from being called") {
- NotificationToken token2, token3;
- bool called = false;
- token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = {};
- });
- token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- called = true;
- });
-
- advance_and_notify(*r);
-
- REQUIRE(called);
- }
-
- SECTION("the first call of a notification can include changes if it previously ran for a different callback") {
- r->begin_transaction();
- auto token2 = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
- REQUIRE(!c.empty());
- });
-
- table->create_object().set(col, 5);
- r->commit_transaction();
- advance_and_notify(*r);
- }
-
- SECTION("handling of results not ready") {
- make_remote_change();
-
- SECTION("notify() does nothing") {
- r->notify();
- REQUIRE(notification_calls == 1);
- coordinator->on_change();
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh() blocks") {
- REQUIRE(notification_calls == 1);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh() advances to the first version with notifiers ready that is at least a recent as the newest at the time it is called") {
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- make_remote_change();
- coordinator->on_change();
- make_remote_change();
- });
- // advances to the version after the one it was waiting for, but still
- // not the latest
- r->refresh();
- REQUIRE(notification_calls == 2);
-
- thread.join();
- REQUIRE(notification_calls == 2);
-
- // now advances to the latest
- coordinator->on_change();
- r->refresh();
- REQUIRE(notification_calls == 3);
- }
-
- SECTION("begin_transaction() blocks") {
- REQUIRE(notification_calls == 1);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->begin_transaction();
- REQUIRE(notification_calls == 2);
- r->cancel_transaction();
- }
-
- SECTION("refresh() does not block for results without callbacks") {
- token = {};
- // this would deadlock if it waits for the notifier to be ready
- r->refresh();
- }
-
- SECTION("begin_transaction() does not block for results without callbacks") {
- token = {};
- // this would deadlock if it waits for the notifier to be ready
- r->begin_transaction();
- r->cancel_transaction();
- }
-
- SECTION("begin_transaction() does not block for Results for different Realms") {
- // this would deadlock if beginning the write on the secondary Realm
- // waited for the primary Realm to be ready
- make_remote_change();
-
- // sanity check that the notifications never did run
- r->notify();
- REQUIRE(notification_calls == 1);
- }
- }
-
- SECTION("handling of stale results") {
- make_remote_change();
- coordinator->on_change();
- make_remote_change();
-
- SECTION("notify() uses the older version") {
- r->notify();
- REQUIRE(notification_calls == 2);
- coordinator->on_change();
- r->notify();
- REQUIRE(notification_calls == 3);
- r->notify();
- REQUIRE(notification_calls == 3);
- }
-
- SECTION("refresh() blocks") {
- REQUIRE(notification_calls == 1);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("begin_transaction() blocks") {
- REQUIRE(notification_calls == 1);
- JoiningThread thread([&] {
- std::this_thread::sleep_for(std::chrono::microseconds(5000));
- coordinator->on_change();
- });
- r->begin_transaction();
- REQUIRE(notification_calls == 2);
- r->cancel_transaction();
- }
- }
-
- SECTION("updates are delivered after invalidate()") {
- r->invalidate();
- make_remote_change();
-
- SECTION("notify()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("notify() without autorefresh") {
- r->set_auto_refresh(false);
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->notify();
- REQUIRE(notification_calls == 1);
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->refresh();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("begin_transaction()") {
- coordinator->on_change();
- REQUIRE_FALSE(r->is_in_read_transaction());
- r->begin_transaction();
- REQUIRE(notification_calls == 2);
- r->cancel_transaction();
- }
- }
-
- SECTION("refresh() from within changes_available() do not interfere with notification delivery") {
- struct Context : BindingContext {
- Realm& realm;
- Context(Realm& realm) : realm(realm) { }
-
- void changes_available() override
- {
- REQUIRE(realm.refresh());
- }
- };
-
- make_remote_change();
- coordinator->on_change();
-
- r->set_auto_refresh(false);
- REQUIRE(notification_calls == 1);
-
- r->notify();
- REQUIRE(notification_calls == 1);
-
- r->m_binding_context.reset(new Context(*r));
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("refresh() from within a notification is a no-op") {
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- REQUIRE_FALSE(r->refresh()); // would deadlock if it actually tried to refresh
- });
- advance_and_notify(*r);
- make_remote_change(); // 1
- coordinator->on_change();
- make_remote_change(); // 2
- r->notify(); // advances to version from 1
- coordinator->on_change();
- REQUIRE(r->refresh()); // advances to version from 2
- REQUIRE_FALSE(r->refresh()); // does not advance since it's now up-to-date
- }
-
- SECTION("begin_transaction() from within a notification does not send notifications immediately") {
- bool first = true;
- auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (first)
- first = false;
- else {
- // would deadlock if it tried to send notifications as they aren't ready yet
- r->begin_transaction();
- r->cancel_transaction();
- }
- });
- advance_and_notify(*r);
-
- make_remote_change(); // 1
- coordinator->on_change();
- make_remote_change(); // 2
- r->notify(); // advances to version from 1
- REQUIRE(notification_calls == 2);
- coordinator->on_change();
- REQUIRE_FALSE(r->refresh()); // we made the commit locally, so no advancing here
- REQUIRE(notification_calls == 3);
- }
-
- SECTION("begin_transaction() from within a notification does not break delivering additional notifications") {
- size_t calls = 0;
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (++calls == 1)
- return;
-
- // force the read version to advance by beginning a transaction
- r->begin_transaction();
- r->cancel_transaction();
- });
-
- auto results2 = results;
- size_t calls2 = 0;
- auto token2 = results2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (++calls2 == 1)
- return;
- REQUIRE_INDICES(c.insertions, 0);
- });
- advance_and_notify(*r);
- REQUIRE(calls == 1);
- REQUIRE(calls2 == 1);
-
- make_remote_change(); // 1
- coordinator->on_change();
- make_remote_change(); // 2
- r->notify(); // advances to version from 1
-
- REQUIRE(calls == 2);
- REQUIRE(calls2 == 2);
- }
-
- SECTION("begin_transaction() from within did_change() does not break delivering collection notification") {
- struct Context : BindingContext {
- Realm& realm;
- Context(Realm& realm) : realm(realm) { }
-
- void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
- {
- if (!realm.is_in_transaction()) {
- // advances to version from 2 (and recursively calls this, hence the check above)
- realm.begin_transaction();
- realm.cancel_transaction();
- }
- }
- };
- r->m_binding_context.reset(new Context(*r));
-
- make_remote_change(); // 1
- coordinator->on_change();
- make_remote_change(); // 2
- r->notify(); // advances to version from 1
- }
-
- SECTION("is_in_transaction() is reported correctly within a notification from begin_transaction() and changes can be made") {
- bool first = true;
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (first) {
- REQUIRE_FALSE(r->is_in_transaction());
- first = false;
- }
- else {
- REQUIRE(r->is_in_transaction());
- table->begin()->set(col, 100);
- }
- });
- advance_and_notify(*r);
- make_remote_change();
- coordinator->on_change();
- r->begin_transaction();
- REQUIRE(table->begin()->get<int64_t>(col) == 100);
- r->cancel_transaction();
- REQUIRE(table->begin()->get<int64_t>(col) != 100);
- }
-
- SECTION("invalidate() from within notification is a no-op") {
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- r->invalidate();
- REQUIRE(r->is_in_read_transaction());
- });
- advance_and_notify(*r);
- REQUIRE(r->is_in_read_transaction());
- make_remote_change();
- coordinator->on_change();
- r->begin_transaction();
- REQUIRE(r->is_in_transaction());
- r->cancel_transaction();
- }
-
- SECTION("cancel_transaction() from within notification ends the write transaction started by begin_transaction()") {
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (r->is_in_transaction())
- r->cancel_transaction();
- });
- advance_and_notify(*r);
- make_remote_change();
- coordinator->on_change();
- r->begin_transaction();
- REQUIRE_FALSE(r->is_in_transaction());
- }
-}
-
-TEST_CASE("notifications: skip") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
- auto col = table->get_column_key("value");
-
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- table->create_object().set(col, i * 2);
- r->commit_transaction();
-
- Results results(r, table->where());
-
- auto add_callback = [](Results& results, int& calls, CollectionChangeSet& changes) {
- return results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- ++calls;
- changes = std::move(c);
- });
- };
-
- auto make_local_change = [&](auto& token) {
- r->begin_transaction();
- table->create_object();
- token.suppress_next();
- r->commit_transaction();
- };
-
- auto make_remote_change = [&] {
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
- };
-
- int calls1 = 0;
- CollectionChangeSet changes1;
- auto token1 = add_callback(results, calls1, changes1);
-
- SECTION("no notification is sent when only callback is skipped") {
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
-
- make_local_change(token1);
- advance_and_notify(*r);
-
- REQUIRE(calls1 == 1);
- REQUIRE(changes1.empty());
- }
-
- SECTION("unskipped tokens for the same Results are still delivered") {
- int calls2 = 0;
- CollectionChangeSet changes2;
- auto token2 = add_callback(results, calls2, changes2);
-
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
- REQUIRE(calls2 == 1);
-
- make_local_change(token1);
- advance_and_notify(*r);
-
- REQUIRE(calls1 == 1);
- REQUIRE(changes1.empty());
- REQUIRE(calls2 == 2);
- REQUIRE_INDICES(changes2.insertions, 10);
- }
-
- SECTION("unskipped tokens for different Results are still delivered") {
- Results results2(r, table->where());
- int calls2 = 0;
- CollectionChangeSet changes2;
- auto token2 = add_callback(results2, calls2, changes2);
-
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
- REQUIRE(calls2 == 1);
-
- make_local_change(token1);
- advance_and_notify(*r);
-
- REQUIRE(calls1 == 1);
- REQUIRE(changes1.empty());
- REQUIRE(calls2 == 2);
- REQUIRE_INDICES(changes2.insertions, 10);
- }
-
- SECTION("additional commits which occur before calculation are merged in") {
- int calls2 = 0;
- CollectionChangeSet changes2;
- auto token2 = add_callback(results, calls2, changes2);
-
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
- REQUIRE(calls2 == 1);
-
- make_local_change(token1);
- make_remote_change();
- advance_and_notify(*r);
-
- REQUIRE(calls1 == 2);
- REQUIRE_INDICES(changes1.insertions, 11);
- REQUIRE(calls2 == 2);
- REQUIRE_INDICES(changes2.insertions, 10, 11);
- }
-
- SECTION("additional commits which occur before delivery are merged in") {
- int calls2 = 0;
- CollectionChangeSet changes2;
- auto token2 = add_callback(results, calls2, changes2);
-
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
- REQUIRE(calls2 == 1);
-
- make_local_change(token1);
- coordinator->on_change();
- make_remote_change();
- advance_and_notify(*r);
-
- REQUIRE(calls1 == 2);
- REQUIRE_INDICES(changes1.insertions, 11);
- REQUIRE(calls2 == 2);
- REQUIRE_INDICES(changes2.insertions, 10, 11);
- }
-
- SECTION("skipping must be done from within a write transaction") {
- REQUIRE_THROWS(token1.suppress_next());
- }
-
- SECTION("skipping must be done from the Realm's thread") {
- advance_and_notify(*r);
- r->begin_transaction();
- std::thread([&] {
- REQUIRE_THROWS(token1.suppress_next());
- }).join();
- r->cancel_transaction();
- }
-
- SECTION("new notifiers do not interfere with skipping") {
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
-
- CollectionChangeSet changes;
-
- // new notifier at a version before the skipped one
- auto r2 = coordinator->get_realm();
- Results results2(r2, r2->read_group().get_table("class_object")->where());
- int calls2 = 0;
- auto token2 = add_callback(results2, calls2, changes);
-
- make_local_change(token1);
-
- // new notifier at the skipped version
- auto r3 = coordinator->get_realm();
- Results results3(r3, r3->read_group().get_table("class_object")->where());
- int calls3 = 0;
- auto token3 = add_callback(results3, calls3, changes);
-
- make_remote_change();
-
- // new notifier at version after the skipped one
- auto r4 = coordinator->get_realm();
- Results results4(r4, r4->read_group().get_table("class_object")->where());
- int calls4 = 0;
- auto token4 = add_callback(results4, calls4, changes);
-
- coordinator->on_change();
- r->notify();
- r2->notify();
- r3->notify();
- r4->notify();
-
- REQUIRE(calls1 == 2);
- REQUIRE(calls2 == 1);
- REQUIRE(calls3 == 1);
- REQUIRE(calls4 == 1);
- }
-
- SECTION("skipping only effects the current transaction even if no notification would occur anyway") {
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
-
- // would not produce a notification even if it wasn't skipped because no changes were made
- r->begin_transaction();
- token1.suppress_next();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
-
- // should now produce a notification
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
- advance_and_notify(*r);
- REQUIRE(calls1 == 2);
- }
-
- SECTION("removing skipped notifier before it gets the chance to run") {
- advance_and_notify(*r);
- REQUIRE(calls1 == 1);
-
- // Set the skip version
- make_local_change(token1);
- // Advance the file to a version after the skip version
- make_remote_change();
- REQUIRE(calls1 == 1);
-
- // Remove the skipped notifier and add an entirely new notifier, so that
- // notifications need to run but the skip logic shouldn't be used
- token1 = {};
- results = {};
- Results results2(r, table->where());
- auto token2 = add_callback(results2, calls1, changes1);
-
- advance_and_notify(*r);
- REQUIRE(calls1 == 2);
- }
-}
-
-TEST_CASE("notifications: TableView delivery") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- config.max_number_of_active_versions = 5;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
- auto col = table->get_column_key("value");
-
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- table->create_object().set(col, i * 2);
- r->commit_transaction();
-
- Results results(r, table->where());
- results.set_update_policy(Results::UpdatePolicy::AsyncOnly);
-
- SECTION("Initial run never happens with no callbacks") {
- advance_and_notify(*r);
- REQUIRE(results.get_mode() == Results::Mode::Query);
- }
-
- results.evaluate_query_if_needed();
- // Create and immediately remove a callback so that the notifier gets created
- // even though we have automatic change notifications disabled
- static_cast<void>(results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {}));
- REQUIRE(results.get_mode() == Results::Mode::TableView);
- REQUIRE(results.size() == 0);
-
- auto make_local_change = [&] {
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
- };
-
- auto make_remote_change = [&] {
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
- };
-
- SECTION("does not update after local change with no on_change") {
- make_local_change();
- REQUIRE(results.size() == 0);
- }
-
- SECTION("TV is delivered when no commit is made") {
- advance_and_notify(*r);
- REQUIRE(results.get_mode() == Results::Mode::TableView);
- REQUIRE(results.size() == 10);
- }
-
- SECTION("TV is not delivered when notifier version > local version") {
- make_remote_change();
- r->refresh();
- REQUIRE(results.size() == 0);
- }
-
- SECTION("TV is delivered when notifier version = local version") {
- make_remote_change();
- advance_and_notify(*r);
- REQUIRE(results.size() == 11);
- }
-
- SECTION("TV is delivered when previous TV wasn't used due to never refreshing") {
- // These two generate TVs that never get used
- make_remote_change();
- on_change_but_no_notify(*r);
- make_remote_change();
- on_change_but_no_notify(*r);
-
- // But we generate a third one anyway because the main thread never even
- // got a chance to use them, rather than it not wanting them
- make_remote_change();
- advance_and_notify(*r);
-
- REQUIRE(results.size() == 13);
- }
-
- SECTION("TV is not delivered when main thread refreshed but previous TV was not used") {
- // First run generates a TV that's unused
- make_remote_change();
- advance_and_notify(*r);
-
- // When the second run is delivered we discover first run wasn't used
- make_remote_change();
- advance_and_notify(*r);
-
- // And then third one doesn't run at all
- make_remote_change();
- advance_and_notify(*r);
-
- // And we can't use the old TV because it's out of date
- REQUIRE(results.size() == 0);
-
- // We don't start implicitly updating again even after it is used
- make_remote_change();
- advance_and_notify(*r);
- REQUIRE(results.size() == 0);
- }
-
- SECTION("TV can be delivered in a write transaction") {
- make_remote_change();
- advance_and_notify(*r);
- r->begin_transaction();
- REQUIRE(results.size() == 11);
- r->cancel_transaction();
- }
-
- SECTION("unused background TVs do not pin old versions forever") {
- // This will exceed the maximum active version count (5) if any
- // transactions are being pinned, resulting in make_remote_change() throwing
- for (int i = 0; i < 10; ++i) {
- REQUIRE_NOTHROW(make_remote_change());
- advance_and_notify(*r);
- }
- }
-}
-
-
-#if REALM_PLATFORM_APPLE && NOTIFIER_BACKGROUND_ERRORS
-TEST_CASE("notifications: async error handling") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- Results results(r, *r->read_group().get_table("class_object"));
-
- auto r2 = Realm::get_shared_realm(config);
-
- class OpenFileLimiter {
- public:
- OpenFileLimiter()
- {
- // Set the max open files to zero so that opening new files will fail
- getrlimit(RLIMIT_NOFILE, &m_old);
- rlimit rl = m_old;
- rl.rlim_cur = 0;
- setrlimit(RLIMIT_NOFILE, &rl);
- }
-
- ~OpenFileLimiter()
- {
- setrlimit(RLIMIT_NOFILE, &m_old);
- }
-
- private:
- rlimit m_old;
- };
-
- SECTION("error when opening the advancer SG") {
- OpenFileLimiter limiter;
-
- bool called = false;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called);
- called = true;
- });
- REQUIRE(!called);
-
- SECTION("error is delivered on notify() without changes") {
- coordinator->on_change();
- REQUIRE(!called);
- r->notify();
- REQUIRE(called);
- }
-
- SECTION("error is delivered on notify() with changes") {
- r2->begin_transaction(); r2->commit_transaction();
- REQUIRE(!called);
- coordinator->on_change();
- REQUIRE(!called);
- r->notify();
- REQUIRE(called);
- }
-
- SECTION("error is delivered on refresh() without changes") {
- coordinator->on_change();
- REQUIRE(!called);
- r->refresh();
- REQUIRE(called);
- }
-
- SECTION("error is delivered on refresh() with changes") {
- r2->begin_transaction(); r2->commit_transaction();
- REQUIRE(!called);
- coordinator->on_change();
- REQUIRE(!called);
- r->refresh();
- REQUIRE(called);
- }
-
- SECTION("error is delivered on begin_transaction() without changes") {
- coordinator->on_change();
- REQUIRE(!called);
- r->begin_transaction();
- REQUIRE(called);
- r->cancel_transaction();
- }
-
- SECTION("error is delivered on begin_transaction() with changes") {
- r2->begin_transaction(); r2->commit_transaction();
- REQUIRE(!called);
- coordinator->on_change();
- REQUIRE(!called);
- r->begin_transaction();
- REQUIRE(called);
- r->cancel_transaction();
- }
-
- SECTION("adding another callback sends the error to only the newly added one") {
- advance_and_notify(*r);
- REQUIRE(called);
-
- bool called2 = false;
- auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called2);
- called2 = true;
- });
-
- advance_and_notify(*r);
- REQUIRE(called2);
- }
-
- SECTION("destroying a token from before the error does not remove newly added callbacks") {
- advance_and_notify(*r);
-
- bool called = false;
- auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called);
- called = true;
- });
- token = {};
-
- advance_and_notify(*r);
- REQUIRE(called);
- }
-
- SECTION("adding another callback from within an error callback defers delivery") {
- NotificationToken token2;
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called);
- called = true;
- });
- });
- advance_and_notify(*r);
- REQUIRE(!called);
- advance_and_notify(*r);
- REQUIRE(called);
- }
-
- SECTION("adding a callback to a different collection from within the error callback defers delivery") {
- auto results2 = results;
- NotificationToken token2;
- token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
- token2 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called);
- called = true;
- });
- });
- advance_and_notify(*r);
- REQUIRE(!called);
- advance_and_notify(*r);
- REQUIRE(called);
- }
- }
-
- SECTION("error when opening the executor SG") {
- SECTION("error is delivered asynchronously") {
- bool called = false;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- called = true;
- });
- OpenFileLimiter limiter;
-
- REQUIRE(!called);
- coordinator->on_change();
- REQUIRE(!called);
- r->notify();
- REQUIRE(called);
- }
-
- SECTION("adding another callback only sends the error to the new one") {
- bool called = false;
- auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called);
- called = true;
- });
- OpenFileLimiter limiter;
-
- advance_and_notify(*r);
-
- bool called2 = false;
- auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
- REQUIRE(err);
- REQUIRE_FALSE(called2);
- called2 = true;
- });
-
- advance_and_notify(*r);
-
- REQUIRE(called2);
- }
- }
-}
-#endif
-
-#if REALM_ENABLE_SYNC
-TEST_CASE("notifications: sync") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- SyncServer server(false);
- SyncTestFile config(server);
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
-
- SECTION("sync progress commits do not distrupt notifications") {
- auto r = Realm::get_shared_realm(config);
- auto wait_realm = Realm::get_shared_realm(config);
-
- Results results(r, r->read_group().get_table("class_object"));
- Results wait_results(wait_realm, wait_realm->read_group().get_table("class_object"));
- auto token1 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
- auto token2 = wait_results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
-
- // Add an object to the Realm so that notifications are needed
- {
- auto write_realm = Realm::get_shared_realm(config);
- write_realm->begin_transaction();
- write_realm->read_group().get_table("class_object")->create_object();
- write_realm->commit_transaction();
- }
-
- // Wait for the notifications to become ready for the new version
- wait_realm->refresh();
-
- // Start the server and wait for the Realm to be uploaded so that sync
- // makes some writes to the Realm and bumps the version
- server.start();
- wait_for_upload(*r);
-
- // Make sure that the notifications still get delivered rather than
- // waiting forever due to that we don't get a commit notification from
- // the commits sync makes to store the upload progress
- r->refresh();
- }
-}
-#endif
-
-TEST_CASE("notifications: results") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int},
- {"link", PropertyType::Object|PropertyType::Nullable, "linked to object"}
- }},
- {"other object", {
- {"value", PropertyType::Int}
- }},
- {"linking object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"}
- }},
- {"linked to object", {
- {"value", PropertyType::Int}
- }}
- });
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto table = r->read_group().get_table("class_object");
- auto col_value = table->get_column_key("value");
- auto col_link = table->get_column_key("link");
-
- r->begin_transaction();
- std::vector<ObjKey> target_keys;
- r->read_group().get_table("class_linked to object")->create_objects(10, target_keys);
-
- ObjKeys object_keys({3, 4, 7, 9, 10, 21, 24, 34, 42, 50});
- for (int i = 0; i < 10; ++i) {
- table->create_object(object_keys[i]).set_all(i * 2, target_keys[i]);
- }
- r->commit_transaction();
-
- auto r2 = coordinator->get_realm();
- auto r2_table = r2->read_group().get_table("class_object");
-
- Results results(r, table->where().greater(col_value, 0).less(col_value, 10));
-
- SECTION("unsorted notifications") {
- int notification_calls = 0;
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- ++notification_calls;
- });
-
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
- advance_and_notify(*r);
- };
-
- SECTION("modifications to unrelated tables do not send notifications") {
- write([&] {
- r->read_group().get_table("class_other object")->create_object();
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("irrelevant modifications to linked tables do not send notifications") {
- write([&] {
- r->read_group().get_table("class_linked to object")->create_object();
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("irrelevant modifications to linking tables do not send notifications") {
- write([&] {
- r->read_group().get_table("class_linking object")->create_object();
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
- write([&] {
- table->get_object(object_keys[6]).set(col_value, 13);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("deleting non-matching rows does not send a notification") {
- write([&] {
- table->remove_object(object_keys[0]);
- table->remove_object(object_keys[6]);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("modifying a matching row and leaving it matching marks that row as modified") {
- write([&] {
- table->get_object(object_keys[1]).set(col_value, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.modifications, 0);
- REQUIRE_INDICES(change.modifications_new, 0);
- }
-
- SECTION("modifying a matching row to no longer match marks that row as deleted") {
- write([&] {
- table->get_object(object_keys[2]).set(col_value, 0);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 1);
- }
-
- SECTION("modifying a non-matching row to match marks that row as inserted, but not modified") {
- write([&] {
- table->get_object(object_keys[7]).set(col_value, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 4);
- REQUIRE(change.modifications.empty());
- REQUIRE(change.modifications_new.empty());
- }
-
- SECTION("deleting a matching row marks that row as deleted") {
- write([&] {
- table->remove_object(object_keys[3]);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 2);
- }
-
- SECTION("modifications from multiple transactions are collapsed") {
- r2->begin_transaction();
- r2_table->get_object(object_keys[0]).set(col_value, 6);
- r2->commit_transaction();
-
- coordinator->on_change();
-
- r2->begin_transaction();
- r2_table->get_object(object_keys[1]).set(col_value,03);
- r2->commit_transaction();
-
- REQUIRE(notification_calls == 1);
- coordinator->on_change();
- r->notify();
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("inserting a row then modifying it in a second transaction does not report it as modified") {
- r2->begin_transaction();
- ObjKey k = r2_table->create_object(ObjKey(53)).set(col_value, 6).get_key();
- r2->commit_transaction();
-
- coordinator->on_change();
-
- r2->begin_transaction();
- r2_table->get_object(k).set(col_value, 7);
- r2->commit_transaction();
-
- advance_and_notify(*r);
-
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 4);
- REQUIRE(change.modifications.empty());
- REQUIRE(change.modifications_new.empty());
- }
-
- SECTION("modification indices are pre-insert/delete") {
- r->begin_transaction();
- table->get_object(object_keys[2]).set(col_value, 0);
- table->get_object(object_keys[3]).set(col_value, 6);
- r->commit_transaction();
- advance_and_notify(*r);
-
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 1);
- REQUIRE_INDICES(change.modifications, 2);
- REQUIRE_INDICES(change.modifications_new, 1);
- }
-
- SECTION("notifications are not delivered when collapsing transactions results in no net change") {
- r2->begin_transaction();
- ObjKey k = r2_table->create_object().set(col_value, 5).get_key();
- r2->commit_transaction();
-
- coordinator->on_change();
-
- r2->begin_transaction();
- r2_table->remove_object(k);
- r2->commit_transaction();
-
- REQUIRE(notification_calls == 1);
- coordinator->on_change();
- r->notify();
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("inserting a non-matching row at the beginning does not produce a notification") {
- write([&] {
- table->create_object(ObjKey(1));
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("inserting a matching row at the beginning marks just it as inserted") {
- write([&] {
- table->create_object(ObjKey(0)).set(col_value, 5);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 0);
- }
-
- SECTION("modification to related table not included in query") {
- write([&] {
- auto table = r->read_group().get_table("class_linked to object");
- auto col = table->get_column_key("value");
- auto obj = table->get_object(target_keys[1]);
- obj.set(col, 42); // Will affect first entry in results
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.modifications, 0);
- }
- }
-
- SECTION("before/after change callback") {
- struct Callback {
- size_t before_calls = 0;
- size_t after_calls = 0;
- CollectionChangeSet before_change;
- CollectionChangeSet after_change;
- std::function<void(void)> on_before = []{};
- std::function<void(void)> on_after = []{};
-
- void before(CollectionChangeSet c) {
- before_change = c;
- ++before_calls;
- on_before();
- }
- void after(CollectionChangeSet c) {
- after_change = c;
- ++after_calls;
- on_after();
- }
- void error(std::exception_ptr) {
- FAIL("error() should not be called");
- }
- } callback;
- auto token = results.add_notification_callback(&callback);
- advance_and_notify(*r);
-
- SECTION("only after() is called for initial results") {
- REQUIRE(callback.before_calls == 0);
- REQUIRE(callback.after_calls == 1);
- REQUIRE(callback.after_change.empty());
- }
-
- auto write = [&](auto&& func) {
- r2->begin_transaction();
- func(*r2_table);
- r2->commit_transaction();
- advance_and_notify(*r);
- };
-
- SECTION("both are called after a write") {
- write([&](auto&& t) {
- t.create_object(ObjKey(53)).set(col_value, 5);
- });
- REQUIRE(callback.before_calls == 1);
- REQUIRE(callback.after_calls == 2);
- REQUIRE_INDICES(callback.before_change.insertions, 4);
- REQUIRE_INDICES(callback.after_change.insertions, 4);
- }
-
- SECTION("deleted objects are usable in before()") {
- callback.on_before = [&] {
- REQUIRE(results.size() == 4);
- REQUIRE_INDICES(callback.before_change.deletions, 0);
- REQUIRE(results.get(0).is_valid());
- REQUIRE(results.get(0).get<int64_t>(col_value) == 2);
- };
- write([&](auto&& t) {
- t.remove_object(results.get(0).get_key());
- });
- REQUIRE(callback.before_calls == 1);
- REQUIRE(callback.after_calls == 2);
- }
-
- SECTION("inserted objects are usable in after()") {
- callback.on_after = [&] {
- REQUIRE(results.size() == 5);
- REQUIRE_INDICES(callback.after_change.insertions, 4);
- REQUIRE(results.last()->get<int64_t>(col_value) == 5);
- };
- write([&](auto&& t) {
- t.create_object(ObjKey(53)).set(col_value, 5);
- });
- REQUIRE(callback.before_calls == 1);
- REQUIRE(callback.after_calls == 2);
- }
- }
-
- SECTION("sorted notifications") {
- // Sort in descending order
- results = results.sort({{{col_value}}, {false}});
-
- int notification_calls = 0;
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- ++notification_calls;
- });
-
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
- advance_and_notify(*r);
- };
-
- SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
- write([&] {
- table->get_object(object_keys[6]).set(col_value, 13);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("deleting non-matching rows does not send a notification") {
- write([&] {
- table->remove_object(object_keys[0]);
- table->remove_object(object_keys[6]);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("modifying a matching row and leaving it matching marks that row as modified") {
- write([&] {
- table->get_object(object_keys[1]).set(col_value, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.modifications, 3);
- REQUIRE_INDICES(change.modifications_new, 3);
- }
-
- SECTION("modifying a matching row to no longer match marks that row as deleted") {
- write([&] {
- table->get_object(object_keys[2]).set(col_value, 0);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 2);
- }
-
- SECTION("modifying a non-matching row to match marks that row as inserted") {
- write([&] {
- table->get_object(object_keys[7]).set(col_value, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 3);
- }
-
- SECTION("deleting a matching row marks that row as deleted") {
- write([&] {
- table->remove_object(object_keys[3]);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 1);
- }
-
- SECTION("clearing the table marks all rows as deleted") {
- size_t num_expected_deletes = results.size();
- write([&] {
- table->clear();
- });
- REQUIRE(notification_calls == 2);
- REQUIRE(change.deletions.count() == num_expected_deletes);
- }
-
- SECTION("clear insert clear marks the correct rows as deleted") {
- size_t num_expected_deletes = results.size();
- write([&] {
- table->clear();
- });
- REQUIRE(notification_calls == 2);
- REQUIRE(change.deletions.count() == num_expected_deletes);
- write([&] {
- table->create_object().set(col_value, 3);
- table->create_object().set(col_value, 4);
- table->create_object().set(col_value, 5);
- });
- REQUIRE(notification_calls == 3);
- REQUIRE_INDICES(change.insertions, 0, 1, 2);
- REQUIRE(change.deletions.empty());
- write([&] {
- table->clear();
- });
- REQUIRE(notification_calls == 4);
- REQUIRE_INDICES(change.deletions, 0, 1, 2);
- REQUIRE(change.insertions.empty());
- REQUIRE(change.modifications.empty());
- }
-
- SECTION("delete insert clear marks the correct rows as deleted") {
- size_t num_expected_deletes = results.size();
- write([&] {
- results.clear(); // delete all 4 matches
- });
- REQUIRE(notification_calls == 2);
- REQUIRE(change.deletions.count() == num_expected_deletes);
- write([&] {
- table->create_object(ObjKey(57)).set(col_value, 3);
- table->create_object(ObjKey(58)).set(col_value, 4);
- table->create_object(ObjKey(59)).set(col_value, 5);
- });
- REQUIRE(notification_calls == 3);
- REQUIRE_INDICES(change.insertions, 0, 1, 2);
- REQUIRE(change.deletions.empty());
- write([&] {
- table->clear();
- });
- REQUIRE(notification_calls == 4);
- REQUIRE_INDICES(change.deletions, 0, 1, 2);
- REQUIRE(change.insertions.empty());
- REQUIRE(change.modifications.empty());
- }
-
- SECTION("modifying a matching row to change its position sends insert+delete") {
- write([&] {
- table->get_object(object_keys[2]).set(col_value, 9);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 2);
- REQUIRE_INDICES(change.insertions, 0);
- }
-
- SECTION("modifications from multiple transactions are collapsed") {
- r2->begin_transaction();
- r2_table->get_object(object_keys[0]).set(col_value, 5);
- r2->commit_transaction();
-
- r2->begin_transaction();
- r2_table->get_object(object_keys[1]).set(col_value, 0);
- r2->commit_transaction();
-
- REQUIRE(notification_calls == 1);
- advance_and_notify(*r);
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("moving a matching row by deleting all other rows") {
- r->begin_transaction();
- table->clear();
- ObjKey k0 = table->create_object().set(col_value, 15).get_key();
- table->create_object().set(col_value, 5);
- r->commit_transaction();
- advance_and_notify(*r);
-
- write([&] {
- table->remove_object(k0);
- table->create_object().set(col_value, 3);
- });
-
- REQUIRE(notification_calls == 3);
- REQUIRE(change.deletions.empty());
- REQUIRE_INDICES(change.insertions, 1);
- }
- }
-
- SECTION("distinct notifications") {
- results = results.distinct(DistinctDescriptor({{col_value}}));
-
- int notification_calls = 0;
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- ++notification_calls;
- });
-
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
- advance_and_notify(*r);
- };
-
- SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
- write([&] {
- table->get_object(object_keys[6]).set(col_value, 13);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("deleting non-matching rows does not send a notification") {
- write([&] {
- table->remove_object(object_keys[0]);
- table->remove_object(object_keys[6]);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("modifying a matching row and leaving it matching marks that row as modified") {
- write([&] {
- table->get_object(object_keys[1]).set(col_value, 3);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.modifications, 0);
- REQUIRE_INDICES(change.modifications_new, 0);
- }
-
- SECTION("modifying a non-matching row which is after the distinct results in the table to be a same value \
- in the distinct results doesn't send notification.") {
- write([&] {
- table->get_object(object_keys[6]).set(col_value, 2);
- });
- REQUIRE(notification_calls == 1);
- }
-
- SECTION("modifying a non-matching row which is before the distinct results in the table to be a same value \
- in the distinct results send insert + delete.") {
- write([&] {
- table->get_object(object_keys[0]).set(col_value, 2);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 0);
- REQUIRE_INDICES(change.insertions, 0);
- }
-
- SECTION("modifying a matching row to duplicated value in distinct results marks that row as deleted") {
- write([&] {
- table->get_object(object_keys[2]).set(col_value, 2);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.deletions, 1);
- }
-
- SECTION("modifying a non-matching row to match and different value marks that row as inserted") {
- write([&] {
- table->get_object(object_keys[0]).set(col_value, 1);
- });
- REQUIRE(notification_calls == 2);
- REQUIRE_INDICES(change.insertions, 0);
- }
- }
-
- SECTION("schema changes") {
- CollectionChangeSet change;
- auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- change = c;
- });
- advance_and_notify(*r);
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
- advance_and_notify(*r);
- };
-
- SECTION("insert table before observed table") {
- write([&] {
- table->create_object(ObjKey(53)).set(col_value, 5);
- r->read_group().add_table("new table");
- table->create_object(ObjKey(0)).set(col_value, 5);
- });
- REQUIRE_INDICES(change.insertions, 0, 5);
- }
-
- auto linked_table = table->get_link_target(col_link);
- auto col = linked_table->get_column_key("value");
- SECTION("insert new column before link column") {
- write([&] {
- linked_table->get_object(target_keys[1]).set(col, 5);
- table->add_column(type_Int, "new col");
- linked_table->get_object(target_keys[2]).set(col, 5);
- });
- REQUIRE_INDICES(change.modifications, 0, 1);
- }
-#ifdef UNITTESTS_NOT_PARSING
- SECTION("insert table before link target") {
- write([&] {
- linked_table->get_object(target_keys[1]).set(col, 5);
- r->read_group().add_table("new table");
- linked_table->get_object(target_keys[2]).set(col, 5);
- });
- REQUIRE_INDICES(change.modifications, 0, 1);
- }
-#endif
- }
-}
-
-TEST_CASE("results: notifications after move") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
-
- auto table = r->read_group().get_table("class_object");
- auto results = std::make_unique<Results>(r, table);
-
- int notification_calls = 0;
- auto token = results->add_notification_callback([&](CollectionChangeSet, 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->create_object().set_all(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->create_object().set_all(1);
- });
- REQUIRE(notification_calls == 2);
- }
-}
-
-TEST_CASE("results: notifier with no callbacks") {
- _impl::RealmCoordinator::assert_no_open_realms();
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- auto r = coordinator->get_realm(std::move(config), none);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- });
-
- auto table = r->read_group().get_table("class_object");
- Results results(r, table->where());
- results.last(); // force evaluation and creation of TableView
-
- SECTION("refresh() does not block due to implicit notifier") {
- // Create and then immediately remove a callback because
- // `automatic_change_notifications = false` makes Results not implicitly
- // create a notifier
- results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) {});
-
- auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
- r2->begin_transaction();
- r2->read_group().get_table("class_object")->create_object();
- r2->commit_transaction();
-
- r->refresh(); // would deadlock if there was a callback
- }
-
- SECTION("refresh() does not attempt to deliver stale results") {
- results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) {});
-
- // Create version 1
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
-
- r->begin_transaction();
- // Run async query for version 1
- coordinator->on_change();
- // Create version 2 without ever letting 1 be delivered
- table->create_object();
- r->commit_transaction();
-
- // Give it a chance to deliver the async query results (and fail, becuse
- // they're for version 1 and the realm is at 2)
- r->refresh();
- }
-
- SECTION("should not pin the source version even after the Realm has been closed") {
- auto r2 = coordinator->get_realm();
- REQUIRE(r != r2);
- r->close();
-
- auto& shared_group = TestHelper::get_shared_group(r2);
- // There's always at least 2 live versions because the previous version
- // isn't clean up until the *next* commit
- REQUIRE(shared_group->get_number_of_versions() == 2);
-
- auto table = r2->read_group().get_table("class_object");
-
- r2->begin_transaction();
- table->create_object();
- r2->commit_transaction();
- r2->begin_transaction();
- table->create_object();
- r2->commit_transaction();
-
- // Would now be 3 if the closed Realm is still pinning the version it was at
- REQUIRE(shared_group->get_number_of_versions() == 2);
- }
-}
-
-TEST_CASE("results: error messages") {
- InMemoryTestFile config;
- config.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, table);
-
- r->begin_transaction();
- table->create_object();
- r->commit_transaction();
-
- SECTION("out of bounds access") {
- REQUIRE_THROWS_WITH(results.get(5), "Requested index 5 greater than max 0");
- }
-
- SECTION("unsupported aggregate operation") {
- REQUIRE_THROWS_WITH(results.sum("value"), "Cannot sum property 'value': operation not supported for 'string' properties");
- }
-}
-
-TEST_CASE("results: snapshots") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- {"array", PropertyType::Array|PropertyType::Object, "linked to object"}
- }},
- {"linked to object", {
- {"value", PropertyType::Int}
- }}
- };
-
- auto r = Realm::get_shared_realm(config);
-
- SECTION("snapshot of empty Results") {
- Results results;
- auto snapshot = results.snapshot();
- REQUIRE(snapshot.size() == 0);
- }
-
- auto write = [&](auto&& f) {
- r->begin_transaction();
- f();
- r->commit_transaction();
- advance_and_notify(*r);
- };
-
- 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->create_object();
- });
- 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->begin()->remove();
- });
- REQUIRE(results.size() == 0);
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
-
- // Adding a row at the same index that was formerly present in the snapshot shouldn't
- // affect the state of the snapshot.
- write([=]{
- table->create_object();
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
- }
- }
-
- SECTION("snapshot of Results based on LinkView") {
- auto object = r->read_group().get_table("class_object");
- auto col_link = object->get_column_key("array");
- auto linked_to = r->read_group().get_table("class_linked to object");
-
- write([=]{
- object->create_object();
- });
-
- std::shared_ptr<LnkLst> lv = object->begin()->get_linklist_ptr(col_link);
- 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->create_object().get_key());
- });
- 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_valid());
-
- // Removing a row present in the snapshot from its table should result in the snapshot
- // returning a detached row accessor.
- write([&]{
- linked_to->begin()->remove();
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
-
- // Adding a new row to the link list shouldn't affect the state of the snapshot.
- write([&]{
- lv->add(linked_to->create_object().get_key());
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
- }
- }
-
- SECTION("snapshot of Results based on Query") {
- auto table = r->read_group().get_table("class_object");
- auto col_value = table->get_column_key("value");
- Query q = table->column<Int>(col_value) > 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->create_object().set(col_value, 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->begin()->set(col_value, 0);
- });
- REQUIRE(results.size() == 0);
- REQUIRE(snapshot.size() == 1);
- REQUIRE(snapshot.get(0).is_valid());
-
- // Removing a row present in the snapshot from its table should result in the snapshot
- // returning a detached row accessor.
- write([=]{
- table->begin()->remove();
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
-
- // Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
- write([=]{
- table->create_object().set(col_value, 1);
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
- }
- }
-
- SECTION("snapshot of Results based on TableView from query") {
- auto table = r->read_group().get_table("class_object");
- auto col_value = table->get_column_key("value");
- Query q = table->column<Int>(col_value) > 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->create_object().set(col_value, 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->begin()->set(col_value, 0);
- });
- REQUIRE(results.size() == 0);
- REQUIRE(snapshot.size() == 1);
- REQUIRE(snapshot.get(0).is_valid());
-
- // Removing a row present in the snapshot from its table should result in the snapshot
- // returning a detached row accessor.
- write([=]{
- table->begin()->remove();
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
-
- // Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
- write([=]{
- table->create_object().set(col_value, 1);
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
- }
- }
-
- SECTION("snapshot of Results based on TableView from backlinks") {
- auto object = r->read_group().get_table("class_object");
- auto col_link = object->get_column_key("array");
- auto linked_to = r->read_group().get_table("class_linked to object");
-
- write([=]{
- linked_to->create_object();
- object->create_object();
- });
-
- auto linked_to_obj = *linked_to->begin();
- auto lv = object->begin()->get_linklist_ptr(col_link);
-
- TableView backlinks = linked_to_obj.get_backlink_view(object, col_link);
- Results results(r, std::move(backlinks));
-
- {
- // 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_obj.get_key());
- });
- 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([&]{
- if (lv->size() > 0)
- lv->remove(0);
- });
- REQUIRE(results.size() == 0);
- REQUIRE(snapshot.size() == 1);
- REQUIRE(snapshot.get(0).is_valid());
-
- // Removing a row present in the snapshot from its table should result in the snapshot
- // returning a detached row accessor.
- write([=]{
- object->begin()->remove();
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
-
- // Adding a new link shouldn't affect the state of the snapshot.
- write([=]{
- object->create_object().get_linklist(col_link).add(linked_to_obj.get_key());
- });
- REQUIRE(snapshot.size() == 1);
- REQUIRE(!snapshot.get(0).is_valid());
- }
- }
-
- SECTION("snapshot of Results with notification callback registered") {
- auto table = r->read_group().get_table("class_object");
- auto col_value = table->get_column_key("value");
- Query q = table->column<Int>(col_value) > 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->create_object().set(col_value, 1);
- });
- REQUIRE(snapshot.size() == 0);
- }
-
- SECTION("snapshot of rvalue") {
- auto snapshot = std::move(results).snapshot();
- write([=] {
- table->create_object().set(col_value, 1);
- });
- REQUIRE(snapshot.size() == 0);
- }
- }
-
- SECTION("adding notification callback to snapshot throws") {
- auto table = r->read_group().get_table("class_object");
- auto col_value = table->get_column_key("value");
- Query q = table->column<Int>(col_value) > 0;
- Results results(r, q.find_all());
- auto snapshot = results.snapshot();
- CHECK_THROWS(snapshot.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {}));
- }
-
- SECTION("accessors should return none for detached row") {
- auto table = r->read_group().get_table("class_object");
- write([=] {
- table->create_object();
- });
- Results results(r, table);
- auto snapshot = results.snapshot();
- write([=] {;
- table->clear();
- });
-
- REQUIRE_FALSE(snapshot.get(0).is_valid());
- REQUIRE_FALSE(snapshot.first()->is_valid());
- REQUIRE_FALSE(snapshot.last()->is_valid());
- }
-}
-
-TEST_CASE("results: distinct") {
- const int N = 10;
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"num1", PropertyType::Int},
- {"string", PropertyType::String},
- {"num2", PropertyType::Int},
- {"num3", PropertyType::Int}
- }},
- });
-
- auto table = r->read_group().get_table("class_object");
-
- r->begin_transaction();
- for (int i = 0; i < N; ++i) {
- table->create_object().set_all(i % 3, util::format("Foo_%1", i % 3).c_str(), N - i, i % 2);
- }
- // table:
- // 0, Foo_0, 10, 0
- // 1, Foo_1, 9, 1
- // 2, Foo_2, 8, 0
- // 0, Foo_0, 7, 1
- // 1, Foo_1, 6, 0
- // 2, Foo_2, 5, 1
- // 0, Foo_0, 4, 0
- // 1, Foo_1, 3, 1
- // 2, Foo_2, 2, 0
- // 0, Foo_0, 1, 1
-
- r->commit_transaction();
- Results results(r, table->where());
- ColKey col_num1 = table->get_column_key("num1");
- ColKey col_string = table->get_column_key("string");
- ColKey col_num2 = table->get_column_key("num2");
- ColKey col_num3 = table->get_column_key("num3");
-
- SECTION("Single integer property") {
- Results unique = results.distinct(DistinctDescriptor({{col_num1}}));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.get(0).get<Int>(col_num2) == 10);
- REQUIRE(unique.get(1).get<Int>(col_num2) == 9);
- REQUIRE(unique.get(2).get<Int>(col_num2) == 8);
- }
-
- SECTION("Single integer via apply_ordering") {
- DescriptorOrdering ordering;
- ordering.append_sort(SortDescriptor({{col_num1}}));
- ordering.append_distinct(DistinctDescriptor({{col_num1}}));
- Results unique = results.apply_ordering(std::move(ordering));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.get(0).get<Int>(col_num2) == 10);
- REQUIRE(unique.get(1).get<Int>(col_num2) == 9);
- REQUIRE(unique.get(2).get<Int>(col_num2) == 8);
- }
-
- SECTION("Single string property") {
- Results unique = results.distinct(DistinctDescriptor({{col_string}}));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.get(0).get<Int>(col_num2) == 10);
- REQUIRE(unique.get(1).get<Int>(col_num2) == 9);
- REQUIRE(unique.get(2).get<Int>(col_num2) == 8);
- }
-
- SECTION("Two integer properties combined") {
- Results unique = results.distinct(DistinctDescriptor({{col_num1}, {col_num2}}));
- // unique is the same as the table
- REQUIRE(unique.size() == N);
- for (int i = 0; i < N; ++i) {
- REQUIRE(unique.get(i).get<String>(col_string) == StringData(util::format("Foo_%1", i % 3).c_str()));
- }
- }
-
- SECTION("String and integer combined") {
- Results unique = results.distinct(DistinctDescriptor({{col_num2}, {col_string}}));
- // unique is the same as the table
- REQUIRE(unique.size() == N);
- for (int i = 0; i < N; ++i) {
- REQUIRE(unique.get(i).get<String>(col_string) == StringData(util::format("Foo_%1", i % 3).c_str()));
- }
- }
-
- // This section and next section demonstrate that sort().distinct() != distinct().sort()
- SECTION("Order after sort and distinct") {
- Results reverse = results.sort(SortDescriptor({{col_num2}}, {true}));
- // reverse:
- // 0, Foo_0, 1
- // ...
- // 0, Foo_0, 10
- REQUIRE(reverse.first()->get<Int>(col_num2) == 1);
- REQUIRE(reverse.last()->get<Int>(col_num2) == 10);
-
- // distinct() will be applied to the table, after sorting
- Results unique = reverse.distinct(DistinctDescriptor({{col_num1}}));
- // unique:
- // 0, Foo_0, 1
- // 2, Foo_2, 2
- // 1, Foo_1, 3
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.get(0).get<Int>(col_num2) == 1);
- REQUIRE(unique.get(1).get<Int>(col_num2) == 2);
- REQUIRE(unique.get(2).get<Int>(col_num2) == 3);
- }
-
- SECTION("Order after distinct and sort") {
- Results unique = results.distinct(DistinctDescriptor({{col_num1}}));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.first()->get<Int>(col_num2) == 10);
- REQUIRE(unique.last()->get<Int>(col_num2) == 8);
-
- // sort() is only applied to unique
- Results reverse = unique.sort(SortDescriptor({{col_num2}}, {true}));
- // reversed:
- // 2, Foo_2, 8
- // 1, Foo_1, 9
- // 0, Foo_0, 10
- REQUIRE(reverse.size() == 3);
- REQUIRE(reverse.get(0).get<Int>(col_num2) == 8);
- REQUIRE(reverse.get(1).get<Int>(col_num2) == 9);
- REQUIRE(reverse.get(2).get<Int>(col_num2) == 10);
- }
-
- SECTION("Chaining distinct") {
- Results first = results.distinct(DistinctDescriptor({{col_num1}}));
- REQUIRE(first.size() == 3);
-
- // distinct() will not discard the previous applied distinct() calls
- Results second = first.distinct(DistinctDescriptor({{col_num3}}));
- REQUIRE(second.size() == 2);
- }
-
- SECTION("Chaining sort") {
- using cols_0_3 = std::pair<int, int>;
- Results first = results.sort(SortDescriptor({{col_num1}}));
- Results second = first.sort(SortDescriptor({{col_num3}}));
-
- REQUIRE(second.size() == 10);
- // results are ordered first by the last sorted column
- // if any duplicates exist in that column, they are resolved by sorting the
- // previously sorted column. Eg. sort(a).sort(b) == sort(b, a)
- std::vector<cols_0_3> results
- = {{0, 0}, {0, 0}, {1, 0}, {2, 0}, {2, 0}, {0, 1}, {0, 1}, {1, 1}, {1, 1}, {2, 1}};
- for (size_t i = 0; i < results.size(); ++i) {
- REQUIRE(second.get(i).get<Int>(col_num1) == results[i].first);
- REQUIRE(second.get(i).get<Int>(col_num3) == results[i].second);
- }
- }
-
- SECTION("Distinct is carried over to new queries") {
- Results unique = results.distinct(DistinctDescriptor({{col_num1}}));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
-
- Results filtered = unique.filter(Query(table->where().less(col_num1, 2)));
- // filtered:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- REQUIRE(filtered.size() == 2);
- REQUIRE(filtered.get(0).get<Int>(col_num2) == 10);
- REQUIRE(filtered.get(1).get<Int>(col_num2) == 9);
- }
-
- SECTION("Distinct will not forget previous query") {
- Results filtered = results.filter(Query(table->where().greater(col_num2, 5)));
- // filtered:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- // 0, Foo_0, 7
- // 1, Foo_1, 6
- REQUIRE(filtered.size() == 5);
-
- Results unique = filtered.distinct(DistinctDescriptor({{col_num1}}));
- // unique:
- // 0, Foo_0, 10
- // 1, Foo_1, 9
- // 2, Foo_2, 8
- REQUIRE(unique.size() == 3);
- REQUIRE(unique.get(0).get<Int>(col_num2) == 10);
- REQUIRE(unique.get(1).get<Int>(col_num2) == 9);
- REQUIRE(unique.get(2).get<Int>(col_num2) == 8);
-
- Results further_filtered = unique.filter(Query(table->where().equal(col_num2, 9)));
- // further_filtered:
- // 1, Foo_1, 9
- REQUIRE(further_filtered.size() == 1);
- REQUIRE(further_filtered.get(0).get<Int>(col_num2) == 9);
- }
-}
-
-TEST_CASE("results: sort") {
- InMemoryTestFile config;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- {"bool", PropertyType::Bool},
- {"data prop", PropertyType::Data},
- {"link", PropertyType::Object|PropertyType::Nullable, "object 2"},
- {"array", PropertyType::Object|PropertyType::Array, "object 2"},
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }},
- };
-
- auto realm = Realm::get_shared_realm(config);
- auto table = realm->read_group().get_table("class_object");
- auto table2 = realm->read_group().get_table("class_object 2");
- Results r(realm, table);
-
- SECTION("invalid keypaths") {
- SECTION("empty property name") {
- REQUIRE_THROWS_WITH(r.sort({{"", true}}), "Cannot sort on key path '': missing property name.");
- REQUIRE_THROWS_WITH(r.sort({{".", true}}), "Cannot sort on key path '.': missing property name.");
- REQUIRE_THROWS_WITH(r.sort({{"link.", true}}), "Cannot sort on key path 'link.': missing property name.");
- REQUIRE_THROWS_WITH(r.sort({{".value", true}}), "Cannot sort on key path '.value': missing property name.");
- REQUIRE_THROWS_WITH(r.sort({{"link..value", true}}), "Cannot sort on key path 'link..value': missing property name.");
- }
- SECTION("bad property name") {
- REQUIRE_THROWS_WITH(r.sort({{"not a property", true}}),
- "Cannot sort on key path 'not a property': property 'object.not a property' does not exist.");
- REQUIRE_THROWS_WITH(r.sort({{"link.not a property", true}}),
- "Cannot sort on key path 'link.not a property': property 'object 2.not a property' does not exist.");
- }
- SECTION("subscript primitive") {
- REQUIRE_THROWS_WITH(r.sort({{"value.link", true}}),
- "Cannot sort on key path 'value.link': property 'object.value' of type 'int' may only be the final property in the key path.");
- }
- SECTION("end in link") {
- REQUIRE_THROWS_WITH(r.sort({{"link", true}}),
- "Cannot sort on key path 'link': property 'object.link' of type 'object' cannot be the final property in the key path.");
- REQUIRE_THROWS_WITH(r.sort({{"link.link", true}}),
- "Cannot sort on key path 'link.link': property 'object 2.link' of type 'object' cannot be the final property in the key path.");
- }
- SECTION("sort involving bad property types") {
- REQUIRE_THROWS_WITH(r.sort({{"array", true}}),
- "Cannot sort on key path 'array': property 'object.array' is of unsupported type 'array'.");
- REQUIRE_THROWS_WITH(r.sort({{"array.value", true}}),
- "Cannot sort on key path 'array.value': property 'object.array' is of unsupported type 'array'.");
- REQUIRE_THROWS_WITH(r.sort({{"link.link.array.value", true}}),
- "Cannot sort on key path 'link.link.array.value': property 'object.array' is of unsupported type 'array'.");
- REQUIRE_THROWS_WITH(r.sort({{"data prop", true}}),
- "Cannot sort on key path 'data prop': property 'object.data prop' is of unsupported type 'data'.");
- }
- }
-
- realm->begin_transaction();
- ObjKeys table_keys;
- ObjKeys table2_keys;
- table->create_objects(4, table_keys);
- table2->create_objects(4, table2_keys);
- ColKey col_link = table->get_column_key("link");
- ColKey col_link2 = table2->get_column_key("link");
- for (int i = 0; i < 4; ++i) {
- table->get_object(table_keys[i]).set_all((i + 2) % 4, bool(i % 2)).set(col_link, table2_keys[3 - i]);
- table2->get_object(table2_keys[i]).set_all((i + 1) % 4).set(col_link2, table_keys[i]);
- }
- realm->commit_transaction();
- /*
- | index | value | bool | link.value | link.link.value |
- |-------|-------|------|------------|-----------------|
- | 0 | 2 | 0 | 0 | 1 |
- | 1 | 3 | 1 | 3 | 0 |
- | 2 | 0 | 0 | 2 | 3 |
- | 3 | 1 | 1 | 1 | 2 |
- */
-
- #define REQUIRE_ORDER(sort, ...) do { \
- ObjKeys expected({__VA_ARGS__}); \
- auto results = sort; \
- REQUIRE(results.size() == expected.size()); \
- for (size_t i = 0; i < expected.size(); ++i) \
- REQUIRE(results.get(i).get_key() == expected[i]); \
- } while (0)
-
- SECTION("sort on single property") {
- REQUIRE_ORDER((r.sort({{"value", true}})),
- 2, 3, 0, 1);
- REQUIRE_ORDER((r.sort({{"value", false}})),
- 1, 0, 3, 2);
- }
-
- SECTION("sort on two properties") {
- REQUIRE_ORDER((r.sort({{"bool", true}, {"value", true}})),
- 2, 0, 3, 1);
- REQUIRE_ORDER((r.sort({{"bool", false}, {"value", true}})),
- 3, 1, 2, 0);
- REQUIRE_ORDER((r.sort({{"bool", true}, {"value", false}})),
- 0, 2, 1, 3);
- REQUIRE_ORDER((r.sort({{"bool", false}, {"value", false}})),
- 1, 3, 0, 2);
- }
- SECTION("sort over link") {
- REQUIRE_ORDER((r.sort({{"link.value", true}})),
- 0, 3, 2, 1);
- REQUIRE_ORDER((r.sort({{"link.value", false}})),
- 1, 2, 3, 0);
- }
- SECTION("sort over two links") {
- REQUIRE_ORDER((r.sort({{"link.link.value", true}})),
- 1, 0, 3, 2);
- REQUIRE_ORDER((r.sort({{"link.link.value", false}})),
- 2, 3, 0, 1);
- }
-}
-
-struct ResultsFromTable {
- static Results call(std::shared_ptr<Realm> r, ConstTableRef table) {
- return Results(std::move(r), table);
- }
-};
-struct ResultsFromQuery {
- static Results call(std::shared_ptr<Realm> r, ConstTableRef table) {
- return Results(std::move(r), table->where());
- }
-};
-struct ResultsFromTableView {
- static Results call(std::shared_ptr<Realm> r, ConstTableRef table) {
- return Results(std::move(r), table->where().find_all());
- }
-};
-struct ResultsFromLinkView {
- static Results call(std::shared_ptr<Realm> r, ConstTableRef table) {
- r->begin_transaction();
- auto link_table = r->read_group().get_table("class_linking_object");
- std::shared_ptr<LnkLst> link_view = link_table->create_object().get_linklist_ptr(link_table->get_column_key("link"));
- for (auto& o : *table)
- link_view->add(o.get_key());
- r->commit_transaction();
- return Results(r, link_view);
- }
-};
-
-TEMPLATE_TEST_CASE("results: get()", "", ResultsFromTable, ResultsFromQuery, ResultsFromTableView, ResultsFromLinkView) {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"value", PropertyType::Int},
- }},
- {"linking_object", {
- {"link", PropertyType::Array|PropertyType::Object, "object"}
- }},
- });
-
- auto table = r->read_group().get_table("class_object");
- ColKey col_value = table->get_column_key("value");
-
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- table->create_object().set_all(i);
- r->commit_transaction();
-
- Results results = TestType::call(r, table);
-
- SECTION("sequential in increasing order") {
- for (int i = 0; i < 10; ++i)
- CHECK(results.get<Obj>(i).get<int64_t>(col_value) == i);
- for (int i = 0; i < 10; ++i)
- CHECK(results.get<Obj>(i).get<int64_t>(col_value) == i);
- CHECK_THROWS(results.get(11));
- }
- SECTION("sequential in decreasing order") {
- for (int i = 9; i >= 0; --i)
- CHECK(results.get<Obj>(i).get<int64_t>(col_value) == i);
- for (int i = 9; i >= 0; --i)
- CHECK(results.get<Obj>(i).get<int64_t>(col_value) == i);
- }
- SECTION("random order") {
- int indexes[10];
- std::iota(std::begin(indexes), std::end(indexes), 0);
- std::random_device rd;
- std::mt19937 g(rd());
- std::shuffle(std::begin(indexes), std::end(indexes), std::mt19937(rd()));
- for (auto index : indexes)
- CHECK(results.get<Obj>(index).get<int64_t>(col_value) == index);
- }
-}
-
-TEMPLATE_TEST_CASE("results: aggregate", "[query][aggregate]", ResultsFromTable, ResultsFromQuery, ResultsFromTableView, ResultsFromLinkView) {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"object", {
- {"int", PropertyType::Int|PropertyType::Nullable},
- {"float", PropertyType::Float|PropertyType::Nullable},
- {"double", PropertyType::Double|PropertyType::Nullable},
- {"date", PropertyType::Date|PropertyType::Nullable},
- }},
- {"linking_object", {
- {"link", PropertyType::Array|PropertyType::Object, "object"}
- }},
- });
-
- auto table = r->read_group().get_table("class_object");
- ColKey col_int = table->get_column_key("int");
- ColKey col_float = table->get_column_key("float");
- ColKey col_double = table->get_column_key("double");
- ColKey col_date = table->get_column_key("date");
-
- SECTION("one row with null values") {
- r->begin_transaction();
- table->create_object();
- table->create_object().set_all(0, 0.f, 0.0, Timestamp(0, 0));
- table->create_object().set_all(2, 2.f, 2.0, Timestamp(2, 0));
- // table:
- // null, null, null, null,
- // 0, 0, 0, (0, 0)
- // 2, 2, 2, (2, 0)
- r->commit_transaction();
-
- Results results = TestType::call(r, table);
-
- SECTION("max") {
- REQUIRE(results.max(col_int)->get_int() == 2);
- REQUIRE(results.max(col_float)->get_float() == 2.f);
- REQUIRE(results.max(col_double)->get_double() == 2.0);
- REQUIRE(results.max(col_date)->get_timestamp() == Timestamp(2, 0));
- }
-
- SECTION("min") {
- REQUIRE(results.min(col_int)->get_int() == 0);
- REQUIRE(results.min(col_float)->get_float() == 0.f);
- REQUIRE(results.min(col_double)->get_double() == 0.0);
- REQUIRE(results.min(col_date)->get_timestamp() == Timestamp(0, 0));
- }
-
- SECTION("average") {
- REQUIRE(results.average(col_int) == 1.0);
- REQUIRE(results.average(col_float) == 1.0);
- REQUIRE(results.average(col_double) == 1.0);
- REQUIRE_THROWS_AS(results.average(col_date), Results::UnsupportedColumnTypeException);
- }
-
- SECTION("sum") {
- REQUIRE(results.sum(col_int)->get_int() == 2);
- REQUIRE(results.sum(col_float)->get_double() == 2.0);
- REQUIRE(results.sum(col_double)->get_double() == 2.0);
- REQUIRE_THROWS_AS(results.sum(col_date), Results::UnsupportedColumnTypeException);
- }
- }
-
- SECTION("rows with all null values") {
- r->begin_transaction();
- table->create_object();
- table->create_object();
- table->create_object();
- // table:
- // null, null, null, null, null
- // null, null, null, null, null
- // null, null, null, null, null
- r->commit_transaction();
-
- Results results = TestType::call(r, table);
-
- SECTION("max") {
- REQUIRE(!results.max(col_int));
- REQUIRE(!results.max(col_float));
- REQUIRE(!results.max(col_double));
- REQUIRE(!results.max(col_date));
- }
-
- SECTION("min") {
- REQUIRE(!results.min(col_int));
- REQUIRE(!results.min(col_float));
- REQUIRE(!results.min(col_double));
- REQUIRE(!results.min(col_date));
- }
-
- SECTION("average") {
- REQUIRE(!results.average(col_int));
- REQUIRE(!results.average(col_float));
- REQUIRE(!results.average(col_double));
- REQUIRE_THROWS_AS(results.average(col_date), Results::UnsupportedColumnTypeException);
- }
-
- SECTION("sum") {
- REQUIRE(results.sum(col_int)->get_int() == 0);
- REQUIRE(results.sum(col_float)->get_double() == 0.0);
- REQUIRE(results.sum(col_double)->get_double() == 0.0);
- REQUIRE_THROWS_AS(results.sum(col_date), Results::UnsupportedColumnTypeException);
- }
- }
-
- SECTION("empty") {
- Results results = TestType::call(r, table);
-
- SECTION("max") {
- REQUIRE(!results.max(col_int));
- REQUIRE(!results.max(col_float));
- REQUIRE(!results.max(col_double));
- REQUIRE(!results.max(col_date));
- }
-
- SECTION("min") {
- REQUIRE(!results.min(col_int));
- REQUIRE(!results.min(col_float));
- REQUIRE(!results.min(col_double));
- REQUIRE(!results.min(col_date));
- }
-
- SECTION("average") {
- REQUIRE(!results.average(col_int));
- REQUIRE(!results.average(col_float));
- REQUIRE(!results.average(col_double));
- REQUIRE_THROWS_AS(results.average(col_date), Results::UnsupportedColumnTypeException);
- }
-
- SECTION("sum") {
- REQUIRE(results.sum(col_int)->get_int() == 0);
- REQUIRE(results.sum(col_float)->get_double() == 0.0);
- REQUIRE(results.sum(col_double)->get_double() == 0.0);
- REQUIRE_THROWS_AS(results.sum(col_date), Results::UnsupportedColumnTypeException);
- }
- }
-}
-
-TEST_CASE("results: set property value on all objects", "[batch_updates]") {
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- // config.cache = false;
- config.schema = Schema{
- {"AllTypes", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"bool", PropertyType::Bool},
- {"int", PropertyType::Int},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"data", PropertyType::Data},
- {"date", PropertyType::Date},
- {"object", PropertyType::Object|PropertyType::Nullable, "AllTypes"},
- {"list", PropertyType::Array|PropertyType::Object, "AllTypes"},
-
- {"bool array", PropertyType::Array|PropertyType::Bool},
- {"int array", PropertyType::Array|PropertyType::Int},
- {"float array", PropertyType::Array|PropertyType::Float},
- {"double array", PropertyType::Array|PropertyType::Double},
- {"string array", PropertyType::Array|PropertyType::String},
- {"data array", PropertyType::Array|PropertyType::Data},
- {"date array", PropertyType::Array|PropertyType::Date},
- {"object array", PropertyType::Array|PropertyType::Object, "AllTypes"},
- }, {
- {"parents", PropertyType::LinkingObjects|PropertyType::Array, "AllTypes", "object"},
- }}
- };
- config.schema_version = 0;
- auto realm = Realm::get_shared_realm(config);
- auto table = realm->read_group().get_table("class_AllTypes");
- realm->begin_transaction();
- table->create_object();
- table->create_object();
- realm->commit_transaction();
- Results r(realm, table);
-
- TestContext ctx(realm);
-
- SECTION("non-existing property name") {
- realm->begin_transaction();
- REQUIRE_THROWS_AS(r.set_property_value(ctx, "i dont exist", util::Any(false)), Results::InvalidPropertyException);
- realm->cancel_transaction();
- }
-
- SECTION("readonly property") {
- realm->begin_transaction();
- REQUIRE_THROWS_AS(r.set_property_value(ctx, "parents", util::Any(false)), ReadOnlyPropertyException);
- realm->cancel_transaction();
- }
-
- SECTION("primarykey property") {
- realm->begin_transaction();
- REQUIRE_THROWS_AS(r.set_property_value(ctx, "pk", util::Any(1)), std::logic_error);
- realm->cancel_transaction();
- }
-
- SECTION("set property values removes object from Results") {
- realm->begin_transaction();
- Results results(realm, table->where().equal(table->get_column_key("int"),0));
- CHECK(results.size() == 2);
- r.set_property_value(ctx, "int", util::Any(INT64_C(42)));
- CHECK(results.size() == 0);
- realm->cancel_transaction();
- }
-
- SECTION("set property value") {
- realm->begin_transaction();
-
- r.set_property_value<util::Any>(ctx, "bool", util::Any(true));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<Bool>("bool") == true);
- }
-
- r.set_property_value(ctx, "int", util::Any(INT64_C(42)));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<Int>("int") == 42);
- }
-
- r.set_property_value(ctx, "float", util::Any(1.23f));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<float>("float") == 1.23f);
- }
-
- r.set_property_value(ctx, "double", util::Any(1.234));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<double>("double") == 1.234);
- }
-
- r.set_property_value(ctx, "string", util::Any(std::string("abc")));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<String>("string") == "abc");
- }
-
- r.set_property_value(ctx, "data", util::Any(std::string("abc")));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<Binary>("data") == BinaryData("abc", 3));
- }
-
- util::Any timestamp = Timestamp(1, 2);
- r.set_property_value(ctx, "date", timestamp);
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<Timestamp>("date") == any_cast<Timestamp>(timestamp));
- }
-
- ObjKey object_key = table->create_object().get_key();
- Object linked_obj(realm, "AllTypes", object_key);
- r.set_property_value(ctx, "object", util::Any(linked_obj));
- for (size_t i = 0; i < r.size(); i++) {
- CHECK(r.get(i).get<ObjKey>("object") == object_key);
- }
-
- ObjKey list_object_key = table->create_object().get_key();
- Object list_object(realm, "AllTypes", list_object_key);
- r.set_property_value(ctx, "list", util::Any(AnyVector{list_object, list_object}));
- for (size_t i = 0; i < r.size(); i++) {
- auto list = r.get(i).get_linklist("list");
- CHECK(list.size() == 2);
- CHECK(list.get(0) == list_object_key);
- CHECK(list.get(1) == list_object_key);
- }
-
- auto check_array = [&](ColKey col, auto val0, auto... values) {
- size_t rows = r.size();
- for (size_t i = 0; i < rows; ++i) {
- Obj row = r.get(i);
- auto array = row.get_list<decltype(val0)>(col);
- CAPTURE(0);
- REQUIRE(val0 == array.get(0));
- size_t j = 1;
- for (auto& value : {values...}) {
- CAPTURE(j);
- REQUIRE(j < array.size());
- REQUIRE(value == array.get(j));
- ++j;
- }
- }
- };
-
- r.set_property_value(ctx, "bool array", util::Any(AnyVec{true, false}));
- check_array(table->get_column_key("bool array"), true, false);
-
- r.set_property_value(ctx, "int array", util::Any(AnyVec{INT64_C(5), INT64_C(6)}));
- check_array(table->get_column_key("int array"), INT64_C(5), INT64_C(6));
-
- r.set_property_value(ctx, "float array", util::Any(AnyVec{1.1f, 2.2f}));
- check_array(table->get_column_key("float array"), 1.1f, 2.2f);
-
- r.set_property_value(ctx, "double array", util::Any(AnyVec{3.3, 4.4}));
- check_array(table->get_column_key("double array"), 3.3, 4.4);
-
- r.set_property_value(ctx, "string array", util::Any(AnyVec{"a"s, "b"s, "c"s}));
- check_array(table->get_column_key("string array"), StringData("a"), StringData("b"), StringData("c"));
-
- r.set_property_value(ctx, "data array", util::Any(AnyVec{"d"s, "e"s, "f"s}));
- check_array(table->get_column_key("data array"), BinaryData("d",1), BinaryData("e",1), BinaryData("f",1));
-
- r.set_property_value(ctx, "date array", util::Any(AnyVec{Timestamp(10,20), Timestamp(20,30), Timestamp(30,40)}));
- check_array(table->get_column_key("date array"), Timestamp(10,20), Timestamp(20,30), Timestamp(30,40));
- }
-}
-
-TEST_CASE("results: limit", "[limit]") {
- InMemoryTestFile config;
- // config.cache = false;
- config.automatic_change_notifications = false;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int},
- }},
- };
-
- auto realm = Realm::get_shared_realm(config);
- auto table = realm->read_group().get_table("class_object");
- auto col = table->get_column_key("value");
-
- realm->begin_transaction();
- for (int i = 0; i < 8; ++i) {
- table->create_object().set(col, (i + 2) % 4);
- }
- realm->commit_transaction();
- Results r(realm, table);
-
- SECTION("unsorted") {
- REQUIRE(r.limit(0).size() == 0);
- REQUIRE_ORDER(r.limit(1), 0);
- REQUIRE_ORDER(r.limit(2), 0, 1);
- REQUIRE_ORDER(r.limit(8), 0, 1, 2, 3, 4, 5, 6, 7);
- REQUIRE_ORDER(r.limit(100), 0, 1, 2, 3, 4, 5, 6, 7);
- }
-
- SECTION("sorted") {
- auto sorted = r.sort({{"value", true}});
- REQUIRE(sorted.limit(0).size() == 0);
- REQUIRE_ORDER(sorted.limit(1), 2);
- REQUIRE_ORDER(sorted.limit(2), 2, 6);
- REQUIRE_ORDER(sorted.limit(8), 2, 6, 3, 7, 0, 4, 1, 5);
- REQUIRE_ORDER(sorted.limit(100), 2, 6, 3, 7, 0, 4, 1, 5);
- }
-
- SECTION("sort after limit") {
- REQUIRE(r.limit(0).sort({{"value", true}}).size() == 0);
- REQUIRE_ORDER(r.limit(1).sort({{"value", true}}), 0);
- REQUIRE_ORDER(r.limit(3).sort({{"value", true}}), 2, 0, 1);
- REQUIRE_ORDER(r.limit(8).sort({{"value", true}}), 2, 6, 3, 7, 0, 4, 1, 5);
- REQUIRE_ORDER(r.limit(100).sort({{"value", true}}), 2, 6, 3, 7, 0, 4, 1, 5);
- }
-
- SECTION("distinct") {
- auto sorted = r.distinct({"value"});
- REQUIRE(sorted.limit(0).size() == 0);
- REQUIRE_ORDER(sorted.limit(1), 0);
- REQUIRE_ORDER(sorted.limit(2), 0, 1);
- REQUIRE_ORDER(sorted.limit(8), 0, 1, 2, 3);
-
- sorted = r.sort({{"value", true}}).distinct({"value"});
- REQUIRE(sorted.limit(0).size() == 0);
- REQUIRE_ORDER(sorted.limit(1), 2);
- REQUIRE_ORDER(sorted.limit(2), 2, 3);
- REQUIRE_ORDER(sorted.limit(8), 2, 3, 0, 1);
- }
-
- SECTION("notifications on results using all descriptor types") {
- r = r.distinct({"value"}).sort({{"value", false}}).limit(2);
- int notification_calls = 0;
- auto token = r.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (notification_calls == 0) {
- REQUIRE(c.empty());
- REQUIRE(r.size() == 2);
- REQUIRE(r.get(0).get<Int>(col) == 3);
- REQUIRE(r.get(1).get<Int>(col) == 2);
- } else if (notification_calls == 1) {
- REQUIRE(!c.empty());
- REQUIRE_INDICES(c.insertions, 0);
- REQUIRE_INDICES(c.deletions, 1);
- REQUIRE(c.moves.size() == 0);
- REQUIRE(c.modifications.count() == 0);
- REQUIRE(r.size() == 2);
- REQUIRE(r.get(0).get<Int>(col) == 5);
- REQUIRE(r.get(1).get<Int>(col) == 3);
- }
- ++notification_calls;
- });
- advance_and_notify(*realm);
- REQUIRE(notification_calls == 1);
- realm->begin_transaction();
- table->create_object().set(col, 5);
- realm->commit_transaction();
- advance_and_notify(*realm);
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("notifications on only limited results") {
- r = r.limit(2);
- int notification_calls = 0;
- auto token = r.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
- REQUIRE_FALSE(err);
- if (notification_calls == 0) {
- REQUIRE(c.empty());
- REQUIRE(r.size() == 2);
- } else if (notification_calls == 1) {
- REQUIRE(!c.empty());
- REQUIRE(c.insertions.count() == 0);
- REQUIRE(c.deletions.count() == 0);
- REQUIRE(c.modifications.count() == 1);
- REQUIRE_INDICES(c.modifications, 1);
- REQUIRE(r.size() == 2);
- }
- ++notification_calls;
- });
- advance_and_notify(*realm);
- REQUIRE(notification_calls == 1);
- realm->begin_transaction();
- table->get_object(1).set(col, 5);
- realm->commit_transaction();
- advance_and_notify(*realm);
- REQUIRE(notification_calls == 2);
- }
-
- SECTION("does not support further filtering") {
- auto limited = r.limit(0);
- REQUIRE_THROWS_AS(limited.filter(table->where()), Results::UnimplementedOperationException);
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp
deleted file mode 100644
index b851c4831..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp
+++ /dev/null
@@ -1,811 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-#include "util/test_file.hpp"
-
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "property.hpp"
-#include "schema.hpp"
-
-#include <realm/group.hpp>
-#include <realm/table.hpp>
-
-using namespace realm;
-
-struct SchemaChangePrinter {
- std::ostream& out;
-
- template<typename Value>
- void print(Value value) const
- {
- out << value;
- }
-
- template<typename Value, typename... Rest>
- void print(Value value, Rest... rest) const
- {
- out << value << ", ";
- print(rest...);
- }
-
-#define REALM_SC_PRINT(type, ...) \
- void operator()(schema_change::type v) const \
- { \
- out << #type << "{"; \
- print(__VA_ARGS__); \
- out << "}"; \
- }
-
- REALM_SC_PRINT(AddIndex, v.object, v.property)
- REALM_SC_PRINT(AddProperty, v.object, v.property)
- REALM_SC_PRINT(AddTable, v.object)
- REALM_SC_PRINT(RemoveTable, v.object)
- REALM_SC_PRINT(AddInitialProperties, v.object)
- REALM_SC_PRINT(ChangePrimaryKey, v.object, v.property)
- REALM_SC_PRINT(ChangePropertyType, v.object, v.old_property, v.new_property)
- REALM_SC_PRINT(MakePropertyNullable, v.object, v.property)
- REALM_SC_PRINT(MakePropertyRequired, v.object, v.property)
- REALM_SC_PRINT(RemoveIndex, v.object, v.property)
- REALM_SC_PRINT(RemoveProperty, v.object, v.property)
-
-#undef REALM_SC_PRINT
-};
-
-namespace Catch {
-template<>
-struct StringMaker<SchemaChange> {
- static std::string convert(SchemaChange const& sc)
- {
- std::stringstream ss;
- sc.visit(SchemaChangePrinter{ss});
- return ss.str();
- }
-};
-} // namespace Catch
-
-#define REQUIRE_THROWS_CONTAINING(expr, msg) \
- REQUIRE_THROWS_WITH(expr, Catch::Matchers::Contains(msg))
-
-TEST_CASE("ObjectSchema") {
-
- SECTION("Aliases are still present in schema returned from the Realm") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "alias"}
- }},
- };
-
- auto realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().find("object")->property_for_name("value")->public_name == "alias");
- }
-
- SECTION("looking up properties by alias matches name if alias is not set") {
- auto schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "alias"},
- {"other_value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}}
- }},
- };
-
- REQUIRE(schema.find("object")->property_for_public_name("value") == nullptr);
- REQUIRE(schema.find("object")->property_for_public_name("alias")->name == "value");
- REQUIRE(schema.find("object")->property_for_public_name("other_value")->name == "other_value");
- }
-
- SECTION("from a Group") {
- Group g;
-
- TableRef table = g.add_table_with_primary_key("class_table", type_Int, "pk");
- TableRef target = g.add_table("class_target");
-
- table->add_column(type_Int, "int");
- table->add_column(type_Bool, "bool");
- table->add_column(type_Float, "float");
- table->add_column(type_Double, "double");
- table->add_column(type_String, "string");
- table->add_column(type_Binary, "data");
- table->add_column(type_Timestamp, "date");
-
- table->add_column_link(type_Link, "object", *target);
- table->add_column_link(type_LinkList, "array", *target);
-
- table->add_column(type_Int, "int?", true);
- table->add_column(type_Bool, "bool?", true);
- table->add_column(type_Float, "float?", true);
- table->add_column(type_Double, "double?", true);
- table->add_column(type_String, "string?", true);
- table->add_column(type_Binary, "data?", true);
- table->add_column(type_Timestamp, "date?", true);
-
- auto add_list = [](TableRef table, DataType type, StringData name, bool nullable) {
- table->add_column_list(type, name, nullable);
- };
-
- add_list(table, type_Int, "int array", false);
- add_list(table, type_Bool, "bool array", false);
- add_list(table, type_Float, "float array", false);
- add_list(table, type_Double, "double array", false);
- add_list(table, type_String, "string array", false);
- add_list(table, type_Binary, "data array", false);
- add_list(table, type_Timestamp, "date array", false);
- add_list(table, type_Int, "int? array", true);
- add_list(table, type_Bool, "bool? array", true);
- add_list(table, type_Float, "float? array", true);
- add_list(table, type_Double, "double? array", true);
- add_list(table, type_String, "string? array", true);
- add_list(table, type_Binary, "data? array", true);
- add_list(table, type_Timestamp, "date? array", true);
-
- std::vector<ColKey> indexed_cols;
- indexed_cols.push_back(table->add_column(type_Int, "indexed int"));
- indexed_cols.push_back(table->add_column(type_Bool, "indexed bool"));
- indexed_cols.push_back(table->add_column(type_String, "indexed string"));
- indexed_cols.push_back(table->add_column(type_Timestamp, "indexed date"));
-
- indexed_cols.push_back(table->add_column(type_Int, "indexed int?", true));
- indexed_cols.push_back(table->add_column(type_Bool, "indexed bool?", true));
- indexed_cols.push_back(table->add_column(type_String, "indexed string?", true));
- indexed_cols.push_back(table->add_column(type_Timestamp, "indexed date?", true));
-
- for (auto col : indexed_cols)
- table->add_search_index(col);
-
- ObjectSchema os(g, "table", table->get_key());
- REQUIRE(os.table_key == table->get_key());
-
-#define REQUIRE_PROPERTY(name, type, ...) do { \
- Property* prop; \
- REQUIRE((prop = os.property_for_name(name))); \
- REQUIRE((*prop == Property{name, PropertyType::type, __VA_ARGS__})); \
- REQUIRE(prop->column_key == *expected_col++); \
-} while (0)
-
- auto all_column_keys = table->get_column_keys();
- auto expected_col = all_column_keys.begin();
-
- REQUIRE(os.property_for_name("nonexistent property") == nullptr);
-
- REQUIRE_PROPERTY("pk", Int, Property::IsPrimary{true});
-
- REQUIRE_PROPERTY("int", Int);
- REQUIRE_PROPERTY("bool", Bool);
- REQUIRE_PROPERTY("float", Float);
- REQUIRE_PROPERTY("double", Double);
- REQUIRE_PROPERTY("string", String);
- REQUIRE_PROPERTY("data", Data);
- REQUIRE_PROPERTY("date", Date);
-
- REQUIRE_PROPERTY("object", Object|PropertyType::Nullable, "target");
- REQUIRE_PROPERTY("array", Array|PropertyType::Object, "target");
-
- REQUIRE_PROPERTY("int?", Int|PropertyType::Nullable);
- REQUIRE_PROPERTY("bool?", Bool|PropertyType::Nullable);
- REQUIRE_PROPERTY("float?", Float|PropertyType::Nullable);
- REQUIRE_PROPERTY("double?", Double|PropertyType::Nullable);
- REQUIRE_PROPERTY("string?", String|PropertyType::Nullable);
- REQUIRE_PROPERTY("data?", Data|PropertyType::Nullable);
- REQUIRE_PROPERTY("date?", Date|PropertyType::Nullable);
-
- REQUIRE_PROPERTY("int array", Int|PropertyType::Array);
- REQUIRE_PROPERTY("bool array", Bool|PropertyType::Array);
- REQUIRE_PROPERTY("float array", Float|PropertyType::Array);
- REQUIRE_PROPERTY("double array", Double|PropertyType::Array);
- REQUIRE_PROPERTY("string array", String|PropertyType::Array);
- REQUIRE_PROPERTY("data array", Data|PropertyType::Array);
- REQUIRE_PROPERTY("date array", Date|PropertyType::Array);
- REQUIRE_PROPERTY("int? array", Int|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("bool? array", Bool|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("float? array", Float|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("double? array", Double|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("string? array", String|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("data? array", Data|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("date? array", Date|PropertyType::Array|PropertyType::Nullable);
-
- REQUIRE_PROPERTY("indexed int", Int, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed bool", Bool, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed string", String, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed date", Date, Property::IsPrimary{false}, Property::IsIndexed{true});
-
- REQUIRE_PROPERTY("indexed int?", Int|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed bool?", Bool|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed string?", String|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed date?", Date|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- }
-}
-
-TEST_CASE("Schema") {
- SECTION("validate()") {
- SECTION("rejects link properties with no target object") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable}
- }},
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' has unknown object type ''");
- }
-
- SECTION("rejects array properties with no target object") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object}
- }},
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' has unknown object type ''");
- }
-
- SECTION("rejects link properties with a target not in the schema") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "invalid target"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' has unknown object type 'invalid target'");
- }
-
- SECTION("rejects array properties with a target not in the schema") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object, "invalid target"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' has unknown object type 'invalid target'");
- }
-
- SECTION("rejects linking objects without a source object") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' has unknown object type ''");
- }
-
- SECTION("rejects linking objects without a source property") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' must have an origin property name.");
- }
-
- SECTION("rejects linking objects with invalid source object") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "not an object type", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' has unknown object type 'not an object type'");
- }
-
- SECTION("rejects linking objects with invalid source property") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", "value"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.value' declared as origin of linking objects property 'object.incoming' is not a link");
-
- schema = {
- {"object", {
- {"value", PropertyType::Int},
- {"link", PropertyType::Object|PropertyType::Nullable, "object 2"},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", "link"}
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' declared as origin of linking objects property 'object.incoming' links to type 'object 2'");
-
- }
-
- SECTION("rejects non-array linking objects") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }, {
- {"incoming", PropertyType::LinkingObjects, "object", "link"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Linking Objects property 'object.incoming' must be an array.");
- }
-
- SECTION("rejects target object types for non-link properties") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int},
- {"bool", PropertyType::Bool},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"date", PropertyType::Date},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.object_type = "object";
- REQUIRE_THROWS_CONTAINING(schema.validate(), "cannot have an object type.");
- prop.object_type = "";
- }
- }
-
- SECTION("rejects source property name for non-linking objects properties") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int},
- {"bool", PropertyType::Bool},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"data", PropertyType::Data},
- {"date", PropertyType::Date},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Object|PropertyType::Array, "object"},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.link_origin_property_name = "source";
- auto expected = util::format("Property 'object.%1' of type '%1' cannot have an origin property name.", prop.name, prop.name);
- REQUIRE_THROWS_CONTAINING(schema.validate(), expected);
- prop.link_origin_property_name = "";
- }
- }
-
- SECTION("rejects non-nullable link properties") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' must be nullable.");
- }
-
- SECTION("rejects nullable array properties") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object|PropertyType::Nullable, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' cannot be nullable.");
- }
-
- SECTION("rejects nullable linking objects") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }, {
- {"incoming", PropertyType::LinkingObjects|PropertyType::Array|PropertyType::Nullable, "object", "link"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' cannot be nullable.");
- }
-
- SECTION("rejects duplicate primary keys") {
- Schema schema = {
- {"object", {
- {"pk1", PropertyType::Int, Property::IsPrimary{true}},
- {"pk2", PropertyType::Int, Property::IsPrimary{true}},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Properties 'pk2' and 'pk1' are both marked as the primary key of 'object'.");
- }
-
- SECTION("rejects invalid primary key types") {
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Float, Property::IsPrimary{true}},
- }}
- };
-
- schema.begin()->primary_key_property()->type = PropertyType::Any;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'any' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Bool;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'bool' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Float;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'float' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Double;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'double' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Object;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'object' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::LinkingObjects;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'linking objects' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Data;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'data' cannot be made the primary key.");
-
- schema.begin()->primary_key_property()->type = PropertyType::Date;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'date' cannot be made the primary key.");
- }
-
- SECTION("allows valid primary key types") {
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- }}
- };
-
- REQUIRE_NOTHROW(schema.validate());
-
- schema.begin()->primary_key_property()->type = PropertyType::Int|PropertyType::Nullable;
- REQUIRE_NOTHROW(schema.validate());
- schema.begin()->primary_key_property()->type = PropertyType::String;
- REQUIRE_NOTHROW(schema.validate());
- schema.begin()->primary_key_property()->type = PropertyType::String|PropertyType::Nullable;
- REQUIRE_NOTHROW(schema.validate());
- }
-
- SECTION("rejects nonexistent primary key") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- schema.begin()->primary_key = "nonexistent";
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Specified primary key 'object.nonexistent' does not exist.");
- }
-
- SECTION("rejects indexes for types that cannot be indexed") {
- Schema schema = {
- {"object", {
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"data", PropertyType::Data},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Array|PropertyType::Object, "object"},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.is_indexed = true;
- auto expected = util::format("Property 'object.%1' of type '%1' cannot be indexed.", prop.name);
- REQUIRE_THROWS_CONTAINING(schema.validate(), expected);
- prop.is_indexed = false;
- }
- }
-
- SECTION("allows indexing types that can be indexed") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"bool", PropertyType::Bool, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"string", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"date", PropertyType::Date, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- REQUIRE_NOTHROW(schema.validate());
- }
-
- SECTION("rejects duplicate types with the same name") {
- Schema schema = {
- {"object1", {
- {"int", PropertyType::Int},
- }},
- {"object2", {
- {"int", PropertyType::Int},
- }},
- {"object3", {
- {"int", PropertyType::Int},
- }},
- {"object2", {
- {"int", PropertyType::Int},
- }},
- {"object1", {
- {"int", PropertyType::Int},
- }}
- };
-
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Type 'object1' appears more than once in the schema.\n"
- "- Type 'object2' appears more than once in the schema.");
- }
-
- SECTION("rejects properties with the same name") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
- {"parent", PropertyType::Int},
- {"field1", PropertyType::Int},
- {"field2", PropertyType::String},
- {"field1", PropertyType::String},
- {"field2", PropertyType::String},
- {"field1", PropertyType::Int},
- }, {
- {"parent", PropertyType::Array|PropertyType::LinkingObjects, "object", "child"}
- }}
- };
-
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'field1' appears more than once in the schema for type 'object'.\n"
- "- Property 'field2' appears more than once in the schema for type 'object'.\n"
- "- Property 'parent' appears more than once in the schema for type 'object'.");
- }
-
- SECTION("rejects schema if all properties have the same name") {
- Schema schema = {
- {"object", {
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- }}
- };
-
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'field' appears more than once in the schema for type 'object'.\n"
- "- Property 'otherField' appears more than once in the schema for type 'object'.");
- }
-
- SECTION("rejects properties with the same alias") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
-
- // Alias == Name on computed property
- {"parentA", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_parent"},
-
- // Name == Alias on other property
- {"fieldA", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
- {"fieldB", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field2"},
- {"fieldC", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
- {"fieldD", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field2"},
- {"fieldE", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
-
- // Name == Alias
- {"fieldF", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "fieldF"},
- }, {
- // Computed property alias == name on persisted property
- {"parentB", PropertyType::Array|PropertyType::LinkingObjects, "object", "child", "_parent"}
- }}
- };
-
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Alias '_field1' appears more than once in the schema for type 'object'.\n"
- "- Alias '_field2' appears more than once in the schema for type 'object'.\n"
- "- Alias '_parent' appears more than once in the schema for type 'object'.");
- }
-
- SECTION("rejects properties whose name conflicts with an alias for another property") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
- {"field1", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "field2"},
- {"field2", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "parent"},
- }, {
- {"parent", PropertyType::Array|PropertyType::LinkingObjects, "object", "child", "field1"}
- }}
- };
-
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'object.parent' has an alias 'field1' that conflicts with a property of the same name.\n"
- "- Property 'object.field1' has an alias 'field2' that conflicts with a property of the same name.\n"
- "- Property 'object.field2' has an alias 'parent' that conflicts with a property of the same name.");
- }
- }
-
- SECTION("compare()") {
- using namespace schema_change;
- using vec = std::vector<SchemaChange>;
- SECTION("add table") {
- Schema schema1 = {
- {"object 1", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object 1", {
- {"int", PropertyType::Int},
- }},
- {"object 2", {
- {"int", PropertyType::Int},
- }}
- };
- auto obj = &*schema2.find("object 2");
- auto expected = vec{AddTable{obj}, AddInitialProperties{obj}};
- REQUIRE(schema1.compare(schema2) == expected);
- }
-
- SECTION("add property") {
- Schema schema1 = {
- {"object", {
- {"int 1", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int 1", PropertyType::Int},
- {"int 2", PropertyType::Int},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(AddProperty{&*schema1.find("object"), &schema2.find("object")->persisted_properties[1]})});
- }
-
- SECTION("remove property") {
- Schema schema1 = {
- {"object", {
- {"int 1", PropertyType::Int},
- {"int 2", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int 1", PropertyType::Int},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(RemoveProperty{&*schema1.find("object"), &schema1.find("object")->persisted_properties[1]})});
- }
-
- SECTION("change property type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- };
-
- SECTION("change link target") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Object, "target 1"},
- }},
- {"target 1", {
- {"value", PropertyType::Int},
- }},
- {"target 2", {
- {"value", PropertyType::Int},
- }},
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Object, "target 2"},
- }},
- {"target 1", {
- {"value", PropertyType::Int},
- }},
- {"target 2", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
-
- SECTION("add index") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == vec{(AddIndex{object_schema, &object_schema->persisted_properties[0]})});
- }
-
- SECTION("remove index") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == vec{(RemoveIndex{object_schema, &object_schema->persisted_properties[0]})});
- }
-
- SECTION("add index and make nullable") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == (vec{
- MakePropertyNullable{object_schema, &object_schema->persisted_properties[0]},
- AddIndex{object_schema, &object_schema->persisted_properties[0]}}));
- }
-
- SECTION("add index and change type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
-
- SECTION("make nullable and change type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double|PropertyType::Nullable},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp
deleted file mode 100644
index 2594e3a12..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp
+++ /dev/null
@@ -1,896 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/test_file.hpp"
-#include "util/test_utils.hpp"
-
-#include "list.hpp"
-#include "object.hpp"
-#include "object_schema.hpp"
-#include "object_store.hpp"
-#include "results.hpp"
-#include "schema.hpp"
-#include "thread_safe_reference.hpp"
-#include "util/scheduler.hpp"
-
-#include "impl/object_accessor_impl.hpp"
-#include "impl/realm_coordinator.hpp"
-
-#include <realm/db.hpp>
-#include <realm/history.hpp>
-#include <realm/string_data.hpp>
-#include <realm/util/optional.hpp>
-
-using namespace realm;
-
-static TableRef get_table(Realm& realm, StringData object_name) {
- return ObjectStore::table_for_object_type(realm.read_group(), object_name);
-}
-
-static Object create_object(SharedRealm const& realm, StringData object_type, AnyDict value) {
- CppContext ctx(realm);
- return Object::create(ctx, realm, object_type, util::Any(value));
-}
-
-TEST_CASE("thread safe reference") {
- using namespace std::string_literals;
-
- Schema schema{
- {"foo object", {
- {"ignore me", PropertyType::Int}, // Used in tests cases that don't care about the value.
- }},
- {"string object", {
- {"value", PropertyType::String|PropertyType::Nullable},
- }},
- {"int object", {
- {"value", PropertyType::Int},
- }},
- {"int array object", {
- {"value", PropertyType::Array|PropertyType::Object, "int object"}
- }},
- {"int array", {
- {"value", PropertyType::Array|PropertyType::Int}
- }},
- };
-
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- SharedRealm r = Realm::get_shared_realm(config);
- r->update_schema(schema);
-
- // Convenience object
- r->begin_transaction();
- auto foo = create_object(r, "foo object", {{"ignore me", INT64_C(0)}});
- r->commit_transaction();
-
- const auto int_obj_col = r->schema().find("int object")->persisted_properties[0].column_key;
-
- SECTION("allowed during write transactions") {
- SECTION("obtain") {
- r->begin_transaction();
- REQUIRE_NOTHROW(ThreadSafeReference(foo));
- }
- SECTION("resolve") {
- auto ref = ThreadSafeReference(foo);
- r->begin_transaction();
- REQUIRE_NOTHROW(ref.resolve<Object>(r));
- }
- }
-
- SECTION("cleanup properly unpins version") {
- auto history = make_in_realm_history(config.path);
- auto shared_group = DB::create(*history, config.options());
-
- auto get_current_version = [&]() -> VersionID {
- auto rt = shared_group->start_read();
- auto version = rt->get_version_of_current_transaction();
- return version;
- };
-
- auto reference_version = get_current_version();
- auto ref = util::make_optional(ThreadSafeReference(foo));
- r->begin_transaction(); r->commit_transaction(); // Advance version
-
- REQUIRE(get_current_version() != reference_version); // Ensure advanced
- REQUIRE_NOTHROW(shared_group->start_read(reference_version)); // Ensure pinned
-
- ref = {}; // Destroy thread safe reference, unpinning version
- r->begin_transaction(); r->commit_transaction(); // Clean up old versions
- REQUIRE_THROWS(shared_group->start_read(reference_version)); // Verify unpinned
- }
-
- SECTION("version mismatch") {
- SECTION("resolves at older version") {
- r->begin_transaction();
- Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
- r->commit_transaction();
-
- ColKey col = num.get_object_schema().property_for_name("value")->column_key;
- ObjKey k = num.obj().get_key();
-
- REQUIRE(num.obj().get<Int>(col) == 7);
- ThreadSafeReference ref;
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Object num = Object(r2, "int object", k);
- REQUIRE(num.obj().get<Int>(col) == 7);
-
- r2->begin_transaction();
- num.obj().set(col, 9);
- r2->commit_transaction();
-
- ref = num;
- };
-
- REQUIRE(num.obj().get<Int>(col) == 7);
- Object num_prime = ref.resolve<Object>(r);
- REQUIRE(num_prime.obj().get<Int>(col) == 9);
- REQUIRE(num.obj().get<Int>(col) == 9);
-
- r->begin_transaction();
- num.obj().set(col, 11);
- r->commit_transaction();
-
- REQUIRE(num_prime.obj().get<Int>(col) == 11);
- REQUIRE(num.obj().get<Int>(col) == 11);
- }
-
- SECTION("resolve at newer version") {
- r->begin_transaction();
- Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
- r->commit_transaction();
-
- ColKey col = num.get_object_schema().property_for_name("value")->column_key;
- ObjKey k = num.obj().get_key();
-
- REQUIRE(num.obj().get<Int>(col) == 7);
- auto ref = ThreadSafeReference(num);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Object num = Object(r2, "int object", k);
-
- r2->begin_transaction();
- num.obj().set(col, 9);
- r2->commit_transaction();
- REQUIRE(num.obj().get<Int>(col) == 9);
-
- Object num_prime = ref.resolve<Object>(r2);
- REQUIRE(num_prime.obj().get<Int>(col) == 9);
-
- r2->begin_transaction();
- num_prime.obj().set(col, 11);
- r2->commit_transaction();
-
- REQUIRE(num.obj().get<Int>(col) == 11);
- REQUIRE(num_prime.obj().get<Int>(col) == 11);
- }
-
- REQUIRE(num.obj().get<Int>(col) == 7);
- r->refresh();
- REQUIRE(num.obj().get<Int>(col) == 11);
- }
-
- SECTION("resolve at newer version when schema is specified") {
- r->close();
- config.schema = schema;
- SharedRealm r = Realm::get_shared_realm(config);
- r->begin_transaction();
- Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
- r->commit_transaction();
-
- ColKey col = num.get_object_schema().property_for_name("value")->column_key;
- auto ref = ThreadSafeReference(num);
-
- r->begin_transaction();
- num.obj().set(col, 9);
- r->commit_transaction();
-
- REQUIRE_NOTHROW(ref.resolve<Object>(r));
- }
-
- SECTION("resolve references at multiple versions") {
- auto commit_new_num = [&](int64_t value) -> Object {
- r->begin_transaction();
- Object num = create_object(r, "int object", {{"value", value}});
- r->commit_transaction();
- return num;
- };
-
- auto ref1 = ThreadSafeReference(commit_new_num(1));
- auto ref2 = ThreadSafeReference(commit_new_num(2));
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Object num1 = ref1.resolve<Object>(r2);
- Object num2 = ref2.resolve<Object>(r2);
-
- ColKey col = num1.get_object_schema().property_for_name("value")->column_key;
- REQUIRE(num1.obj().get<Int>(col) == 1);
- REQUIRE(num2.obj().get<Int>(col) == 2);
- }
- }
- }
-
- SECTION("same thread") {
- r->begin_transaction();
- Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
- r->commit_transaction();
-
- ColKey col = num.get_object_schema().property_for_name("value")->column_key;
- REQUIRE(num.obj().get<Int>(col) == 7);
- auto ref = ThreadSafeReference(num);
- bool did_run_section = false;
-
- SECTION("same realm") {
- did_run_section = true;
- {
- Object num = ref.resolve<Object>(r);
- REQUIRE(num.obj().get<Int>(col) == 7);
- r->begin_transaction();
- num.obj().set(col, 9);
- r->commit_transaction();
- REQUIRE(num.obj().get<Int>(col) == 9);
- }
- REQUIRE(num.obj().get<Int>(col) == 9);
- }
- SECTION("different realm") {
- did_run_section = true;
- {
- SharedRealm r = Realm::get_shared_realm(config);
- Object num = ref.resolve<Object>(r);
- REQUIRE(num.obj().get<Int>(col) == 7);
- r->begin_transaction();
- num.obj().set(col, 9);
- r->commit_transaction();
- REQUIRE(num.obj().get<Int>(col) == 9);
- }
- REQUIRE(num.obj().get<Int>(col) == 7);
- }
- catch2_ensure_section_run_workaround(did_run_section, "same thread", [&](){
- r->begin_transaction(); // advance to latest version by starting a write
- REQUIRE(num.obj().get<Int>(col) == 9);
- r->cancel_transaction();
- });
- }
-
- SECTION("passing over") {
- SECTION("objects") {
- r->begin_transaction();
- auto str = create_object(r, "string object", {});
- auto num = create_object(r, "int object", {{"value", INT64_C(0)}});
- r->commit_transaction();
-
- ColKey col_num = num.get_object_schema().property_for_name("value")->column_key;
- ColKey col_str = str.get_object_schema().property_for_name("value")->column_key;
- auto ref_str = ThreadSafeReference(str);
- auto ref_num = ThreadSafeReference(num);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Object str = ref_str.resolve<Object>(r2);
- Object num = ref_num.resolve<Object>(r2);
-
- REQUIRE(str.obj().get<String>(col_str).is_null());
- REQUIRE(num.obj().get<Int>(col_num) == 0);
-
- r2->begin_transaction();
- str.obj().set(col_str, "the meaning of life");
- num.obj().set(col_num, 42);
- r2->commit_transaction();
- }
-
- REQUIRE(str.obj().get<String>(col_str).is_null());
- REQUIRE(num.obj().get<Int>(col_num) == 0);
-
- r->refresh();
-
- REQUIRE(str.obj().get<String>(col_str) == "the meaning of life");
- REQUIRE(num.obj().get<Int>(col_num) == 42);
- }
-
- SECTION("object list") {
- r->begin_transaction();
- auto zero = create_object(r, "int object", {{"value", INT64_C(0)}});
- auto obj = create_object(r, "int array object", {{"value", AnyVector{zero}}});
- auto col = get_table(*r, "int array object")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 0);
- auto ref = ThreadSafeReference(list);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- List list = ref.resolve<List>(r2);
- REQUIRE(list.size() == 1);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 0);
-
- r2->begin_transaction();
- list.remove_all();
- auto one = create_object(r2, "int object", {{"value", INT64_C(1)}});
- auto two = create_object(r2, "int object", {{"value", INT64_C(2)}});
- list.add(one.obj());
- list.add(two.obj());
- r2->commit_transaction();
-
- REQUIRE(list.size() == 2);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 1);
- REQUIRE(list.get(1).get<int64_t>(int_obj_col) == 2);
- }
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 0);
-
- r->refresh();
-
- REQUIRE(list.size() == 2);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 1);
- REQUIRE(list.get(1).get<int64_t>(int_obj_col) == 2);
- }
-
- SECTION("sorted object results") {
- auto& table = *get_table(*r, "string object");
- auto col = table.get_column_key("value");
- auto results = Results(r, table.where().not_equal(col, "C")).sort({{{col}}, {false}});
-
- r->begin_transaction();
- create_object(r, "string object", {{"value", "A"s}});
- create_object(r, "string object", {{"value", "B"s}});
- create_object(r, "string object", {{"value", "C"s}});
- create_object(r, "string object", {{"value", "D"s}});
- r->commit_transaction();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get(0).get<StringData>(col) == "D");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- REQUIRE(results.get(2).get<StringData>(col) == "A");
- auto ref = ThreadSafeReference(results);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Results results = ref.resolve<Results>(r2);
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get(0).get<StringData>(col) == "D");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- REQUIRE(results.get(2).get<StringData>(col) == "A");
-
- r2->begin_transaction();
- results.get(2).remove();
- results.get(0).remove();
- create_object(r2, "string object", {{"value", "E"s}});
- r2->commit_transaction();
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get(0).get<StringData>(col) == "E");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- }
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get(0).get<StringData>(col) == "D");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- REQUIRE(results.get(2).get<StringData>(col) == "A");
-
- r->refresh();
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get(0).get<StringData>(col) == "E");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- }
-
- SECTION("distinct object results") {
- auto& table = *get_table(*r, "string object");
- auto col = table.get_column_key("value");
- auto results = Results(r, table.where()).distinct({{{col}}}).sort({{"value", true}});
-
- r->begin_transaction();
- create_object(r, "string object", {{"value", "A"s}});
- create_object(r, "string object", {{"value", "A"s}});
- create_object(r, "string object", {{"value", "B"s}});
- r->commit_transaction();
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get(0).get<StringData>(col) == "A");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- auto ref = ThreadSafeReference(results);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Results results = ref.resolve<Results>(r2);
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get(0).get<StringData>(col) == "A");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
-
- r2->begin_transaction();
- results.get(0).remove();
- create_object(r2, "string object", {{"value", "C"s}});
- r2->commit_transaction();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get(0).get<StringData>(col) == "A");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- REQUIRE(results.get(2).get<StringData>(col) == "C");
- }
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get(0).get<StringData>(col) == "A");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
-
- r->refresh();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get(0).get<StringData>(col) == "A");
- REQUIRE(results.get(1).get<StringData>(col) == "B");
- REQUIRE(results.get(2).get<StringData>(col) == "C");
- }
-
- SECTION("int list") {
- r->begin_transaction();
- auto obj = create_object(r, "int array", {{"value", AnyVector{INT64_C(0)}}});
- auto col = get_table(*r, "int array")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- auto ref = ThreadSafeReference(list);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- List list = ref.resolve<List>(r2);
- REQUIRE(list.size() == 1);
- REQUIRE(list.get<int64_t>(0) == 0);
-
- r2->begin_transaction();
- list.remove_all();
- list.add(int64_t(1));
- list.add(int64_t(2));
- r2->commit_transaction();
-
- REQUIRE(list.size() == 2);
- REQUIRE(list.get<int64_t>(0) == 1);
- REQUIRE(list.get<int64_t>(1) == 2);
- };
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.get<int64_t>(0) == 0);
-
- r->refresh();
-
- REQUIRE(list.size() == 2);
- REQUIRE(list.get<int64_t>(0) == 1);
- REQUIRE(list.get<int64_t>(1) == 2);
- }
-
- SECTION("sorted int results") {
- r->begin_transaction();
- auto obj = create_object(r, "int array", {{"value", AnyVector{INT64_C(0), INT64_C(2), INT64_C(1)}}});
- auto col = get_table(*r, "int array")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- auto results = list.sort({{"self", true}});
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 0);
- REQUIRE(results.get<int64_t>(1) == 1);
- REQUIRE(results.get<int64_t>(2) == 2);
- auto ref = ThreadSafeReference(results);
- std::thread([ref = std::move(ref), config]() mutable {
- config.scheduler = util::Scheduler::get_frozen();
- SharedRealm r = Realm::get_shared_realm(config);
- Results results = ref.resolve<Results>(r);
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 0);
- REQUIRE(results.get<int64_t>(1) == 1);
- REQUIRE(results.get<int64_t>(2) == 2);
-
- r->begin_transaction();
- auto table = get_table(*r, "int array");
- List list(r, *table->begin(), table->get_column_key("value"));
- list.remove(1);
- list.add(int64_t(-1));
- r->commit_transaction();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == -1);
- REQUIRE(results.get<int64_t>(1) == 0);
- REQUIRE(results.get<int64_t>(2) == 1);
- }).join();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 0);
- REQUIRE(results.get<int64_t>(1) == 1);
- REQUIRE(results.get<int64_t>(2) == 2);
-
- r->refresh();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == -1);
- REQUIRE(results.get<int64_t>(1) == 0);
- REQUIRE(results.get<int64_t>(2) == 1);
- }
-
- SECTION("distinct int results") {
- r->begin_transaction();
- auto obj = create_object(
- r, "int array", {{"value", AnyVector{INT64_C(3), INT64_C(2), INT64_C(1), INT64_C(1), INT64_C(2)}}});
- auto col = get_table(*r, "int array")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- auto results = list.as_results().distinct({"self"}).sort({{"self", true}});
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 1);
- REQUIRE(results.get<int64_t>(1) == 2);
- REQUIRE(results.get<int64_t>(2) == 3);
-
- auto ref = ThreadSafeReference(results);
- std::thread([ref = std::move(ref), config]() mutable {
- config.scheduler = util::Scheduler::get_frozen();
- SharedRealm r = Realm::get_shared_realm(config);
- Results results = ref.resolve<Results>(r);
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 1);
- REQUIRE(results.get<int64_t>(1) == 2);
- REQUIRE(results.get<int64_t>(2) == 3);
-
- r->begin_transaction();
- auto table = get_table(*r, "int array");
- List list(r, *table->begin(), table->get_column_key("value"));
- list.remove(1);
- list.remove(0);
- r->commit_transaction();
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get<int64_t>(0) == 1);
- REQUIRE(results.get<int64_t>(1) == 2);
- }).join();
-
- REQUIRE(results.size() == 3);
- REQUIRE(results.get<int64_t>(0) == 1);
- REQUIRE(results.get<int64_t>(1) == 2);
- REQUIRE(results.get<int64_t>(2) == 3);
-
- r->refresh();
-
- REQUIRE(results.size() == 2);
- REQUIRE(results.get<int64_t>(0) == 1);
- REQUIRE(results.get<int64_t>(1) == 2);
- }
-
- SECTION("multiple types") {
- auto results = Results(r, get_table(*r, "int object")->where().equal(int_obj_col, 5));
-
- r->begin_transaction();
- auto num = create_object(r, "int object", {{"value", INT64_C(5)}});
- auto obj = create_object(r, "int array object", {{"value", AnyVector{}}});
- auto col = get_table(*r, "int array object")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 1);
- REQUIRE(results.get(0).get<int64_t>(int_obj_col) == 5);
- auto ref_num = ThreadSafeReference(num);
- auto ref_list = ThreadSafeReference(list);
- auto ref_results = ThreadSafeReference(results);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- auto num = ref_num.resolve<Object>(r2);
- auto list = ref_list.resolve<List>(r2);
- auto results = ref_results.resolve<Results>(r2);
-
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 1);
- REQUIRE(results.get(0).get<int64_t>(int_obj_col) == 5);
-
- r2->begin_transaction();
- num.obj().set_all(6);
- list.add(num.obj().get_key());
- r2->commit_transaction();
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 6);
- REQUIRE(results.size() == 0);
- }
-
- REQUIRE(list.size() == 0);
- REQUIRE(results.size() == 1);
- REQUIRE(results.get(0).get<int64_t>(int_obj_col) == 5);
-
- r->refresh();
-
- REQUIRE(list.size() == 1);
- REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 6);
- REQUIRE(results.size() == 0);
- }
- }
-
- SECTION("resolve at version where handed over thing has been deleted") {
- Object obj;
- auto delete_and_resolve = [&](auto&& list) {
- auto ref = ThreadSafeReference(list);
-
- r->begin_transaction();
- obj.obj().remove();
- r->commit_transaction();
-
- return ref.resolve<typename std::remove_reference<decltype(list)>::type>(r);
- };
-
- SECTION("object") {
- r->begin_transaction();
- obj = create_object(r, "int object", {{"value", INT64_C(7)}});
- r->commit_transaction();
-
- REQUIRE(!delete_and_resolve(obj).is_valid());
- }
-
- SECTION("object list") {
- r->begin_transaction();
- obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- auto col = get_table(*r, "int array object")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- REQUIRE(!delete_and_resolve(list).is_valid());
- }
-
- SECTION("int list") {
- r->begin_transaction();
- obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- auto col = get_table(*r, "int array")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- REQUIRE(!delete_and_resolve(list).is_valid());
- }
-
- SECTION("object results") {
- r->begin_transaction();
- obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- auto col = get_table(*r, "int array object")->get_column_key("value");
- List list(r, obj.obj(), col);
- r->commit_transaction();
-
- auto results = delete_and_resolve(list.sort({{"value", true}}));
- REQUIRE(results.is_valid());
- REQUIRE(results.size() == 0);
- }
-
- SECTION("int results") {
- r->begin_transaction();
- obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- List list(r, obj.obj(), get_table(*r, "int array")->get_column_key("value"));
- r->commit_transaction();
-
- REQUIRE(!delete_and_resolve(list).is_valid());
- }
- }
-
- SECTION("resolve at version before where handed over thing was created") {
- auto create_ref = [&](auto&& fn) -> ThreadSafeReference {
- ThreadSafeReference ref;
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- auto obj = fn(r2);
- r2->commit_transaction();
- ref = obj;
- };
- return ref;
- };
-
- SECTION("object") {
- auto obj = create_ref([](auto& r) {
- return create_object(r, "int object", {{"value", INT64_C(7)}});
- }).resolve<Object>(r);
- REQUIRE(obj.is_valid());
- REQUIRE(obj.get_column_value<int64_t>("value") == 7);
- }
-
- SECTION("object list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- return List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE(list.is_valid());
- REQUIRE(list.size() == 1);
- }
-
- SECTION("int list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE(list.is_valid());
- REQUIRE(list.size() == 1);
- }
-
- SECTION("object results") {
- auto results = create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- Results results = List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"))
- .sort({{"value", true}});
- REQUIRE(results.size() == 1);
- return results;
- }).resolve<Results>(r);
- REQUIRE(results.is_valid());
- REQUIRE(results.size() == 1);
- }
-
- SECTION("int results") {
- auto results = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value")).sort({{"self", true}});
- }).resolve<Results>(r);
- REQUIRE(results.is_valid());
- REQUIRE(results.size() == 1);
- }
- }
-
- SECTION("create TSR inside the write transaction which created the object being handed over") {
- auto create_ref = [&](auto&& fn) -> ThreadSafeReference {
- ThreadSafeReference ref;
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- ref = fn(r2);
- r2->commit_transaction();
- };
- return ref;
- };
-
- SECTION("object") {
- auto obj = create_ref([](auto& r) {
- return create_object(r, "int object", {{"value", INT64_C(7)}});
- }).resolve<Object>(r);
- REQUIRE(obj.is_valid());
- REQUIRE(obj.get_column_value<int64_t>("value") == 7);
- }
-
- SECTION("object list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- return List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE(list.is_valid());
- REQUIRE(list.size() == 1);
- }
-
- SECTION("int list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE(list.is_valid());
- REQUIRE(list.size() == 1);
- }
-
- SECTION("object results") {
- REQUIRE_THROWS(create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- Results results = List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"))
- .sort({{"value", true}});
- REQUIRE(results.size() == 1);
- return results;
- }));
- }
-
- SECTION("int results") {
- auto results = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value")).sort({{"self", true}});
- }).resolve<Results>(r);
- REQUIRE(results.is_valid());
- REQUIRE(results.size() == 1);
- }
- }
-
- SECTION("create TSR inside cancelled write transaction") {
- auto create_ref = [&](auto&& fn) -> ThreadSafeReference {
- ThreadSafeReference ref;
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- r2->begin_transaction();
- ref = fn(r2);
- r2->cancel_transaction();
- };
- return ref;
- };
-
- SECTION("object") {
- auto obj = create_ref([](auto& r) {
- return create_object(r, "int object", {{"value", INT64_C(7)}});
- }).resolve<Object>(r);
- REQUIRE_FALSE(obj.is_valid());
- }
-
- SECTION("object list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- return List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE_FALSE(list.is_valid());
- }
-
- SECTION("int list") {
- auto list = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value"));
- }).resolve<List>(r);
- REQUIRE_FALSE(list.is_valid());
- }
-
- SECTION("object results") {
- REQUIRE_THROWS(create_ref([](auto& r) {
- auto obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
- Results results = List(r, obj.obj(), get_table(*r, "int array object")->get_column_key("value"))
- .sort({{"value", true}});
- REQUIRE(results.size() == 1);
- return results;
- }));
- }
-
- SECTION("int results") {
- auto results = create_ref([](auto& r) {
- auto obj = create_object(r, "int array", {{"value", AnyVector{{INT64_C(1)}}}});
- return List(r, obj.obj(), get_table(*r, "int array")->get_column_key("value")).sort({{"self", true}});
- }).resolve<Results>(r);
- REQUIRE_FALSE(results.is_valid());
- }
- }
-
- SECTION("lifetime") {
- SECTION("retains source realm") { // else version will become unpinned
- auto ref = ThreadSafeReference(foo);
- foo = {};
- r = nullptr;
- r = Realm::get_shared_realm(config);
- REQUIRE_NOTHROW(ref.resolve<Object>(r));
- }
-
- SECTION("retains source RealmCoordinator") {
- auto ref = ThreadSafeReference(foo);
- auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path).get();
- foo = {};
- r = nullptr;
- REQUIRE(coordinator == _impl::RealmCoordinator::get_existing_coordinator(config.path).get());
- }
- }
-
- SECTION("metadata") {
- r->begin_transaction();
- auto num = create_object(r, "int object", {{"value", INT64_C(5)}});
- r->commit_transaction();
- REQUIRE(num.get_object_schema().name == "int object");
-
- auto ref = ThreadSafeReference(num);
- {
- SharedRealm r2 = Realm::get_shared_realm(config);
- Object num = ref.resolve<Object>(r2);
- REQUIRE(num.get_object_schema().name == "int object");
- }
- }
-
- SECTION("allow multiple resolves") {
- auto ref = ThreadSafeReference(foo);
- ref.resolve<Object>(r);
- REQUIRE_NOTHROW(ref.resolve<Object>(r));
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp
deleted file mode 100644
index ed52366de..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp
+++ /dev/null
@@ -1,1714 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "catch2/catch.hpp"
-
-#include "util/index_helpers.hpp"
-#include "util/test_file.hpp"
-#include "util/test_utils.hpp"
-
-#include "impl/collection_notifier.hpp"
-#include "impl/realm_coordinator.hpp"
-#include "impl/transact_log_handler.hpp"
-#include "binding_context.hpp"
-#include "property.hpp"
-#include "object_schema.hpp"
-#include "schema.hpp"
-
-#include <realm/db.hpp>
-#include <realm/history.hpp>
-
-using namespace realm;
-
-class CaptureHelper {
-public:
- CaptureHelper(TransactionRef group, SharedRealm const& r, LnkLst& lv, TableKey table_key)
- : m_realm(r)
- , m_group(group)
- , m_list(lv)
- , m_table_key(table_key)
- {
- m_realm->begin_transaction();
-
- m_initial.reserve(lv.size());
- for (size_t i = 0; i < lv.size(); ++i)
- m_initial.push_back(lv.ObjList::get_key(i));
- }
-
- CollectionChangeSet finish() {
- m_realm->commit_transaction();
-
- _impl::CollectionChangeBuilder c;
- _impl::TransactionChangeInfo info{};
- info.tables[m_table_key.value];
- info.lists.push_back({m_table_key, m_list.ConstLstBase::get_key().value, m_list.get_col_key().value, &c});
- _impl::transaction::advance(*m_group, info);
-
- if (info.lists.empty()) {
- REQUIRE(!m_list.is_attached());
- return {};
- }
-
- validate(c);
- return std::move(c);
- }
-
- explicit operator bool() const { return m_realm->is_in_transaction(); }
-
-private:
- SharedRealm m_realm;
- TransactionRef m_group;
-
- LnkLst& m_list;
- std::vector<ObjKey> m_initial;
- TableKey m_table_key;
-
- void validate(CollectionChangeSet const& info)
- {
- info.insertions.verify();
- info.deletions.verify();
- info.modifications.verify();
-
- std::vector<ObjKey> move_sources;
- for (auto const& move : info.moves)
- move_sources.push_back(m_initial[move.from]);
-
- // Apply the changes from the transaction log to our copy of the
- // initial, using UITableView's batching rules (i.e. delete, then
- // insert, then update)
- auto it = util::make_reverse_iterator(info.deletions.end());
- auto end = util::make_reverse_iterator(info.deletions.begin());
- for (; it != end; ++it) {
- m_initial.erase(m_initial.begin() + it->first, m_initial.begin() + it->second);
- }
-
- m_list.size();
- for (auto const& range : info.insertions) {
- for (auto i = range.first; i < range.second; ++i)
- m_initial.insert(m_initial.begin() + i, m_list.ObjList::get_key(i));
- }
-
- for (auto const& range : info.modifications) {
- for (auto i = range.first; i < range.second; ++i)
- m_initial[i] = m_list.ObjList::get_key(i);
- }
-
- REQUIRE(m_list.is_attached());
-
- // and make sure we end up with the same end result
- if (m_initial.size() != m_list.size()) {
- std::cout << "Error " << m_list.size() << std::endl;
- }
- REQUIRE(m_initial.size() == m_list.size());
- for (size_t i = 0; i < m_initial.size(); ++i)
- CHECK(m_initial[i] == m_list.ObjList::get_key(i));
-
- // Verify that everything marked as a move actually is one
- for (size_t i = 0; i < move_sources.size(); ++i) {
- if (!info.modifications.contains(info.moves[i].to)) {
- CHECK(m_list.ObjList::get_key(info.moves[i].to) == move_sources[i]);
- }
- }
- }
-};
-
-struct ArrayChange {
- BindingContext::ColumnInfo::Kind kind;
- IndexSet indices;
-};
-
-static bool operator==(ArrayChange const& a, ArrayChange const& b)
-{
- return a.kind == b.kind
- && std::equal(a.indices.as_indexes().begin(), a.indices.as_indexes().end(),
- b.indices.as_indexes().begin(), b.indices.as_indexes().end());
-}
-
-namespace Catch {
-template<>
-struct StringMaker<ArrayChange> {
- static std::string convert(ArrayChange const& c)
- {
- std::stringstream ss;
- switch (c.kind) {
- case BindingContext::ColumnInfo::Kind::Insert: ss << "Insert{"; break;
- case BindingContext::ColumnInfo::Kind::Remove: ss << "Remove{"; break;
- case BindingContext::ColumnInfo::Kind::Set: ss << "Set{"; break;
- case BindingContext::ColumnInfo::Kind::SetAll: return "SetAll";
- case BindingContext::ColumnInfo::Kind::None: return "None";
- }
- for (auto& range : c.indices)
- ss << range.first << "-" << range.second << ", ";
- auto str = ss.str();
- str.pop_back();
- str.back() = '}';
- return str;
- }
-};
-} // namespace Catch
-
-class KVOContext : public BindingContext {
-public:
- KVOContext(std::initializer_list<Obj> objects)
- {
- m_result.reserve(objects.size());
- for (auto& obj : objects) {
- m_result.push_back(ObserverState{obj.get_table()->get_key(),
- obj.get_key().value, (void *)(uintptr_t)m_result.size()});
- }
- }
-
- bool modified(size_t index, ColKey col_key) const noexcept
- {
- auto it = std::find_if(begin(m_result), end(m_result),
- [=](auto&& change) { return (void *)(uintptr_t)index == change.info; });
- if (it == m_result.end())
- return false;
- auto col = it->changes.find(col_key.value);
- return col != it->changes.end() && col->second.kind != BindingContext::ColumnInfo::Kind::None;
- }
-
- bool invalidated(size_t index) const noexcept
- {
- return std::find(begin(m_invalidated), end(m_invalidated), (void *)(uintptr_t)index) != end(m_invalidated);
- }
-
- ArrayChange array_change(size_t index, ColKey col_key) const noexcept
- {
- auto& changes = m_result[index].changes;
- auto col = changes.find(col_key.value);
- return col == changes.end()
- ? ArrayChange{ColumnInfo::Kind::None, {}}
- : ArrayChange{col->second.kind, col->second.indices};
- }
-
-private:
- std::vector<ObserverState> m_result;
- std::vector<void*> m_invalidated;
-
- std::vector<ObserverState> get_observed_rows() override
- {
- return m_result;
- }
-
- void did_change(std::vector<ObserverState> const& observers,
- std::vector<void*> const& invalidated, bool) override
- {
- m_invalidated = invalidated;
- m_result = observers;
- }
-};
-
-TEST_CASE("Transaction log parsing: schema change validation") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- config.schema_mode = SchemaMode::Additive;
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"table", {
- {"unindexed", PropertyType::Int},
- {"indexed", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}}
- }},
- });
- r->read_group();
-
- auto history = make_in_realm_history(config.path);
- auto db = DB::create(*history, config.options());
-
- SECTION("adding a table is allowed") {
- auto wt = db->start_write();
- TableRef table = wt->add_table("new table");
- table->add_column(type_String, "new col");
- wt->commit();
-
- REQUIRE_NOTHROW(r->refresh());
- }
-
- SECTION("adding a column at the end of an existing table is allowed") {
- auto wt = db->start_write();
- TableRef table = wt->get_table("class_table");
- table->add_column(type_String, "new col");
- wt->commit();
-
- REQUIRE_NOTHROW(r->refresh());
- }
-
- SECTION("removing a column is not allowed") {
- auto wt = db->start_write();
- TableRef table = wt->get_table("class_table");
- table->remove_column(table->get_column_key("indexed"));
- wt->commit();
-
- REQUIRE_THROWS(r->refresh());
- }
-
- SECTION("removing a table is not allowed") {
- auto wt = db->start_write();
- wt->remove_table("class_table");
- wt->commit();
-
- REQUIRE_THROWS(r->refresh());
- }
-}
-
-TEST_CASE("Transaction log parsing: changeset calcuation") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
-
- SECTION("table change information") {
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"table", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"value", PropertyType::Int}
- }},
- });
-
- auto& table = *r->read_group().get_table("class_table");
- auto table_key = table.get_key().value;
- auto cols = table.get_column_keys();
-
- r->begin_transaction();
- std::vector<ObjKey> objects;
- table.create_objects(10, objects);
- for (int i = 0; i < 10; ++i)
- table.get_object(objects[i]).set_all(i, i);
- r->commit_transaction();
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
- using TableKeyType = decltype(TableKey::value);
- auto track_changes = [&](std::vector<TableKeyType> tables_needed, auto&& f) {
- auto sg = coordinator->begin_read();
-
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- _impl::TransactionChangeInfo info{};
- for (auto table : tables_needed)
- info.tables[table];
- _impl::transaction::advance(static_cast<Transaction&>(*sg), info);
- return info;
- };
-
- SECTION("modifying a row marks it as modified") {
- auto info = track_changes({table_key}, [&] {
- table.get_object(objects[1]).set(cols[1], 2);
- });
- REQUIRE(info.tables.size() == 1);
- REQUIRE(info.tables[table_key].modifications_size() == 1);
- REQUIRE(info.tables[table_key].modifications_contains(1));
- }
-
- SECTION("modifications to untracked tables are ignored") {
- auto info = track_changes({}, [&] {
- table.get_object(objects[1]).set(cols[1], 2);
- });
- REQUIRE(info.tables.empty());
- }
-
- SECTION("new row additions are reported") {
- auto info = track_changes({table_key}, [&] {
- table.create_object();
- table.create_object();
- });
- REQUIRE(info.tables.size() == 1);
- REQUIRE(info.tables[table_key].insertions_size() == 2);
- REQUIRE(info.tables[table_key].insertions_contains(10));
- REQUIRE(info.tables[table_key].insertions_contains(11));
- }
-
- SECTION("deleting newly added rows makes them not be reported") {
- auto info = track_changes({table_key}, [&] {
- table.create_object();
- table.remove_object(table.create_object().get_key());
- });
- REQUIRE(info.tables.size() == 1);
- REQUIRE(info.tables[table_key].insertions_size() == 1);
- REQUIRE(info.tables[table_key].insertions_contains(10));
- REQUIRE(info.tables[table_key].deletions_empty());
- }
-
- SECTION("modifying newly added rows does not report it as a modification") {
- auto info = track_changes({table_key}, [&] {
- table.create_object().set_all(10, 0);
- });
- REQUIRE(info.tables.size() == 1);
- REQUIRE(info.tables[table_key].insertions_size() == 1);
- REQUIRE(info.tables[table_key].insertions_contains(10));
- REQUIRE(info.tables[table_key].modifications_size() == 0);
- REQUIRE(!info.tables[table_key].modifications_contains(10));
- REQUIRE(info.tables[table_key].deletions_empty());
- }
-
- SECTION("remove_object() does not shift rows") {
- auto info = track_changes({table_key}, [&] {
- table.remove_object(objects[2]);
- table.remove_object(objects[3]);
- });
- REQUIRE(info.tables.size() == 1);
- REQUIRE(info.tables[table_key].deletions_size() == 2);
- REQUIRE(info.tables[table_key].deletions_contains(2));
- REQUIRE(info.tables[table_key].deletions_contains(3));
- REQUIRE(info.tables[table_key].insertions_empty());
- REQUIRE(info.tables[table_key].modifications_empty());
- }
-
- SECTION("SetDefault does not mark a row as modified") {
- auto info = track_changes({table_key}, [&] {
- bool is_default = true;
- table.get_object(objects[0]).set(cols[0], 1, is_default);
- });
- REQUIRE(info.tables.empty());
- }
- }
-
- SECTION("LinkView change information") {
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"origin", {
- {"array", PropertyType::Array|PropertyType::Object, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }},
- });
-
- auto origin = r->read_group().get_table("class_origin");
- auto target = r->read_group().get_table("class_target");
-
- r->begin_transaction();
-
- LnkLst lv = origin->create_object().get_linklist("array");
- std::vector<ObjKey> target_keys;
- for (int i = 0; i < 10; ++i) {
- target_keys.push_back(target->create_object().set_all(i).get_key());
- lv.add(target_keys.back());
- }
-
- r->commit_transaction();
-
- auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
-#define VALIDATE_CHANGES(out) \
- for (CaptureHelper helper(std::static_pointer_cast<Transaction>(coordinator->begin_read()), r, lv, origin->get_key()); \
- helper; out = helper.finish())
-
- CollectionChangeSet changes;
- SECTION("single change type") {
- SECTION("add single") {
- VALIDATE_CHANGES(changes) {
- lv.add(target_keys[0]);
- }
- REQUIRE_INDICES(changes.insertions, 10);
- }
- SECTION("add multiple") {
- VALIDATE_CHANGES(changes) {
- lv.add(target_keys[0]);
- lv.add(target_keys[0]);
- }
- REQUIRE_INDICES(changes.insertions, 10, 11);
- }
-
- SECTION("erase single") {
- VALIDATE_CHANGES(changes) {
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 5);
- }
- SECTION("erase contiguous forward") {
- VALIDATE_CHANGES(changes) {
- lv.remove(5);
- lv.remove(5);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 5, 6, 7);
- }
- SECTION("erase contiguous reverse") {
- VALIDATE_CHANGES(changes) {
- lv.remove(7);
- lv.remove(6);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 5, 6, 7);
- }
- SECTION("erase contiguous mixed") {
- VALIDATE_CHANGES(changes) {
- lv.remove(5);
- lv.remove(6);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 5, 6, 7);
- }
- SECTION("erase scattered forward") {
- VALIDATE_CHANGES(changes) {
- lv.remove(3);
- lv.remove(4);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 3, 5, 7);
- }
- SECTION("erase scattered backwards") {
- VALIDATE_CHANGES(changes) {
- lv.remove(7);
- lv.remove(5);
- lv.remove(3);
- }
- REQUIRE_INDICES(changes.deletions, 3, 5, 7);
- }
- SECTION("erase scattered mixed") {
- VALIDATE_CHANGES(changes) {
- lv.remove(3);
- lv.remove(6);
- lv.remove(4);
- }
- REQUIRE_INDICES(changes.deletions, 3, 5, 7);
- }
-
- SECTION("set single") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- }
- REQUIRE_INDICES(changes.modifications, 5);
- }
- SECTION("set contiguous") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.set(6, target_keys[0]);
- lv.set(7, target_keys[0]);
- }
- REQUIRE_INDICES(changes.modifications, 5, 6, 7);
- }
- SECTION("set scattered") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.set(7, target_keys[0]);
- lv.set(9, target_keys[0]);
- }
- REQUIRE_INDICES(changes.modifications, 5, 7, 9);
- }
- SECTION("set redundant") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.set(5, target_keys[0]);
- lv.set(5, target_keys[0]);
- }
- REQUIRE_INDICES(changes.modifications, 5);
- }
-
- SECTION("clear") {
- VALIDATE_CHANGES(changes) {
- lv.clear();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("move backward") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 3);
- }
- REQUIRE_MOVES(changes, {5, 3});
- }
-
- SECTION("move forward") {
- VALIDATE_CHANGES(changes) {
- lv.move(1, 3);
- }
- REQUIRE_MOVES(changes, {1, 3});
- }
-
- SECTION("chained moves") {
- VALIDATE_CHANGES(changes) {
- lv.move(1, 3);
- lv.move(3, 5);
- }
- REQUIRE_MOVES(changes, {1, 5});
- }
-
- SECTION("backwards chained moves") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 3);
- lv.move(3, 1);
- }
- REQUIRE_MOVES(changes, {5, 1});
- }
-
- SECTION("moves shifting other moves") {
- VALIDATE_CHANGES(changes) {
- lv.move(1, 5);
- lv.move(2, 7);
- }
- REQUIRE_MOVES(changes, {1, 4}, {3, 7});
-
- VALIDATE_CHANGES(changes) {
- lv.move(1, 5);
- lv.move(7, 0);
- }
- REQUIRE_MOVES(changes, {1, 6}, {7, 0});
- }
-
- SECTION("move to current location is a no-op") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 5);
- }
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
- REQUIRE(changes.moves.empty());
- }
-
- SECTION("delete a target row") {
- VALIDATE_CHANGES(changes) {
- target->get_object(5).remove();
- }
- REQUIRE_INDICES(changes.deletions, 5);
- }
-
- SECTION("delete all target rows") {
- VALIDATE_CHANGES(changes) {
- lv.remove_all_target_rows();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("clear target table") {
- VALIDATE_CHANGES(changes) {
- target->clear();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("swap()") {
- VALIDATE_CHANGES(changes) {
- lv.swap(3, 5);
- }
- REQUIRE_MOVES(changes, {3, 5}, {5, 3});
- }
- }
-
- SECTION("mixed change types") {
- SECTION("set -> insert") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.insert(5, target_keys[0]);
- }
- REQUIRE_INDICES(changes.insertions, 5);
- REQUIRE_INDICES(changes.modifications, 6);
-
- VALIDATE_CHANGES(changes) {
- lv.set(4, target_keys[0]);
- lv.insert(5, target_keys[0]);
- }
- REQUIRE_INDICES(changes.insertions, 5);
- REQUIRE_INDICES(changes.modifications, 4);
- }
- SECTION("insert -> set") {
- VALIDATE_CHANGES(changes) {
- lv.insert(5, target_keys[0]);
- lv.set(5, target_keys[1]);
- }
- REQUIRE_INDICES(changes.insertions, 5);
- REQUIRE_INDICES(changes.modifications, 5);
-
- VALIDATE_CHANGES(changes) {
- lv.insert(5, target_keys[0]);
- lv.set(6, target_keys[1]);
- }
- REQUIRE_INDICES(changes.insertions, 5);
- REQUIRE_INDICES(changes.modifications, 6);
-
- VALIDATE_CHANGES(changes) {
- lv.insert(6, target_keys[0]);
- lv.set(5, target_keys[1]);
- }
- REQUIRE_INDICES(changes.insertions, 6);
- REQUIRE_INDICES(changes.modifications, 5);
- }
-
- SECTION("set -> erase") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE(changes.modifications.empty());
-
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.remove(4);
- }
- REQUIRE_INDICES(changes.deletions, 4);
- REQUIRE_INDICES(changes.modifications, 4);
-
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[0]);
- lv.remove(4);
- lv.remove(4);
- }
- REQUIRE_INDICES(changes.deletions, 4, 5);
- REQUIRE(changes.modifications.empty());
- }
-
- SECTION("erase -> set") {
- VALIDATE_CHANGES(changes) {
- lv.remove(5);
- lv.set(5, target_keys[0]);
- }
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE_INDICES(changes.modifications, 5);
- }
-
- SECTION("insert -> clear") {
- VALIDATE_CHANGES(changes) {
- lv.add(target_keys[0]);
- lv.clear();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- REQUIRE(changes.insertions.empty());
- }
-
- SECTION("set -> clear") {
- VALIDATE_CHANGES(changes) {
- lv.set(0, target_keys[5]);
- lv.clear();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- REQUIRE(changes.modifications.empty());
- }
-
- SECTION("clear -> insert") {
- VALIDATE_CHANGES(changes) {
- lv.clear();
- lv.add(target_keys[0]);
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- REQUIRE_INDICES(changes.insertions, 0);
- }
-
- SECTION("insert -> delete") {
- VALIDATE_CHANGES(changes) {
- lv.add(target_keys[0]);
- lv.remove(10);
- }
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
-
- VALIDATE_CHANGES(changes) {
- lv.add(target_keys[0]);
- lv.remove(9);
- }
- REQUIRE_INDICES(changes.deletions, 9);
- REQUIRE_INDICES(changes.insertions, 9);
-
- VALIDATE_CHANGES(changes) {
- lv.insert(1, target_keys[1]);
- lv.insert(3, target_keys[3]);
- lv.insert(5, target_keys[5]);
- lv.remove(6);
- lv.remove(4);
- lv.remove(2);
- }
- REQUIRE_INDICES(changes.deletions, 1, 2, 3);
- REQUIRE_INDICES(changes.insertions, 1, 2, 3);
-
- VALIDATE_CHANGES(changes) {
- lv.insert(1, target_keys[1]);
- lv.insert(3, target_keys[3]);
- lv.insert(5, target_keys[5]);
- lv.remove(2);
- lv.remove(3);
- lv.remove(4);
- }
- REQUIRE_INDICES(changes.deletions, 1, 2, 3);
- REQUIRE_INDICES(changes.insertions, 1, 2, 3);
- }
-
- SECTION("delete -> insert") {
- VALIDATE_CHANGES(changes) {
- lv.remove(9);
- lv.add(target_keys[0]);
- }
- REQUIRE_INDICES(changes.deletions, 9);
- REQUIRE_INDICES(changes.insertions, 9);
- }
-
- SECTION("interleaved delete and insert") {
- VALIDATE_CHANGES(changes) {
- lv.remove(9);
- lv.remove(7);
- lv.remove(5);
- lv.remove(3);
- lv.remove(1);
-
- lv.insert(4, target_keys[9]);
- lv.insert(3, target_keys[7]);
- lv.insert(2, target_keys[5]);
- lv.insert(1, target_keys[3]);
- lv.insert(0, target_keys[1]);
-
- lv.remove(9);
- lv.remove(7);
- lv.remove(5);
- lv.remove(3);
- lv.remove(1);
- }
-
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- REQUIRE_INDICES(changes.insertions, 0, 1, 2, 3, 4);
- }
-
- SECTION("move after set is just insert+delete") {
- VALIDATE_CHANGES(changes) {
- lv.set(5, target_keys[6]);
- lv.move(5, 0);
- }
-
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE_INDICES(changes.insertions, 0);
- REQUIRE_MOVES(changes, {5, 0});
- }
-
- SECTION("set after move is just insert+delete") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 0);
- lv.set(0, target_keys[6]);
- }
-
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE_INDICES(changes.insertions, 0);
- REQUIRE_MOVES(changes, {5, 0});
- }
-
- SECTION("delete after move removes original row") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 0);
- lv.remove(0);
- }
-
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE(changes.moves.empty());
- }
-
- SECTION("moving newly inserted row just changes reported index of insert") {
- VALIDATE_CHANGES(changes) {
- lv.move(5, 0);
- lv.remove(0);
- }
-
- REQUIRE_INDICES(changes.deletions, 5);
- REQUIRE(changes.moves.empty());
- }
-
- SECTION("moves shift insertions/changes like any other insertion") {
- VALIDATE_CHANGES(changes) {
- lv.insert(5, target_keys[5]);
- lv.set(6, target_keys[6]);
- lv.move(7, 4);
- }
- REQUIRE_INDICES(changes.deletions, 6);
- REQUIRE_INDICES(changes.insertions, 4, 6);
- REQUIRE_INDICES(changes.modifications, 7);
- REQUIRE_MOVES(changes, {6, 4});
- }
-
- SECTION("clear after delete") {
- VALIDATE_CHANGES(changes) {
- lv.remove(5);
- lv.clear();
- }
- REQUIRE_INDICES(changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- }
-
- SECTION("erase before previous move target") {
- VALIDATE_CHANGES(changes) {
- lv.move(2, 8);
- lv.remove(5);
- }
- REQUIRE_INDICES(changes.insertions, 7);
- REQUIRE_INDICES(changes.deletions, 2, 6);
- REQUIRE_MOVES(changes, {2, 7});
- }
-
- SECTION("insert after move updates move destination") {
- VALIDATE_CHANGES(changes) {
- lv.move(2, 8);
- lv.insert(5, target_keys[5]);
- }
- REQUIRE_MOVES(changes, {2, 9});
- }
- }
-
- SECTION("deleting the linkview") {
- SECTION("directly") {
- VALIDATE_CHANGES(changes) {
- origin->get_object(0).remove();
- }
- REQUIRE(!lv.is_attached());
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
- REQUIRE(changes.modifications.empty());
- }
-
- SECTION("table clear") {
- VALIDATE_CHANGES(changes) {
- origin->clear();
- }
- REQUIRE(!lv.is_attached());
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
- REQUIRE(changes.modifications.empty());
- }
-
- SECTION("delete a different lv") {
- r->begin_transaction();
- auto new_obj = origin->create_object();
- r->commit_transaction();
-
- VALIDATE_CHANGES(changes) {
- new_obj.remove();
- }
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
- REQUIRE(changes.modifications.empty());
- }
- }
-
- SECTION("modifying a different linkview should not produce notifications") {
- r->begin_transaction();
- auto lv2 = origin->create_object().get_linklist("array");
- lv2.add(target_keys[5]);
- r->commit_transaction();
-
- VALIDATE_CHANGES(changes) {
- lv2.add(target_keys[1]);
- lv2.add(target_keys[2]);
- lv2.remove(0);
- lv2.set(0, target_keys[6]);
- lv2.move(1, 0);
- lv2.swap(0, 1);
- lv2.clear();
- lv2.add(target_keys[1]);
- }
-
- REQUIRE(changes.insertions.empty());
- REQUIRE(changes.deletions.empty());
- REQUIRE(changes.modifications.empty());
- }
- }
-
- SECTION("object change information") {
- auto realm = Realm::get_shared_realm(config);
- realm->update_schema({
- {"origin", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"link", PropertyType::Object|PropertyType::Nullable, "target"},
- {"array", PropertyType::Array|PropertyType::Object, "target"},
- {"int array", PropertyType::Array|PropertyType::Int},
- }},
- {"origin 2", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"link", PropertyType::Object|PropertyType::Nullable, "target"},
- {"array", PropertyType::Array|PropertyType::Object, "target"}
- }},
- {"target", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- {"value 1", PropertyType::Int},
- {"value 2", PropertyType::Int},
- }},
- });
-
- auto origin = realm->read_group().get_table("class_origin");
- auto target = realm->read_group().get_table("class_target");
- auto origin_cols = origin->get_column_keys();
- auto target_cols = target->get_column_keys();
-
- realm->begin_transaction();
-
- std::vector<ObjKey> target_keys;
- target->create_objects(10, target_keys);
- for (int i = 0; i < 10; ++i)
- target->get_object(target_keys[i]).set_all(i, i, i);
-
- std::vector<ObjKey> origin_keys;
- origin->create_objects(3, origin_keys);
- origin->get_object(origin_keys[0]).set_all(5, target_keys[5]);
- origin->get_object(origin_keys[1]).set_all(5, target_keys[6]);
-
- auto lv = origin->get_object(origin_keys[0]).get_linklist(origin_cols[2]);
- for (auto key : target_keys)
- lv.add(key);
- auto lv2 = origin->get_object(origin_keys[1]).get_linklist(origin_cols[2]);
- lv2.add(target_keys[0]);
-
- auto tr = origin->get_object(origin_keys[0]).get_list<int64_t>(origin_cols[3]);
- for (int i = 0; i < 10; ++i)
- tr.add(i);
- auto tr2 = origin->get_object(origin_keys[1]).get_list<int64_t>(origin_cols[3]);
- for (int i = 0; i < 10; ++i)
- tr2.add(0);
-
- realm->read_group().get_table("class_origin 2")->create_object();
-
- realm->commit_transaction();
-
- auto observe = [&](std::initializer_list<Obj> rows, auto&& fn) {
- auto realm2 = Realm::get_shared_realm(config);
- auto& group = realm2->read_group();
- static_cast<void>(group); // silence unused warning
- KVOContext observer(rows);
- observer.realm = realm2;
- realm2->m_binding_context.reset(&observer);
-
- realm->begin_transaction();
- lv.size(); lv2.size(); tr.size(); tr2.size();
- fn();
- realm->commit_transaction();
- lv.size(); lv2.size(); tr.size(); tr2.size();
-
- realm2->refresh();
- realm2->m_binding_context.release();
-
- return observer;
- };
-
- auto observe_rollback = [&](std::initializer_list<Obj> rows, auto&& fn) {
- KVOContext observer(rows);
- observer.realm = realm;
- realm->m_binding_context.reset(&observer);
-
- realm->begin_transaction();
- lv.size(); lv2.size(); tr.size(); tr2.size();
- fn();
- realm->cancel_transaction();
- lv.size(); lv2.size(); tr.size(); tr2.size();
-
- realm->m_binding_context.release();
- return observer;
- };
-
- SECTION("setting a property marks that property as changed") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- o.set(target_cols[0], 1);
- });
- REQUIRE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("self-assignment marks as changed") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- o.set(target_cols[0], o.get<int64_t>(target_cols[0]));
- });
- REQUIRE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("SetDefault does not mark as changed") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- o.set(target_cols[0], 5, true);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("multiple properties on a single object are handled properly") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- o.set(target_cols[1], 1);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[0]));
- REQUIRE(changes.modified(0, target_cols[1]));
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
-
- changes = observe({o}, [&] {
- o.set(target_cols[2], 1);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE(changes.modified(0, target_cols[2]));
-
- changes = observe({o}, [&] {
- o.set(target_cols[0], 1);
- o.set(target_cols[2], 1);
- });
- REQUIRE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE(changes.modified(0, target_cols[2]));
-
- changes = observe({o}, [&] {
- o.set(target_cols[0], 1);
- o.set(target_cols[1], 1);
- o.set(target_cols[2], 1);
- });
- REQUIRE(changes.modified(0, target_cols[0]));
- REQUIRE(changes.modified(0, target_cols[1]));
- REQUIRE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("setting other objects does not mark as changed") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- target->get_object(target_keys[1]).set(target_cols[0], 5);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[0]));
- REQUIRE_FALSE(changes.modified(0, target_cols[1]));
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("deleting an observed object adds it to invalidated") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- o.remove();
- });
- REQUIRE(changes.invalidated(0));
- }
-
- SECTION("deleting an unobserved object does nothing") {
- auto o = target->get_object(target_keys[0]);
- auto changes = observe({o}, [&] {
- target->get_object(target_keys[1]).remove();
- });
- REQUIRE_FALSE(changes.invalidated(0));
- }
-
- SECTION("deleting the target of a link marks the link as modified") {
- auto o = origin->get_object(origin_keys[0]);
- auto changes = observe({o}, [&] {
- o.get_linked_object(origin_cols[1]).remove();
- });
- REQUIRE(changes.modified(0, origin_cols[1]));
- }
-
- SECTION("clearing the target table of a link marks the link as modified") {
- auto o = origin->get_object(origin_keys[0]);
- auto changes = observe({o}, [&] {
- target->clear();
- });
- REQUIRE(changes.modified(0, origin_cols[1]));
- }
-
- SECTION("clearing a table invalidates all observers for that table") {
- auto r1 = target->get_object(target_keys[0]);
- auto r2 = target->get_object(target_keys[5]);
- auto r3 = origin->get_object(origin_keys[0]);
- auto changes = observe({r1, r2, r3}, [&] {
- target->clear();
- });
- REQUIRE(changes.invalidated(0));
- REQUIRE(changes.invalidated(1));
- REQUIRE_FALSE(changes.invalidated(2));
- }
-
- using Kind = BindingContext::ColumnInfo::Kind;
- auto o = origin->get_object(origin_keys[0]);
- const auto lv_col = origin_cols[2];
- SECTION("array: add()") {
- auto changes = observe({o}, [&] {
- lv.add(target_keys[0]);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {10}}));
- }
-
- SECTION("array: insert()") {
- auto changes = observe({o}, [&] {
- lv.insert(4, target_keys[0]);
- lv.insert(2, target_keys[0]);
- lv.insert(8, target_keys[0]);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {2, 5, 8}}));
- }
-
- SECTION("array: remove()") {
- auto changes = observe({o}, [&] {
- lv.remove(0);
- lv.remove(2);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Remove, {0, 3}}));
- }
-
- SECTION("array: set()") {
- auto changes = observe({o}, [&] {
- lv.set(0, target_keys[3]);
- lv.set(2, target_keys[3]);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {0, 2}}));
- }
-
- SECTION("array: move()") {
- SECTION("swap forward") {
- auto changes = observe({o}, [&] {
- lv.move(3, 4);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 4}}));
- }
-
- SECTION("swap backwards") {
- auto changes = observe({o}, [&] {
- lv.move(4, 3);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 4}}));
- }
-
- SECTION("move fowards") {
- auto changes = observe({o}, [&] {
- lv.move(3, 5);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 4, 5}}));
- }
-
- SECTION("move backwards") {
- auto changes = observe({o}, [&] {
- lv.move(5, 3);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 4, 5}}));
- }
-
- SECTION("multiple moves collapsing to nothing") {
- auto changes = observe({o}, [&] {
- lv.move(3, 4);
- lv.move(4, 5);
- lv.move(5, 3);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::None, {}}));
- }
-
- SECTION("multiple moves") {
- auto changes = observe({o}, [&] {
- lv.move(3, 6);
- lv.move(6, 4);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 4}}));
-
- changes = observe({o}, [&] {
- lv.move(3, 6);
- lv.move(6, 0);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {0, 1, 2, 3}}));
-
- changes = observe({o}, [&] {
- lv.move(9, 0);
- lv.move(1, 7);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {0, 7, 8, 9}}));
- }
- }
-
- SECTION("array: swap()") {
- auto changes = observe({o}, [&] {
- lv.swap(5, 3);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Set, {3, 5}}));
- }
-
- SECTION("array: clear()") {
- auto changes = observe({o}, [&] {
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: clear() after add()") {
- auto changes = observe({o}, [&] {
- lv.add(target_keys[0]);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: clear() after set()") {
- auto changes = observe({o}, [&] {
- lv.set(5, target_keys[3]);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: clear() after remove()") {
- auto changes = observe({o}, [&] {
- lv.remove(2);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: rollback clear()") {
- auto changes = observe_rollback({o}, [&] {
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: rollback clear() after add()") {
- auto changes = observe_rollback({o}, [&] {
- lv.add(target_keys[0]);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: rollback clear() after set()") {
- auto changes = observe_rollback({o}, [&] {
- lv.set(5, target_keys[3]);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: rollback clear() after remove()") {
- auto changes = observe_rollback({o}, [&] {
- lv.remove(2);
- lv.clear();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("array: rollback add after clear()") {
- auto changes = observe_rollback({o}, [&] {
- lv.clear();
- lv.add(target_keys[0]);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::SetAll, {}}));
- }
-
- SECTION("array: multiple change kinds") {
- auto changes = observe({o}, [&] {
- lv.add(target_keys[0]);
- lv.remove(0);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::SetAll, {}}));
- }
-
- SECTION("array: modify newly inserted row") {
- auto changes = observe({o}, [&] {
- lv.add(target_keys[0]);
- lv.set(lv.size() - 1, target_keys[1]);
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::Insert, {10}}));
- }
-
- SECTION("array: modifying different array does not produce changes") {
- auto changes = observe({o}, [&] {
- lv2.add(target_keys[0]);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("array: modifying different table does not produce changes") {
- auto changes = observe({o}, [&] {
- realm->read_group().get_table("class_origin 2")->begin()->get_linklist("array").add(target_keys[0]);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[2]));
- }
-
- SECTION("array: deleting the containing row after making changes discards the changes") {
- auto changes = observe({o}, [&] {
- lv.insert(4, target_keys[0]);
- lv.insert(2, target_keys[0]);
- lv.insert(8, target_keys[0]);
- o.remove();
- });
- REQUIRE(changes.array_change(0, lv_col) == (ArrayChange{Kind::None, {}}));
- }
-
- // ----------------------------------------------------------------------
-
- const auto tr_col = origin_cols[3];
- SECTION("int array: add()") {
- auto changes = observe({o}, [&] {
- tr.add(0);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {10}}));
- }
-
- SECTION("int array: insert()") {
- auto changes = observe({o}, [&] {
- tr.insert(4, 0);
- tr.insert(2, 0);
- tr.insert(8, 0);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {2, 5, 8}}));
- }
-
- SECTION("int array: remove()") {
- auto changes = observe({o}, [&] {
- tr.remove(0);
- tr.remove(2);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Remove, {0, 3}}));
- }
-
- SECTION("int array: set()") {
- auto changes = observe({o}, [&] {
- tr.set(0, 3);
- tr.set(2, 3);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Set, {0, 2}}));
- }
-
- SECTION("int array: move()") {
- auto changes = observe({o}, [&] {
- tr.move(8, 2);
- tr.move(4, 6);
-
- // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
- // Now: 0, 1, 8, 2, 4, 5, 3, 6, 7, 9
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Set, {2, 3, 6, 7, 8}}));
- }
-
- SECTION("int array: emulated move()") {
- auto changes = observe({o}, [&] {
- // list.move(8, 2);
- tr.insert(2, 0);
- tr.swap(9, 2);
- tr.remove(9);
-
- // list.move(4, 6);
- tr.insert(7, 0);
- tr.swap(4, 7);
- tr.remove(4);
-
- // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
- // Now: 0, 1, 8, 2, 4, 5, 3, 6, 7, 9
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Set, {2, 3, 6, 7, 8}}));
- }
-
- SECTION("int array: swap()") {
- SECTION("adjacent") {
- auto changes = observe({o}, [&] {
- tr.swap(5, 4);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Set, {4, 5}}));
- }
- SECTION("non-adjacent") {
- auto changes = observe({o}, [&] {
- tr.swap(5, 3);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Set, {3, 5}}));
- }
- }
-
- SECTION("int array: clear()") {
- auto changes = observe({o}, [&] {
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: clear() after add()") {
- auto changes = observe({o}, [&] {
- tr.add(0);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: clear() after set()") {
- auto changes = observe({o}, [&] {
- tr.set(5, 3);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: clear() after remove()") {
- auto changes = observe({o}, [&] {
- tr.remove(2);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Remove, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: multiple change kinds") {
- auto changes = observe({o}, [&] {
- tr.add(0);
- tr.remove(0);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::SetAll, {}}));
- }
-
- SECTION("int array: modifying different array does not produce changes") {
- auto changes = observe({o}, [&] {
- tr2.add(0);
- });
- REQUIRE_FALSE(changes.modified(0, target_cols[3]));
- }
-
- SECTION("int array: deleting the containing row after making changes discards the changes") {
- auto changes = observe({o}, [&] {
- tr.insert(4, 0);
- tr.insert(2, 0);
- tr.insert(8, 0);
- o.remove();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::None, {}}));
- }
-
- SECTION("int array: rollback clear()") {
- auto changes = observe_rollback({o}, [&] {
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: rollback clear() after add()") {
- auto changes = observe_rollback({o}, [&] {
- tr.add(0);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: rollback clear() after set()") {
- auto changes = observe_rollback({o}, [&] {
- tr.set(5, 3);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: rollback clear() after remove()") {
- auto changes = observe_rollback({o}, [&] {
- tr.remove(2);
- tr.clear();
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::Insert, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}));
- }
-
- SECTION("int array: rollback add() after clear()") {
- auto changes = observe_rollback({o}, [&] {
- tr.clear();
- tr.add(0);
- });
- REQUIRE(changes.array_change(0, tr_col) == (ArrayChange{Kind::SetAll, {}}));
- }
- }
-}
-
-TEST_CASE("DeepChangeChecker") {
- InMemoryTestFile config;
- config.automatic_change_notifications = false;
- auto r = Realm::get_shared_realm(config);
- r->update_schema({
- {"table", {
- {"int", PropertyType::Int},
- {"link1", PropertyType::Object|PropertyType::Nullable, "table"},
- {"link2", PropertyType::Object|PropertyType::Nullable, "table"},
- {"array", PropertyType::Array|PropertyType::Object, "table"}
- }},
- });
- auto table = r->read_group().get_table("class_table");
-
- std::vector<Obj> objects;
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- objects.push_back(table->create_object().set_all(i));
- r->commit_transaction();
-
- auto track_changes = [&](auto&& f) {
- auto history = make_in_realm_history(config.path);
- auto db = DB::create(*history, config.options());
- auto rt = db->start_read();
-
- r->begin_transaction();
- f();
- r->commit_transaction();
-
- _impl::TransactionChangeInfo info{};
- for (auto key : rt->get_table_keys())
- info.tables[key.value];
- _impl::transaction::advance(*rt, info);
- return info;
- };
-
- std::vector<_impl::DeepChangeChecker::RelatedTable> tables;
- _impl::DeepChangeChecker::find_related_tables(tables, *table);
-
- auto cols = table->get_column_keys();
- SECTION("direct changes are tracked") {
- auto info = track_changes([&] {
- table->get_object(9).set(cols[0], 10);
- });
-
- _impl::DeepChangeChecker checker(info, *table, tables);
- REQUIRE_FALSE(checker(8));
- REQUIRE(checker(9));
- }
-
- SECTION("changes over links are tracked") {
- bool did_run_section = false;
- SECTION("first link set") {
- did_run_section = true;
- r->begin_transaction();
- objects[0].set(cols[1], objects[1].get_key());
- objects[1].set(cols[1], objects[2].get_key());
- objects[2].set(cols[1], objects[4].get_key());
- r->commit_transaction();
- }
- SECTION("second link set") {
- did_run_section = true;
- r->begin_transaction();
- objects[0].set(cols[2], objects[1].get_key());
- objects[1].set(cols[2], objects[2].get_key());
- objects[2].set(cols[2], objects[4].get_key());
- r->commit_transaction();
- }
- SECTION("both set") {
- did_run_section = true;
- r->begin_transaction();
- objects[0].set(cols[1], objects[1].get_key());
- objects[1].set(cols[1], objects[2].get_key());
- objects[2].set(cols[1], objects[4].get_key());
-
- objects[0].set(cols[2], objects[1].get_key());
- objects[1].set(cols[2], objects[2].get_key());
- objects[2].set(cols[2], objects[4].get_key());
- r->commit_transaction();
- }
- SECTION("circular link") {
- did_run_section = true;
- r->begin_transaction();
- objects[0].set(cols[1], objects[0].get_key());
- objects[1].set(cols[1], objects[1].get_key());
- objects[2].set(cols[1], objects[2].get_key());
- objects[3].set(cols[1], objects[3].get_key());
- objects[4].set(cols[1], objects[4].get_key());
-
- objects[0].set(cols[2], objects[1].get_key());
- objects[1].set(cols[2], objects[2].get_key());
- objects[2].set(cols[2], objects[4].get_key());
- r->commit_transaction();
- }
-
- catch2_ensure_section_run_workaround(did_run_section, "changes over links are tracked", [&]() {
- auto info = track_changes([&] {
- objects[4].set(cols[0], 10);
- });
-
- // link chain should cascade to all but #3 being marked as modified
- REQUIRE(_impl::DeepChangeChecker(info, *table, tables)(0));
- REQUIRE(_impl::DeepChangeChecker(info, *table, tables)(1));
- REQUIRE(_impl::DeepChangeChecker(info, *table, tables)(2));
- REQUIRE_FALSE(_impl::DeepChangeChecker(info, *table, tables)(3));
-
- });
- }
-
- SECTION("changes over linklists are tracked") {
- r->begin_transaction();
- for (int i = 0; i < 3; ++i) {
- objects[i].get_linklist(cols[3]).add(objects[i].get_key());
- objects[i].get_linklist(cols[3]).add(objects[i].get_key());
- objects[i].get_linklist(cols[3]).add(objects[i + 1 + (i == 2)].get_key());
- }
- r->commit_transaction();
-
- auto info = track_changes([&] {
- objects[4].set(cols[0], 10);
- });
-
- REQUIRE(_impl::DeepChangeChecker(info, *table, tables)(0));
- REQUIRE_FALSE(_impl::DeepChangeChecker(info, *table, tables)(3));
- }
-
- SECTION("cycles over links do not loop forever") {
- r->begin_transaction();
- objects[0].set(cols[1], objects[0].get_key());
- r->commit_transaction();
-
- auto info = track_changes([&] {
- objects[9].set(cols[0], 10);
- });
- REQUIRE_FALSE(_impl::DeepChangeChecker(info, *table, tables)(0));
- }
-
- SECTION("cycles over linklists do not loop forever") {
- r->begin_transaction();
- objects[0].get_linklist(cols[3]).add(objects[0].get_key());
- r->commit_transaction();
-
- auto info = track_changes([&] {
- objects[9].set(cols[0], 10);
- });
- REQUIRE_FALSE(_impl::DeepChangeChecker(info, *table, tables)(0));
- }
-
- SECTION("link chains are tracked up to 4 levels deep") {
- r->begin_transaction();
- for (int i = 0; i < 10; ++i)
- objects.push_back(table->create_object());
- for (int i = 0; i < 19; ++i)
- objects[i].set(cols[1], objects[i + 1].get_key());
- r->commit_transaction();
-
- auto info = track_changes([&] {
- objects[19].set(cols[0], -1);
- });
-
- _impl::DeepChangeChecker checker(info, *table, tables);
- CHECK(checker(19));
- CHECK(checker(18));
- CHECK(checker(16));
- CHECK_FALSE(checker(15));
-
- // Check in other orders to make sure that the caching doesn't effect
- // the results
- _impl::DeepChangeChecker checker2(info, *table, tables);
- CHECK_FALSE(checker2(15));
- CHECK(checker2(16));
- CHECK(checker2(18));
- CHECK(checker2(19));
-
- _impl::DeepChangeChecker checker3(info, *table, tables);
- CHECK(checker3(16));
- CHECK_FALSE(checker3(15));
- CHECK(checker3(18));
- CHECK(checker3(19));
- }
-
- SECTION("changes made in the 3rd elements in the link list") {
- r->begin_transaction();
- objects[0].get_linklist(cols[3]).add(objects[1].get_key());
- objects[0].get_linklist(cols[3]).add(objects[2].get_key());
- objects[0].get_linklist(cols[3]).add(objects[3].get_key());
- objects[1].set(cols[1], objects[0].get_key());
- objects[2].set(cols[1], objects[0].get_key());
- objects[3].set(cols[1], objects[0].get_key());
- r->commit_transaction();
-
- auto info = track_changes([&] {
- objects[3].set(cols[0], 42);
- });
- _impl::DeepChangeChecker checker(info, *table, tables);
- REQUIRE(checker(1));
- REQUIRE(checker(2));
- REQUIRE(checker(3));
- }
-
- SECTION("changes made to lists mark the containing row as modified") {
- auto info = track_changes([&] {
- objects[0].get_linklist(cols[3]).add(objects[1].get_key());
- });
- _impl::DeepChangeChecker checker(info, *table, tables);
- REQUIRE(checker(0));
- }
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.cpp
deleted file mode 100644
index 9bb8bc251..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*************************************************************************
- *
- * 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/event_loop.hpp>
-
-#include <realm/util/features.h>
-
-#include <mutex>
-#include <stdexcept>
-#include <vector>
-
-#if (REALM_HAVE_UV && !REALM_PLATFORM_APPLE) || REALM_PLATFORM_NODE
-#define REALM_USE_UV 1
-#else
-#define REALM_USE_UV 0
-#endif
-
-#if REALM_USE_UV
-#include <uv.h>
-#elif REALM_PLATFORM_APPLE
-#include <realm/util/cf_ptr.hpp>
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-using namespace realm::util;
-
-struct EventLoop::Impl {
- // Returns the main event loop.
- static std::unique_ptr<Impl> main();
-
- // Run the event loop until the given return predicate returns true
- void run_until(std::function<bool()> predicate);
-
- // Schedule execution of the given function on the event loop.
- void perform(std::function<void()>);
-
- ~Impl();
-
-private:
-#if REALM_USE_UV
- Impl(uv_loop_t* loop);
-
- std::vector<std::function<void()>> m_pending_work;
- std::mutex m_mutex;
- uv_loop_t* m_loop;
- uv_async_t m_perform_work;
-#elif REALM_PLATFORM_APPLE
- Impl(util::CFPtr<CFRunLoopRef> loop) : m_loop(std::move(loop)) { }
-
- util::CFPtr<CFRunLoopRef> m_loop;
-#endif
-};
-
-EventLoop& EventLoop::main()
-{
- static EventLoop main(Impl::main());
- return main;
-}
-
-EventLoop::EventLoop(std::unique_ptr<Impl> impl) : m_impl(std::move(impl))
-{
-}
-
-EventLoop::~EventLoop() = default;
-
-void EventLoop::run_until(std::function<bool()> predicate)
-{
- return m_impl->run_until(std::move(predicate));
-}
-
-void EventLoop::perform(std::function<void()> function)
-{
- return m_impl->perform(std::move(function));
-}
-
-#if REALM_USE_UV
-
-bool EventLoop::has_implementation() { return true; }
-
-std::unique_ptr<EventLoop::Impl> EventLoop::Impl::main()
-{
- return std::unique_ptr<Impl>(new Impl(uv_default_loop()));
-}
-
-EventLoop::Impl::Impl(uv_loop_t* loop)
- : m_loop(loop)
-{
- m_perform_work.data = this;
- uv_async_init(uv_default_loop(), &m_perform_work, [](uv_async_t* handle) {
- std::vector<std::function<void()>> pending_work;
- {
- Impl& self = *static_cast<Impl*>(handle->data);
- std::lock_guard<std::mutex> lock(self.m_mutex);
- std::swap(pending_work, self.m_pending_work);
- }
-
- for (auto& f : pending_work)
- f();
- });
-}
-
-EventLoop::Impl::~Impl()
-{
- uv_close((uv_handle_t*)&m_perform_work, [](uv_handle_t*){});
- uv_loop_close(m_loop);
-}
-
-struct IdleHandler {
- uv_idle_t* idle = new uv_idle_t;
-
- IdleHandler(uv_loop_t* loop)
- {
- uv_idle_init(loop, idle);
- }
- ~IdleHandler()
- {
- uv_close(reinterpret_cast<uv_handle_t*>(idle), [](uv_handle_t* handle) {
- delete reinterpret_cast<uv_idle_t*>(handle);
- });
- }
-};
-
-void EventLoop::Impl::run_until(std::function<bool()> predicate)
-{
- if (predicate())
- return;
-
- IdleHandler observer(m_loop);
- observer.idle->data = &predicate;
-
- uv_idle_start(observer.idle, [](uv_idle_t* handle) {
- auto& predicate = *static_cast<std::function<bool()>*>(handle->data);
- if (predicate()) {
- uv_stop(handle->loop);
- }
- });
-
- uv_run(m_loop, UV_RUN_DEFAULT);
- uv_idle_stop(observer.idle);
-}
-
-void EventLoop::Impl::perform(std::function<void()> f)
-{
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_pending_work.push_back(std::move(f));
- }
- uv_async_send(&m_perform_work);
-}
-
-#elif REALM_PLATFORM_APPLE
-
-bool EventLoop::has_implementation() { return true; }
-
-std::unique_ptr<EventLoop::Impl> EventLoop::Impl::main()
-{
- return std::unique_ptr<Impl>(new Impl(retainCF(CFRunLoopGetMain())));
-}
-
-EventLoop::Impl::~Impl() = default;
-
-void EventLoop::Impl::run_until(std::function<bool()> predicate)
-{
- REALM_ASSERT(m_loop.get() == CFRunLoopGetCurrent());
-
- auto callback = [](CFRunLoopObserverRef, CFRunLoopActivity, void* info) {
- if ((*static_cast<std::function<bool()>*>(info))()) {
- CFRunLoopStop(CFRunLoopGetCurrent());
- }
- };
- CFRunLoopObserverContext ctx{};
- ctx.info = &predicate;
- auto observer = adoptCF(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities,
- true, 0, callback, &ctx));
- auto timer = adoptCF(CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0.0005, 0, 0,
- ^(CFRunLoopTimerRef){
- // Do nothing. The timer firing is sufficient to cause our runloop observer to run.
- }));
- CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer.get(), kCFRunLoopCommonModes);
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer.get(), kCFRunLoopCommonModes);
- CFRunLoopRun();
- CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer.get(), kCFRunLoopCommonModes);
- CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer.get(), kCFRunLoopCommonModes);
-}
-
-void EventLoop::Impl::perform(std::function<void()> f)
-{
- CFRunLoopPerformBlock(m_loop.get(), kCFRunLoopDefaultMode, ^{ f(); });
- CFRunLoopWakeUp(m_loop.get());
-}
-
-#else
-
-bool EventLoop::has_implementation() { return false; }
-std::unique_ptr<EventLoop::Impl> EventLoop::Impl::main() { return nullptr; }
-EventLoop::Impl::~Impl() = default;
-void EventLoop::Impl::run_until(std::function<bool()>) { printf("WARNING: there is no event loop implementation and nothing is happening.\n"); }
-void EventLoop::Impl::perform(std::function<void()>) { printf("WARNING: there is no event loop implementation and nothing is happening.\n"); }
-
-#endif
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.hpp
deleted file mode 100644
index 0b0f6a58c..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/event_loop.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*************************************************************************
- *
- * 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_OS_TESTS_UTIL_EVENT_LOOP_HPP
-#define REALM_OS_TESTS_UTIL_EVENT_LOOP_HPP
-
-#include <functional>
-#include <memory>
-
-namespace realm {
-namespace util {
-
-struct EventLoop {
- // Returns if the current platform has an event loop implementation
- static bool has_implementation();
-
- // Returns the main event loop.
- static EventLoop& main();
-
- // Run the event loop until the given return predicate returns true
- void run_until(std::function<bool()> predicate);
-
- // Schedule execution of the given function on the event loop.
- void perform(std::function<void()>);
-
- EventLoop(EventLoop&&) = default;
- EventLoop& operator=(EventLoop&&) = default;
- ~EventLoop();
-
-private:
- struct Impl;
-
- EventLoop(std::unique_ptr<Impl>);
-
- std::unique_ptr<Impl> m_impl;
-};
-
-} // namespace util
-} // namespace realm
-
-#endif // REALM_OS_TESTS_UTIL_EVENT_LOOP_HPP
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp
deleted file mode 100644
index 3e4e208e6..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-////////////////////////////////////////////////////////////////////////////
-
-#define REQUIRE_INDICES(index_set, ...) do { \
- index_set.verify(); \
- std::initializer_list<size_t> expected = {__VA_ARGS__}; \
- auto actual = index_set.as_indexes(); \
- INFO("Checking " #index_set); \
- REQUIRE(expected.size() == static_cast<size_t>(std::distance(actual.begin(), actual.end()))); \
- auto begin = actual.begin(); \
- for (auto index : expected) { \
- REQUIRE(*begin++ == index); \
- } \
-} while (0)
-
-#define REQUIRE_COLUMN_INDICES(columns, col, ...) do { \
- auto it = (columns).find(col); \
- REQUIRE(it != (columns).end()); \
- REQUIRE_INDICES(it->second, __VA_ARGS__); \
-} while (0)
-
-#define REQUIRE_MOVES(c, ...) do { \
- auto actual = (c); \
- std::initializer_list<CollectionChangeSet::Move> expected = {__VA_ARGS__}; \
- REQUIRE(expected.size() == actual.moves.size()); \
- auto begin = actual.moves.begin(); \
- for (auto move : expected) { \
- CHECK(begin->from == move.from); \
- CHECK(begin->to == move.to); \
- ++begin; \
- } \
-} while (0)
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp
deleted file mode 100644
index e09cabf51..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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/test_file.hpp"
-
-#include "test_utils.hpp"
-
-#include "impl/realm_coordinator.hpp"
-
-#if REALM_ENABLE_SYNC
-#include "sync/sync_config.hpp"
-#include "sync/sync_manager.hpp"
-#include "sync/sync_session.hpp"
-#include "schema.hpp"
-#endif
-
-#include <realm/db.hpp>
-#include <realm/disable_sync_to_disk.hpp>
-#include <realm/history.hpp>
-#include <realm/string_data.hpp>
-#include <realm/util/base64.hpp>
-
-#include <cstdlib>
-
-#ifdef _WIN32
-#include <io.h>
-#include <fcntl.h>
-
-inline static int mkstemp(char* _template) { return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE); }
-#else
-#include <unistd.h>
-#endif
-
-#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
-#include <condition_variable>
-#include <functional>
-#include <thread>
-#include <map>
-#endif
-
-using namespace realm;
-
-TestFile::TestFile()
-{
- static std::string tmpdir = [] {
- disable_sync_to_disk();
-
- const char* dir = getenv("TMPDIR");
- if (dir && *dir)
- return dir;
-#if REALM_ANDROID
- return "/data/local/tmp";
-#else
- return "/tmp";
-#endif
- }();
- path = tmpdir + "/realm.XXXXXX";
- int fd = mkstemp(&path[0]);
- if (fd < 0) {
- int err = errno;
- throw std::system_error(err, std::system_category());
- }
- close(fd);
- unlink(path.c_str());
-
- schema_version = 0;
-}
-
-TestFile::~TestFile()
-{
- if (!m_persist)
- unlink(path.c_str());
-}
-
-DBOptions TestFile::options() const
-{
- DBOptions options;
- options.durability = in_memory
- ? DBOptions::Durability::MemOnly
- : DBOptions::Durability::Full;
- return options;
-}
-
-InMemoryTestFile::InMemoryTestFile()
-{
- in_memory = true;
-}
-
-#if REALM_ENABLE_SYNC
-SyncTestFile::SyncTestFile(SyncServer& server, std::string name, bool is_partial, std::string user_name)
-{
- if (name.empty())
- name = path.substr(path.rfind('/') + 1);
- auto url = server.url_for_realm(name);
-
- sync_config = std::make_shared<SyncConfig>(SyncManager::shared().get_user({user_name, url}, "not_a_real_token"), url);
- sync_config->user->set_is_admin(true);
- sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
- sync_config->bind_session_handler = [=](auto&, auto& config, auto session) {
- std::string token, encoded;
- // FIXME: Tokens without a path are currently implicitly considered
- // admin tokens by the sync service, so until that changes we need to
- // add a path for non-admin users
- if (config.user->is_admin())
- token = util::format("{\"identity\": \"%1\", \"access\": [\"download\", \"upload\"]}", user_name);
- else {
- std::string suffix;
- if (config.is_partial)
- suffix = util::format("/__partial/%1/%2", config.user->identity(), SyncConfig::partial_sync_identifier(*config.user));
- token = util::format("{\"identity\": \"%1\", \"path\": \"/%2%3\", \"access\": [\"download\", \"upload\"]}",
- user_name, name, suffix);
- }
- encoded.resize(base64_encoded_size(token.size()));
- base64_encode(token.c_str(), token.size(), &encoded[0], encoded.size());
- session->refresh_access_token(encoded, config.realm_url());
- };
- sync_config->error_handler = [](auto, auto) { abort(); };
- sync_config->is_partial = is_partial;
- schema_mode = SchemaMode::Additive;
-}
-
-SyncServer::SyncServer(StartImmediately start_immediately, std::string local_dir)
-: m_local_root_dir(local_dir.empty() ? util::make_temp_dir() : local_dir)
-, m_server(m_local_root_dir, util::none, ([&] {
- using namespace std::literals::chrono_literals;
-
- sync::Server::Config config;
-#if TEST_ENABLE_SYNC_LOGGING
- auto logger = new util::StderrLogger;
- logger->set_level_threshold(util::Logger::Level::all);
- config.logger = logger;
-#else
- config.logger = new TestLogger;
-#endif
- m_logger.reset(config.logger);
- config.history_compaction_clock = this;
- config.disable_history_compaction = false;
- config.history_ttl = 1s;
- config.history_compaction_interval = 1s;
- config.state_realm_dir = util::make_temp_dir();
- config.listen_address = "127.0.0.1";
-
- return config;
-})())
-{
-#if TEST_ENABLE_SYNC_LOGGING
- SyncManager::shared().set_log_level(util::Logger::Level::all);
-#else
- SyncManager::shared().set_log_level(util::Logger::Level::off);
-#endif
-
- m_server.start();
- m_url = util::format("realm://127.0.0.1:%1", m_server.listen_endpoint().port());
- if (start_immediately)
- start();
-}
-
-SyncServer::~SyncServer()
-{
- stop();
- SyncManager::shared().reset_for_testing();
-}
-
-void SyncServer::start()
-{
- REALM_ASSERT(!m_thread.joinable());
- m_thread = std::thread([this]{ m_server.run(); });
-}
-
-void SyncServer::stop()
-{
- m_server.stop();
- if (m_thread.joinable())
- m_thread.join();
-}
-
-std::string SyncServer::url_for_realm(StringData realm_name) const
-{
- return util::format("%1/%2", m_url, realm_name);
-}
-
-static void wait_for_session(Realm& realm, void (SyncSession::*fn)(std::function<void(std::error_code)>))
-{
- std::condition_variable cv;
- std::mutex wait_mutex;
- bool wait_flag(false);
- auto& session = *SyncManager::shared().get_session(realm.config().path, *realm.config().sync_config);
- (session.*fn)([&](auto) {
- std::unique_lock<std::mutex> lock(wait_mutex);
- wait_flag = true;
- cv.notify_one();
- });
- std::unique_lock<std::mutex> lock(wait_mutex);
- cv.wait(lock, [&]() { return wait_flag == true; });
-}
-
-void wait_for_upload(Realm& realm)
-{
- wait_for_session(realm, &SyncSession::wait_for_upload_completion);
-}
-
-void wait_for_download(Realm& realm)
-{
- wait_for_session(realm, &SyncSession::wait_for_download_completion);
-}
-
-TestSyncManager::TestSyncManager(std::string const& base_path, SyncManager::MetadataMode mode)
-{
- configure(base_path, mode);
-}
-
-TestSyncManager::~TestSyncManager()
-{
- SyncManager::shared().reset_for_testing();
-}
-
-void TestSyncManager::configure(std::string const& base_path, SyncManager::MetadataMode mode)
-{
- SyncClientConfig config;
- config.base_file_path = base_path.empty() ? tmp_dir() : base_path;
- config.metadata_mode = mode;
-#if TEST_ENABLE_SYNC_LOGGING
- config.log_level = util::Logger::Level::all;
-#else
- config.log_level = util::Logger::Level::off;
-#endif
- SyncManager::shared().configure(config);
-}
-
-#endif // REALM_ENABLE_SYNC
-
-#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
-// A helper which synchronously runs on_change() on a fixed background thread
-// so that ThreadSanitizer can potentially detect issues
-// This deliberately uses an unsafe spinlock for synchronization to ensure that
-// the code being tested has to supply all required safety
-static class TsanNotifyWorker {
-public:
- TsanNotifyWorker()
- {
- m_thread = std::thread([&] { work(); });
- }
-
- void work()
- {
- while (true) {
- auto value = m_signal.load(std::memory_order_relaxed);
- if (value == 0 || value == 1)
- continue;
- 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<_impl::RealmCoordinator *>(value);
- c->on_change();
- m_signal.store(1, std::memory_order_relaxed);
- }
- }
-
- ~TsanNotifyWorker()
- {
- m_signal = 2;
- m_thread.join();
- }
-
- void on_change(const std::shared_ptr<_impl::RealmCoordinator>& c)
- {
- 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<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
-} s_worker;
-
-void on_change_but_no_notify(Realm& realm)
-{
- s_worker.on_change(_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
-}
-
-void advance_and_notify(Realm& realm)
-{
- on_change_but_no_notify(realm);
- realm.notify();
-}
-
-#else // REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
-
-void on_change_but_no_notify(Realm& realm)
-{
- _impl::RealmCoordinator::get_coordinator(realm.config().path)->on_change();
-}
-
-void advance_and_notify(Realm& realm)
-{
- on_change_but_no_notify(realm);
- realm.notify();
-}
-#endif
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp
deleted file mode 100644
index 8aa6fa3b7..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp
+++ /dev/null
@@ -1,155 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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_TEST_UTIL_TEST_FILE_HPP
-#define REALM_TEST_UTIL_TEST_FILE_HPP
-
-#include "shared_realm.hpp"
-#include "util/tagged_bool.hpp"
-
-#include <realm/util/logger.hpp>
-#include <realm/util/optional.hpp>
-
-#include <thread>
-
-#if REALM_ENABLE_SYNC
-#include "sync/sync_config.hpp"
-
-#include <realm/sync/client.hpp>
-#include <realm/sync/server.hpp>
-
-// {"identity":"test", "access": ["download", "upload"]}
-static const std::string s_test_token = "eyJpZGVudGl0eSI6InRlc3QiLCAiYWNjZXNzIjogWyJkb3dubG9hZCIsICJ1cGxvYWQiXX0=";
-#endif // REALM_ENABLE_SYNC
-
-namespace realm {
-class Schema;
-enum class SyncSessionStopPolicy;
-struct DBOptions;
-struct SyncConfig;
-}
-
-class JoiningThread {
-public:
- template<typename... Args>
- JoiningThread(Args&&... args) : m_thread(std::forward<Args>(args)...) { }
- ~JoiningThread() { if (m_thread.joinable()) m_thread.join(); }
- void join() { m_thread.join(); }
-
-private:
- std::thread m_thread;
-};
-
-
-struct TestFile : realm::Realm::Config {
- TestFile();
- ~TestFile();
-
- // The file should outlive the object, ie. should not be deleted in destructor
- void persist()
- {
- m_persist = true;
- }
-
- realm::DBOptions options() const;
-
-private:
- bool m_persist = false;
-};
-
-struct InMemoryTestFile : TestFile {
- InMemoryTestFile();
-};
-
-void advance_and_notify(realm::Realm& realm);
-void on_change_but_no_notify(realm::Realm& realm);
-
-#if REALM_ENABLE_SYNC
-
-#define TEST_ENABLE_SYNC_LOGGING 0 // change to 1 to enable logging
-
-struct TestLogger : realm::util::Logger::LevelThreshold, realm::util::Logger {
- void do_log(realm::util::Logger::Level, std::string) override {}
- Level get() const noexcept override { return Level::off; }
- TestLogger() : Logger::LevelThreshold(), Logger(static_cast<Logger::LevelThreshold&>(*this)) { }
-};
-
-using StartImmediately = realm::util::TaggedBool<class StartImmediatelyTag>;
-
-class SyncServer : private realm::sync::Clock {
-public:
- SyncServer(StartImmediately start_immediately=true, std::string local_dir = "");
- ~SyncServer();
-
- void start();
- void stop();
-
- std::string url_for_realm(realm::StringData realm_name) const;
- std::string base_url() const { return m_url; }
- std::string local_root_dir() const { return m_local_root_dir; }
-
- template <class R, class P>
- void advance_clock(std::chrono::duration<R, P> duration = std::chrono::seconds(1)) noexcept
- {
- m_now += std::chrono::duration_cast<time_point::duration>(duration).count();
- }
-
-private:
- std::string m_local_root_dir;
- std::unique_ptr<realm::util::Logger> m_logger;
- realm::sync::Server m_server;
- std::thread m_thread;
- std::string m_url;
- std::atomic<time_point::rep> m_now{0};
-
- time_point now() const noexcept override
- {
- return time_point{time_point::duration{m_now}};
- }
-};
-
-struct SyncTestFile : TestFile {
- template<typename BindHandler, typename ErrorHandler>
- SyncTestFile(const realm::SyncConfig& sync_config,
- realm::SyncSessionStopPolicy stop_policy,
- BindHandler&& bind_handler,
- ErrorHandler&& error_handler)
- {
- this->sync_config = std::make_shared<realm::SyncConfig>(sync_config);
- this->sync_config->stop_policy = stop_policy;
- this->sync_config->bind_session_handler = std::forward<BindHandler>(bind_handler);
- this->sync_config->error_handler = std::forward<ErrorHandler>(error_handler);
- schema_mode = realm::SchemaMode::Additive;
- }
-
- SyncTestFile(SyncServer& server, std::string name="", bool is_partial=false,
- std::string user_name="test");
-};
-
-struct TestSyncManager {
- TestSyncManager(std::string const& base_path="", realm::SyncManager::MetadataMode = realm::SyncManager::MetadataMode::NoMetadata);
- ~TestSyncManager();
- static void configure(std::string const& base_path, realm::SyncManager::MetadataMode);
-};
-
-void wait_for_upload(realm::Realm& realm);
-void wait_for_download(realm::Realm& realm);
-
-#endif // REALM_ENABLE_SYNC
-
-#endif
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/uuid.cpp b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/uuid.cpp
deleted file mode 100644
index 97e21f0dd..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/uuid.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2017 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 "catch2/catch.hpp"
-
-#include "util/uuid.hpp"
-
-#include <algorithm>
-#include <cctype>
-
-using namespace realm;
-
-TEST_CASE("uuid") {
- auto isxdigit = [](char c) { return std::isxdigit(c); };
-
- auto uuid = util::uuid_string();
- INFO("uuid: " << uuid);
-
- CHECK(uuid.size() == 36);
-
- // Version 4.
- CHECK(uuid[14] == '4');
- // Variant 1 (IETF).
- CHECK((uuid[19] == '8' || uuid[19] == '9' || uuid[19] == 'a' || uuid[19] == 'b'));
-
- CHECK(std::all_of(&uuid[0], &uuid[8], isxdigit));
- CHECK(uuid[8] == '-');
- CHECK(std::all_of(&uuid[9], &uuid[13], isxdigit));
- CHECK(uuid[13] == '-');
- CHECK(std::all_of(&uuid[14], &uuid[18], isxdigit));
- CHECK(uuid[18] == '-');
- CHECK(std::all_of(&uuid[19], &uuid[23], isxdigit));
- CHECK(uuid[23] == '-');
- CHECK(std::all_of(&uuid[24], &uuid[36], isxdigit));
-}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh
deleted file mode 100755
index 23675ceda..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-
-# This script is used by CI to build for a specific flavor. It can be used
-# locally: `./workspace/build.sh [linux|android] [sync]`
-#
-# For Android builds, you must set the ANDROID_NDK_PATH environment variable
-# to point to your Android NDK installation.
-
-flavor=${1:-linux}
-sync=${2}
-extra_flags=${3}
-
-nprocs=4
-if [ "$(uname)" = "Linux" ]; then
- nprocs=$(grep -c ^processor /proc/cpuinfo)
-fi
-
-: ${OPENSSL_ROOT_DIR:=/usr/local}
-
-set -e
-
-rm -rf ci.build
-mkdir -p ci.build
-cd ci.build
-
-cmake_flags="-DCMAKE_CXX_FLAGS=-Werror"
-if [ "${flavor}" = "android" ]; then
- [ -z $ANDROID_NDK_PATH ] && (echo "ANDROID_NDK_PATH is not set!"; exit 1)
- cmake_flags="-DREALM_PLATFORM=Android -DANDROID_NDK=${ANDROID_NDK_PATH}"
-fi
-
-if [ "${sync}" = "sync" ]; then
- cmake_flags="${cmake_flags} -DREALM_ENABLE_SYNC=1 -DREALM_ENABLE_SERVER=1 -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}"
-fi
-
-cmake ${cmake_flags} ${extra_flags} ..
-make VERBOSE=1 -j${nprocs}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/docker_build_wrapper.sh b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/docker_build_wrapper.sh
deleted file mode 100755
index 8cf9c9bb0..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/docker_build_wrapper.sh
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/bin/bash
-
-set -e
-
-script_path="$(pushd "$(dirname "$0")" >/dev/null; pwd)"
-src_path="$(pushd "${script_path}/.." >/dev/null; pwd)"
-
-die() { echo "$@" 1>&2 ; exit 1; }
-info() { echo "===> $*"; }
-
-docker_build() {
- declare name="$1"; shift
- declare path="$1"; shift
- declare args="$*"
-
- if [ "${DOCKER_REGISTRY}" != "" ]; then
- remote_name="${DOCKER_REGISTRY}/${name}"
- fi
-
- info "Building ${name} image..."
- if [ "${DOCKER_REGISTRY}" != "" ]; then
- docker_pull "${remote_name}" && docker tag "${remote_name}" "${name}" || true
- fi
-
- old_id=$(docker images -q "${name}")
- info "Old ${name} image id: ${old_id}"
-
- if [ "${DOCKERFILE}" != "" ]; then
- docker build ${args} -t "${name}" -f "${DOCKERFILE}" "${path}" || \
- die "Building ${name} image failed"
- else
- docker build ${args} -t "${name}" "${path}" || \
- die "Building ${name} image failed"
- fi
-
- new_id=$(docker images -q "${name}")
- info "New ${name} image id: ${new_id}"
-
- if [ "${DEBUG}" ] && [ "${new_id}" != "${old_id}" ]; then
- info "History for old id $old_id:"
- if [ "${old_id}" != "" ]; then
- docker history "$old_id"
- fi
-
- info "History for new id $new_id:"
- docker history "$new_id"
- fi
-
- if [ "${DOCKER_PUSH:-0}" != "0" ] && [ "${DOCKER_REGISTRY}" != "" ]; then
- docker tag "${name}" "${remote_name}"
- docker_push "${remote_name}"
- fi
-}
-
-# Due to https://github.com/docker/docker/issues/20316, we use
-# https://github.com/tonistiigi/buildcache to generate a cache of the layer
-# metadata for later builds.
-my_buildcache() {
- if [ "$DOCKER_REGISTRY" != "" ]; then
-
- docker_path="/var/lib/docker"
-
- # Stupid hack for our AWS nodes, which have docker data on another volume
- if [ -d "/mnt/docker" ]; then
- docker_path="/mnt/docker"
- fi
-
- docker pull "${DOCKER_REGISTRY}/ci/buildcache" >/dev/null && \
- docker run --rm \
- -v /var/run/docker.sock:/var/run/docker.sock \
- -v ${docker_path}:/var/lib/docker \
- "${DOCKER_REGISTRY}/ci/buildcache" "$@"
- fi
-}
-
-docker_pull() {
- info "Attempting to pull '$1' image from registry..."
- (
- tmpdir=$(mktemp -d)
- docker pull "$1"
- (
- # In addition, pull the cached metadata and load it (buildcache)
- cd "${tmpdir}" && docker pull "$1-cache" && docker save "$1-cache" | \
- tar -xf - && docker load -i ./*/layer.tar
- )
- rm -rf "${tmpdir}"
- ) || true
-}
-
-docker_push() {
- info "Pushing '$1' image to registry..."
- docker push "$1"
- # Create a cache of the layer metdata we need and push it as an image
- #docker rmi $1-cache 2>/dev/null || true
- my_buildcache save -g /var/lib/docker "$1" | gunzip -c | \
- docker import - "$1-cache" && \
- docker push "$1-cache"
-}
-
-docker_build $@
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/local_docker_build.sh b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/local_docker_build.sh
deleted file mode 100755
index bf4b86610..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/local_docker_build.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-# This script can be used to locally check and debug
-# the linux build process outside of CI.
-# This should be run from the root directory: `./workflow/local_docker_build.sh`
-
-set -e
-
-flavor=${1:-linux}
-
-docker build -t ci/realm-object-server:build .
-
-docker run -it --rm \
- -u $(id -u) \
- -v "${HOME}:${HOME}" \
- -e HOME="${HOME}" \
- -v /etc/passwd:/etc/passwd:ro \
- -v $(pwd):/source \
- -w /source \
- ci/realm-object-server:build \
- ./workflow/test_coverage.sh ${flavor}
diff --git a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh b/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh
deleted file mode 100755
index 731053ba9..000000000
--- a/Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-
-# This script is used by CI to test coverage for a specific flavor. It can be
-# used locally: `./workspace/test_coverage.sh [sync] [dependency-suffix]`
-
-sync=${1}
-deps_suffix="${2}"
-
-: ${OPENSSL_ROOT_DIR:=/usr/local}
-
-set -e
-
-if [ "${TMPDIR}" = "" ]; then
- TMPDIR="/tmp"
-fi
-
-echo "TMPDIR: ${TMPDIR}"
-find $TMPDIR -name 'realm*' -exec rm -rf "{}" \+ || true
-rm -rf coverage.build
-mkdir -p coverage.build
-cd coverage.build
-
-cmake_flags="-DCMAKE_CXX_FLAGS=-Werror"
-if [ "${sync}" = "sync" ]; then
- cmake_flags="${cmake_flags} -DREALM_ENABLE_SYNC=1 -DREALM_ENABLE_SERVER=1 -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}"
-fi
-
-cmake ${cmake_flags} -G Ninja -DCMAKE_BUILD_TYPE=Coverage -DDEPENDENCIES_FILE="dependencies${deps_suffix}.list" ..
-ninja -v generate-coverage-cobertura
-
-find $TMPDIR -name 'realm*' -exec rm -rf "{}" \+ || true