marinofaggiana 4 years ago
parent
commit
49621ce55f
100 changed files with 6303 additions and 5877 deletions
  1. 227 0
      Carthage/Checkouts/realm-cocoa/.jenkins.yml
  2. 374 0
      Carthage/Checkouts/realm-cocoa/CHANGELOG.md
  3. 2 1
      Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig
  4. 1 1
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig
  5. 26 18
      Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability
  6. 2 1
      Carthage/Checkouts/realm-cocoa/Realm.podspec
  7. 15 48
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm
  8. 0 35
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift
  9. 4 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake
  10. 71 87
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake
  11. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt
  12. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile
  13. 6 8
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile
  14. 3 3
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
  15. 10 14
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt
  16. 8 13
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp
  17. 2 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp
  18. 3 3
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp
  19. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp
  20. 112 326
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp
  21. 10 21
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp
  22. 105 102
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp
  23. 53 54
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp
  24. 3 3
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp
  25. 2 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp
  26. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp
  27. 42 47
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp
  28. 11 12
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp
  29. 20 20
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp
  30. 18 51
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp
  31. 5 13
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp
  32. 340 221
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp
  33. 82 58
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp
  34. 246 144
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp
  35. 58 23
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp
  36. 148 459
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp
  37. 7 10
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp
  38. 16 18
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp
  39. 6 19
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp
  40. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp
  41. 206 167
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp
  42. 57 29
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp
  43. 48 22
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp
  44. 29 14
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp
  45. 75 91
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp
  46. 52 28
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp
  47. 20 13
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp
  48. 116 234
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp
  49. 7 15
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp
  50. 17 16
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp
  51. 456 211
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp
  52. 120 90
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp
  53. 16 33
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp
  54. 11 11
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp
  55. 249 364
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp
  56. 80 84
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp
  57. 208 78
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp
  58. 21 96
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp
  59. 7 7
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp
  60. 7 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt
  61. 26 298
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp
  62. 151 183
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp
  63. 4 4
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp
  64. 120 141
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp
  65. 0 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp
  66. 133 123
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp
  67. 3 14
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp
  68. 102 82
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp
  69. 129 187
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp
  70. 287 202
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp
  71. 19 39
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp
  72. 429 252
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp
  73. 195 429
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp
  74. 3 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp
  75. 32 24
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp
  76. 21 13
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp
  77. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh
  78. 4 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh
  79. 4 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.hpp
  80. 83 68
      Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.mm
  81. 81 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMArray.h
  82. 14 2
      Carthage/Checkouts/realm-cocoa/Realm/RLMArray.mm
  83. 0 1
      Carthage/Checkouts/realm-cocoa/Realm/RLMArray_Private.hpp
  84. 10 10
      Carthage/Checkouts/realm-cocoa/Realm/RLMClassInfo.hpp
  85. 17 18
      Carthage/Checkouts/realm-cocoa/Realm/RLMClassInfo.mm
  86. 27 0
      Carthage/Checkouts/realm-cocoa/Realm/RLMCollection.h
  87. 90 49
      Carthage/Checkouts/realm-cocoa/Realm/RLMCollection.mm
  88. 7 9
      Carthage/Checkouts/realm-cocoa/Realm/RLMCollection_Private.hpp
  89. 2 9
      Carthage/Checkouts/realm-cocoa/Realm/RLMConstants.h
  90. 4 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMListBase.h
  91. 27 15
      Carthage/Checkouts/realm-cocoa/Realm/RLMListBase.mm
  92. 46 20
      Carthage/Checkouts/realm-cocoa/Realm/RLMManagedArray.mm
  93. 20 42
      Carthage/Checkouts/realm-cocoa/Realm/RLMMigration.mm
  94. 65 3
      Carthage/Checkouts/realm-cocoa/Realm/RLMObject.h
  95. 14 140
      Carthage/Checkouts/realm-cocoa/Realm/RLMObject.mm
  96. 257 15
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectBase.mm
  97. 0 6
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectSchema.mm
  98. 3 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.h
  99. 15 10
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.mm
  100. 13 2
      Carthage/Checkouts/realm-cocoa/Realm/RLMObject_Private.h

+ 227 - 0
Carthage/Checkouts/realm-cocoa/.jenkins.yml

@@ -9,6 +9,7 @@ xcode_version:
  - 11.2.1
  - 11.2.1
  - 11.3
  - 11.3
  - 11.4.1
  - 11.4.1
+ - 11.5
 target: 
 target: 
  - docs
  - docs
  - swiftlint
  - swiftlint
@@ -24,6 +25,7 @@ target:
  - tvos-swift
  - tvos-swift
  - catalyst
  - catalyst
  - catalyst-swift
  - catalyst-swift
+ - xcframework
  - cocoapods-osx
  - cocoapods-osx
  - cocoapods-ios
  - cocoapods-ios
  - cocoapods-ios-dynamic
  - cocoapods-ios-dynamic
@@ -31,6 +33,7 @@ target:
  - swiftpm
  - swiftpm
  - swiftpm-address
  - swiftpm-address
  - swiftpm-thread
  - swiftpm-thread
+ - swiftpm-ios
 configuration: 
 configuration: 
  - Debug
  - Debug
  - Release
  - Release
@@ -73,6 +76,14 @@ exclude:
     target: docs
     target: docs
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: docs
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: docs
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: swiftlint
     target: swiftlint
     configuration: Debug
     configuration: Debug
@@ -109,6 +120,14 @@ exclude:
     target: swiftlint
     target: swiftlint
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: swiftlint
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: swiftlint
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: osx-encryption
     target: osx-encryption
     configuration: Debug
     configuration: Debug
@@ -141,6 +160,14 @@ exclude:
     target: osx-encryption
     target: osx-encryption
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: osx-encryption
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: osx-encryption
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: osx-object-server
     target: osx-object-server
     configuration: Debug
     configuration: Debug
@@ -173,6 +200,14 @@ exclude:
     target: osx-object-server
     target: osx-object-server
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: osx-object-server
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: osx-object-server
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: ios-static
     target: ios-static
     configuration: Debug
     configuration: Debug
@@ -205,6 +240,14 @@ exclude:
     target: ios-static
     target: ios-static
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: ios-static
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: ios-static
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: ios-dynamic
     target: ios-dynamic
     configuration: Debug
     configuration: Debug
@@ -237,6 +280,14 @@ exclude:
     target: ios-dynamic
     target: ios-dynamic
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: ios-dynamic
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: watchos
     target: watchos
     configuration: Debug
     configuration: Debug
@@ -269,6 +320,14 @@ exclude:
     target: watchos
     target: watchos
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: watchos
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: watchos
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: tvos
     target: tvos
     configuration: Debug
     configuration: Debug
@@ -301,6 +360,14 @@ exclude:
     target: tvos
     target: tvos
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: tvos
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: tvos
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: ios-swift
     target: ios-swift
     configuration: Debug
     configuration: Debug
@@ -333,6 +400,14 @@ exclude:
     target: ios-swift
     target: ios-swift
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: ios-swift
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: ios-swift
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: tvos-swift
     target: tvos-swift
     configuration: Debug
     configuration: Debug
@@ -365,6 +440,14 @@ exclude:
     target: tvos-swift
     target: tvos-swift
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: tvos-swift
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: tvos-swift
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: catalyst
     target: catalyst
     configuration: Debug
     configuration: Debug
@@ -385,6 +468,10 @@ exclude:
     target: catalyst
     target: catalyst
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: catalyst
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: catalyst-swift
     target: catalyst-swift
     configuration: Debug
     configuration: Debug
@@ -405,6 +492,54 @@ exclude:
     target: catalyst-swift
     target: catalyst-swift
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: catalyst-swift
+    configuration: Debug
+
+  - xcode_version: 10.3
+    target: xcframework
+    configuration: Debug
+
+  - xcode_version: 10.3
+    target: xcframework
+    configuration: Release
+
+  - xcode_version: 11.1
+    target: xcframework
+    configuration: Debug
+
+  - xcode_version: 11.1
+    target: xcframework
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: xcframework
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: xcframework
+    configuration: Release
+
+  - xcode_version: 11.3
+    target: xcframework
+    configuration: Debug
+
+  - xcode_version: 11.3
+    target: xcframework
+    configuration: Release
+
+  - xcode_version: 11.4.1
+    target: xcframework
+    configuration: Debug
+
+  - xcode_version: 11.4.1
+    target: xcframework
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: xcframework
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: cocoapods-osx
     target: cocoapods-osx
     configuration: Debug
     configuration: Debug
@@ -425,6 +560,10 @@ exclude:
     target: cocoapods-osx
     target: cocoapods-osx
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.5
+    target: cocoapods-osx
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: cocoapods-ios
     target: cocoapods-ios
     configuration: Debug
     configuration: Debug
@@ -457,6 +596,14 @@ exclude:
     target: cocoapods-ios
     target: cocoapods-ios
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: cocoapods-ios
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: cocoapods-ios
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: cocoapods-ios-dynamic
     target: cocoapods-ios-dynamic
     configuration: Debug
     configuration: Debug
@@ -489,6 +636,14 @@ exclude:
     target: cocoapods-ios-dynamic
     target: cocoapods-ios-dynamic
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: cocoapods-ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: cocoapods-ios-dynamic
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: cocoapods-watchos
     target: cocoapods-watchos
     configuration: Debug
     configuration: Debug
@@ -521,6 +676,14 @@ exclude:
     target: cocoapods-watchos
     target: cocoapods-watchos
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: cocoapods-watchos
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: cocoapods-watchos
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: swiftpm
     target: swiftpm
     configuration: Debug
     configuration: Debug
@@ -541,6 +704,10 @@ exclude:
     target: swiftpm
     target: swiftpm
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: swiftpm
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: swiftpm-address
     target: swiftpm-address
     configuration: Debug
     configuration: Debug
@@ -577,6 +744,14 @@ exclude:
     target: swiftpm-address
     target: swiftpm-address
     configuration: Debug
     configuration: Debug
 
 
+  - xcode_version: 11.4.1
+    target: swiftpm-address
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: swiftpm-address
+    configuration: Debug
+
   - xcode_version: 10.3
   - xcode_version: 10.3
     target: swiftpm-thread
     target: swiftpm-thread
     configuration: Debug
     configuration: Debug
@@ -612,3 +787,55 @@ exclude:
   - xcode_version: 11.4.1
   - xcode_version: 11.4.1
     target: swiftpm-thread
     target: swiftpm-thread
     configuration: Debug
     configuration: Debug
+
+  - xcode_version: 11.4.1
+    target: swiftpm-thread
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: swiftpm-thread
+    configuration: Debug
+
+  - xcode_version: 10.3
+    target: swiftpm-ios
+    configuration: Debug
+
+  - xcode_version: 10.3
+    target: swiftpm-ios
+    configuration: Release
+
+  - xcode_version: 11.1
+    target: swiftpm-ios
+    configuration: Debug
+
+  - xcode_version: 11.1
+    target: swiftpm-ios
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: swiftpm-ios
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: swiftpm-ios
+    configuration: Release
+
+  - xcode_version: 11.3
+    target: swiftpm-ios
+    configuration: Debug
+
+  - xcode_version: 11.3
+    target: swiftpm-ios
+    configuration: Release
+
+  - xcode_version: 11.4.1
+    target: swiftpm-ios
+    configuration: Debug
+
+  - xcode_version: 11.4.1
+    target: swiftpm-ios
+    configuration: Release
+
+  - xcode_version: 11.5
+    target: swiftpm-ios
+    configuration: Debug

+ 374 - 0
Carthage/Checkouts/realm-cocoa/CHANGELOG.md

@@ -1,3 +1,226 @@
+5.0.3 Release notes (2020-06-10)
+=============================================================
+
+### Fixed
+
+* `-[RLMObject isFrozen]` always returned false. ([#6568](https://github.com/realm/realm-cocoa/issues/6568), since 5.0.0).
+* Freezing an object within the write transaction that the object was created
+  in now throws an exception rather than crashing when the object is first
+  used.
+* The schema for frozen Realms was not properly initialized, leading to crashes
+  when accessing a RLMLinkingObjects property.
+  ([#6568](https://github.com/realm/realm-cocoa/issues/6568), since 5.0.0).
+* Observing `Object.isInvalidated` via a keypath literal would produce a
+  warning in Swift 5.2 due to the property not being marked as @objc.
+  ([#6554](https://github.com/realm/realm-cocoa/issues/6554))
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Realm Studio: 3.11 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.5.
+
+5.0.2 Release notes (2020-06-02)
+=============================================================
+
+### Fixed
+
+* Fix errSecDuplicateItem (-25299) errors when opening a synchronized Realm
+  when upgrading from pre-5.0 versions of Realm.
+  ([#6538](https://github.com/realm/realm-cocoa/issues/6538), [#6494](https://github.com/realm/realm-cocoa/issues/6494), since 5.0.0).
+* Opening Realms stored on filesystems which do not support preallocation (such
+  as ExFAT) would give "Operation not supported" exceptions.
+  ([#6508](https://github.com/realm/realm-cocoa/issues/6508), since 3.2.0).
+* 'NoSuchTable' exceptions would sometimes be thrown after upgrading a Relam
+  file to the v10 format. ([Core #3701](https://github.com/realm/realm-core/issues/3701), since 5.0.0)
+* If the upgrade process was interrupted/killed for various reasons, the
+  following run could stop with some assertions failing. No instances of this
+  happening were reported to us. (Since 5.0.0).
+* Queries filtering a `List` where the query was on an indexed property over a
+  link would sometimes give incomplete results.
+  ([#6540](https://github.com/realm/realm-cocoa/issues/6540), since 4.1.0 but
+  more common since 5.0.0)
+* Opening a file in read-only mode would attempt to make a spurious write to
+  the file, causing errors if the file was in read-only storage (since 5.0.0).
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Realm Studio: 3.11 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.5.
+
+### Internal
+
+* Upgraded realm-core from v6.0.4 to v6.0.6
+* Upgraded realm-sync from v5.0.3 to v5.0.5
+
+5.0.1 Release notes (2020-05-27)
+=============================================================
+
+### Enhancements
+
+* Add prebuilt binary for Xcode 11.5 to the release package.
+
+### Fixed
+
+* Fix linker error when building a xcframework for Catalyst.
+  ([#6511](https://github.com/realm/realm-cocoa/issues/6511), since 4.3.1).
+* Fix building for iOS devices when using Swift Package Manager
+  ([#6522](https://github.com/realm/realm-cocoa/issues/6522), since 5.0.0).
+* `List` and `RealmOptional` properties on frozen objects were not initialized
+  correctly and would always report `nil` or an empty list.
+  ([#6527](https://github.com/realm/realm-cocoa/issues/6527), since 5.0.0).
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Realm Studio: 3.11 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.5.
+
+5.0.0 Release notes (2020-05-15)
+=============================================================
+
+NOTE: This version bumps the Realm file format to version 10. It is not
+possible to downgrade version 9 or earlier. Files created with older versions
+of Realm will be automatically upgraded. Only 
+[Studio 3.11](https://github.com/realm/realm-studio/releases/tag/v3.11.0) or later will be able
+to open the new file format.
+
+### Enhancements
+
+* Storing large binary blobs in Realm files no longer forces the file to be at
+  least 8x the size of the largest blob.
+* Reduce the size of transaction logs stored inside the Realm file, reducing
+  file size growth from large transactions.
+* Add support for frozen objects. `Realm`, `Results`, `List` and `Object` now
+  have `freeze()` methods which return a frozen copy of the object. These
+  objects behave similarly to creating unmanaged deep copies of the source
+  objects. They can be read from any thread and do not update when writes are
+  made to the Realm, but creating frozen objects does not actually copy data
+  out of the Realm and so can be much faster and use less memory. Frozen
+  objects cannot be mutated or observed for changes (as they never change).
+  ([PR #6427](https://github.com/realm/realm-cocoa/pull/6427)).
+* Add the `isFrozen` property to `Realm`, `Results`, `List` and `Object`.
+* Add `Realm.Configuration.maxNumberOfActiveVersions`. Each time a write
+  transaction is performed, a new version is created inside the Realm, and then
+  any versions which are no longer in use are cleaned up. If too many versions
+  are kept alive while performing writes (either due to a background thread
+  performing a long operation that doesn't let the Realm on that thread
+  refresh, or due to holding onto frozen versions for a long time) the Realm
+  file will grow in size, potentially to the point where it is too large to be
+  opened. Setting this configuration option will make write transactions which
+  would cause the live version count to exceed the limit to instead fail.
+* Add support for queue-confined Realms. Rather than being bound to a specific
+  thread, queue-confined Realms are bound to a serial dispatch queue and can be
+  used within blocks dispatched to that queue regardless of what thread they
+  happen to run on. In addition, change notifications will be delivered to that
+  queue rather than the thread's run loop. ([PR #6478](https://github.com/realm/realm-cocoa/pull/6478)).
+* Add an option to deliver object and collection notifications to a specific
+  serial queue rather than the current thread. ([PR #6478](https://github.com/realm/realm-cocoa/pull/6478)).
+* Add Combine publishers for Realm types. Realm collections have a `.publisher`
+  property which publishes the collection each time it changes, and a
+  `.changesetPublisher` which publishes a `RealmCollectionChange` each time the
+  collection changes. Corresponding publishers for Realm Objects can be
+  obtained with the `publisher()` and `changesetPublisher()` global functions.
+* Extend Combine publishers which output Realm types with a `.freeze()`
+  function which will make the publisher instead output frozen objects.
+* String primary keys no longer require a separate index, improving insertion
+  and deletion performance without hurting lookup performance.
+* Reduce the encrypted page reclaimer's impact on battery life when encryption
+  is used. ([Core #3461](https://github.com/realm/realm-core/pull/3461)).
+
+### Fixed
+
+* The uploaded bytes in sync progress notifications was sometimes incorrect and
+  wouldn't exactly equal the uploadable bytes when the uploaded completed.
+* macOS binaries were built with the incorrect deployment target (10.14 rather
+  than 10.9), resulting in linker warnings. ([#6299](https://github.com/realm/realm-cocoa/issues/6299), since 3.18.0).
+* An internal datastructure for List properties could be double-deleted if the
+  last reference was released from a thread other than the one which the List
+  was created on at the wrong time. This would typically manifest as
+  "pthread_mutex_destroy() failed", but could also result in other kinds of
+  crashes. ([#6333](https://github.com/realm/realm-cocoa/issues/6333)).
+* Sorting on float or double properties containing NaN values had inconsistent
+  results and would sometimes crash due to out-of-bounds memory accesses.
+  ([#6357](https://github.com/realm/realm-cocoa/issues/6357)).
+
+### Breaking Changes
+
+* The ObjectChange type in Swift is now generic and includes a reference to the
+  object which changed. When using `observe(on:)` to receive notifications on a
+  dispatch queue, the object will be confined to that queue.
+* The Realm instance passed in the callback to asyncOpen() is now confined to
+  the callback queue passed to asyncOpen() rather than the thread which the
+  callback happens to be called on. This means that the Realm instance may be
+  stored and reused in further blocks dispatched to that queue, but the queue
+  must now be a serial queue.
+* Files containing Date properties written by version of Realm prior to 1.0 can
+  no longer be opened.
+* Files containing Any properties can no longer be opened. This property type
+  was never documented and was deprecated in 1.0.
+* Deleting objects now preserves the order of objects reported by unsorted
+  Results rather than performing a swap operation before the delete. Note that
+  it is still not safe to assume that the order of objects in an unsorted
+  Results is the order that the objects were created in.
+* The minimum supported deployment target for iOS when using Swift Package
+  Manager to install Realm is now iOS 11.
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Realm Studio: 3.11 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.4.1.
+
+### Internal
+
+* Upgraded realm-core from v5.23.8 to v6.0.4
+* Upgraded realm-sync from v4.9.5 to v5.0.3
+
+5.0.0-beta.6 Release notes (2020-05-08)
+=============================================================
+
+### Enhancements
+
+* Add support for queue-confined Realms. Rather than being bound to a specific
+  thread, queue-confined Realms are bound to a serial dispatch queue and can be
+  used within blocks dispatched to that queue regardless of what thread they
+  happen to run on. In addition, change notifications will be delivered to that
+  queue rather than the thread's run loop. ([PR #6478](https://github.com/realm/realm-cocoa/pull/6478)).
+* Add an option to deliver object and collection notifications to a specific
+  serial queue rather than the current thread. ([PR #6478](https://github.com/realm/realm-cocoa/pull/6478)).
+
+### Fixed
+
+* The uploaded bytes in sync progress notifications was sometimes incorrect and
+  wouldn't exactly equal the uploadable bytes when the uploaded completed.
+
+### Breaking Changes
+
+* The Realm instance passed in the callback to asyncOpen() is now confined to
+  the callback queue passed to asyncOpen() rather than the thread which the
+  callback happens to be called on. This means that the Realm instance may be
+  stored and reused in further blocks dispatched to that queue, but the queue
+  must now be a serial queue.
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Carthage release for Swift is built with Xcode 11.4.1.
+
+### Internal
+
+* Upgraded realm-core from v6.0.3 to v6.0.4
+* Upgraded realm-sync from v5.0.1 to v5.0.3
+
 4.4.1 Release notes (2020-04-16)
 4.4.1 Release notes (2020-04-16)
 =============================================================
 =============================================================
 
 
@@ -16,6 +239,157 @@
 * Realm Object Server: 3.21.0 or later.
 * Realm Object Server: 3.21.0 or later.
 * Carthage release for Swift is built with Xcode 11.4.1.
 * Carthage release for Swift is built with Xcode 11.4.1.
 
 
+5.0.0-beta.3 Release notes (2020-02-26)
+=============================================================
+
+Based on 4.3.2 and also includes all changes since 4.3.0.
+
+### Enhancements
+
+* Add support for frozen objects. `Realm`, `Results`, `List` and `Object` now
+  have `freeze()` methods which return a frozen copy of the object. These
+  objects behave similarly to creating unmanaged deep copies of the source
+  objects. They can be read from any thread and do not update when writes are
+  made to the Realm, but creating frozen objects does not actually copy data
+  out of the Realm and so can be much faster and use less memory. Frozen
+  objects cannot be mutated or observed for changes (as they never change).
+  ([PR #6427](https://github.com/realm/realm-cocoa/pull/6427)).
+* Add the `isFrozen` property to `Realm`, `Results`, `List` and `Object`.
+* Add `Realm.Configuration.maxNumberOfActiveVersions`. Each time a write
+  transaction is performed, a new version is created inside the Realm, and then
+  any versions which are no longer in use are cleaned up. If too many versions
+  are kept alive while performing writes (either due to a background thread
+  performing a long operation that doesn't let the Realm on that thread
+  refresh, or due to holding onto frozen versions for a long time) the Realm
+  file will grow in size, potentially to the point where it is too large to be
+  opened. Setting this configuration option will make write transactions which
+  would cause the live version count to exceed the limit to instead fail.
+
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.3.
+
+### Internal
+
+* Upgraded realm-core from v6.0.0-beta.3 to v6.0.3
+* Upgraded realm-sync from v5.0.0-beta.2 to v5.0.1
+
+5.0.0-beta.2 Release notes (2020-01-13)
+=============================================================
+
+Based on 4.3.0 and also includes all changes since 4.1.1.
+
+### Fixed
+
+* Fix compilation when using CocoaPods targeting iOS versions older than 11 (since 5.0.0-alpha).
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.3.
+
+### Internal
+
+* Upgraded realm-core from v6.0.0-beta.2 to v6.0.0-beta.3
+* Upgraded realm-sync from v5.0.0-beta.1 to v5.0.0-beta.2
+
+5.0.0-beta.1 Release notes (2019-12-13)
+=============================================================
+
+Based on 4.1.1 and also includes all changes since 4.1.0.
+
+NOTE: This version bumps the Realm file format to version 10. It is not possible to downgrade version 9 or earlier. Files created with older versions of Realm will be automatically upgraded.
+
+### Enhancements
+
+* String primary keys no longer require a separate index, improving insertion
+  and deletion performance without hurting lookup performance.
+* Reduce the encrypted page reclaimer's impact on battery life when encryption
+  is used. ([Core #3461](https://github.com/realm/realm-core/pull/3461)).
+
+### Fixed
+
+* Fix an error when a table-backed Results was accessed immediately after
+  deleting the object previously at the index being accessed (since
+  5.0.0-alpha.1).
+* macOS binaries were built with the incorrect deployment target (10.14 rather
+  than 10.9), resulting in linker warnings. ([#6299](https://github.com/realm/realm-cocoa/issues/6299), since 3.18.0).
+* An internal datastructure for List properties could be double-deleted if the
+  last reference was released from a thread other than the one which the List
+  was created on at the wrong time. This would typically manifest as
+  "pthread_mutex_destroy() failed", but could also result in other kinds of
+  crashes. ([#6333](https://github.com/realm/realm-cocoa/issues/6333)).
+* Sorting on float or double properties containing NaN values had inconsistent
+  results and would sometimes crash due to out-of-bounds memory accesses.
+  ([#6357](https://github.com/realm/realm-cocoa/issues/6357)).
+
+### Known Issues
+
+* Changing which property of an object is the primary key in a migration will
+  break incoming links to objects of that type.
+* Changing the primary key of an object with Data properties in a migration
+  will crash.
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* APIs are backwards compatible with all previous releases in the 5.x.y series.
+* Carthage release for Swift is built with Xcode 11.3.
+
+### Internal
+
+* Upgraded realm-core from v6.0.0-alpha.24 to v6.0.0-beta.2
+* Upgraded realm-sync from 4.7.1-core6.5 to v5.0.0-beta.1
+
+5.0.0-alpha.1 Release notes (2019-11-14)
+=============================================================
+
+Based on 4.1.0.
+
+### Enhancements
+
+* Add `-[RLMRealm fileExistsForConfiguration:]`/`Realm.fileExists(for:)`,
+  which checks if a local Realm file exists for the given configuration.
+* Add `-[RLMRealm deleteFilesForConfiguration:]`/`Realm.deleteFiles(for:)`
+  to delete the Realm file and all auxiliary files for the given configuration.
+* Storing large binary blobs in Realm files no longer forces the file to be at
+  least 8x the size of the largest blob.
+* Reduce the size of transaction logs stored inside the Realm file, reducing
+  file size growth from large transactions.
+
+NOTE: This version bumps the Realm file format to version 10. It is not
+possible to downgrade version 9 or earlier. Files created with older versions
+of Realm will be automatically upgraded. This automatic upgrade process is not
+yet well tested. Do not open Realm files with data you care about with this
+alpha version.
+
+### Breaking Changes
+
+* Files containing Date properties written by version of Realm prior to 1.0 can
+  no longer be opened.
+* Files containing Any properties can no longer be opened. This property type
+  was never documented and was deprecated in 1.0.
+
+### Compatibility
+
+* File format: Generates Realms with format v10 (Reads and upgrades v9)
+* Realm Object Server: 3.21.0 or later.
+* APIs are backwards compatible with all previous releases in the 4.x.y series.
+* Carthage release for Swift is built with Xcode 11.3.
+* Carthage release for Swift is built with Xcode 11.2.1.
+
+### Internal
+
+* Upgraded realm-core from 5.23.6 to v6.0.0-alpha.24.
+* Upgraded realm-sync from 4.8.2 to 4.7.1-core6.5.
+
 4.4.0 Release notes (2020-03-26)
 4.4.0 Release notes (2020-03-26)
 =============================================================
 =============================================================
 
 

+ 2 - 1
Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig

@@ -1,5 +1,5 @@
 ALWAYS_SEARCH_USER_PATHS = NO;
 ALWAYS_SEARCH_USER_PATHS = NO;
-CLANG_CXX_LANGUAGE_STANDARD = c++14;
+CLANG_CXX_LANGUAGE_STANDARD = c++17;
 CLANG_CXX_LIBRARY = libc++;
 CLANG_CXX_LIBRARY = libc++;
 CLANG_ENABLE_MODULES = YES;
 CLANG_ENABLE_MODULES = YES;
 CLANG_ENABLE_OBJC_ARC = YES;
 CLANG_ENABLE_OBJC_ARC = YES;
@@ -46,6 +46,7 @@ SWIFT_COMPILATION_MODE = wholemodule;
 SWIFT_OPTIMIZATION_LEVEL = -Owholemodule;
 SWIFT_OPTIMIZATION_LEVEL = -Owholemodule;
 WARNING_CFLAGS = -Wmismatched-tags -Wunused-private-field -Wpartial-availability;
 WARNING_CFLAGS = -Wmismatched-tags -Wunused-private-field -Wpartial-availability;
 OTHER_CFLAGS = -fvisibility-inlines-hidden $(REALM_CATALYST_FLAGS);
 OTHER_CFLAGS = -fvisibility-inlines-hidden $(REALM_CATALYST_FLAGS);
+OTHER_CFLAGS[arch=armv7] = -fvisibility-inlines-hidden -fno-aligned-new $(REALM_CATALYST_FLAGS);
 OTHER_LDFLAGS = $(REALM_CATALYST_FLAGS);
 OTHER_LDFLAGS = $(REALM_CATALYST_FLAGS);
 OTHER_SWIFT_FLAGS = $(REALM_CATALYST_FLAGS);
 OTHER_SWIFT_FLAGS = $(REALM_CATALYST_FLAGS);
 
 

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig

@@ -33,7 +33,7 @@ REALM_PLATFORM_SUFFIX_macos = macosx;
 REALM_PLATFORM_SUFFIX_iosmac = maccatalyst;
 REALM_PLATFORM_SUFFIX_iosmac = maccatalyst;
 REALM_PLATFORM_SUFFIX = $(REALM_PLATFORM_SUFFIX_$(SDK_VARIANT));
 REALM_PLATFORM_SUFFIX = $(REALM_PLATFORM_SUFFIX_$(SDK_VARIANT));
 OTHER_LDFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX) $(REALM_CATALYST_FLAGS);
 OTHER_LDFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX) $(REALM_CATALYST_FLAGS);
-OTHER_LIBTOOLFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX) $(REALM_CATALYST_FLAGS);
+OTHER_LIBTOOLFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX);
 OTHER_LDFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
 OTHER_LDFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
 OTHER_LIBTOOLFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
 OTHER_LIBTOOLFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
 OTHER_LDFLAGS[sdk=watch*] = -lrealm-watchos$(REALM_LIBRARY_SUFFIX);
 OTHER_LDFLAGS[sdk=watch*] = -lrealm-watchos$(REALM_LIBRARY_SUFFIX);

+ 26 - 18
Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability

@@ -1,10 +1,10 @@
-xcodeVersions = ['10.3', '11.1', '11.2.1', '11.3', '11.4.1']
+xcodeVersions = ['10.3', '11.1', '11.2.1', '11.3', '11.4.1', '11.5']
 platforms = ['osx', 'ios', 'watchos', 'tvos', 'catalyst']
 platforms = ['osx', 'ios', 'watchos', 'tvos', 'catalyst']
 carthagePlatforms = ['osx', 'ios', 'watchos', 'tvos']
 carthagePlatforms = ['osx', 'ios', 'watchos', 'tvos']
 platformNames = ['osx': 'macOS', 'ios': 'iOS', 'watchos': 'watchOS', 'tvos': 'tvOS', 'catalyst': 'Catalyst']
 platformNames = ['osx': 'macOS', 'ios': 'iOS', 'watchos': 'watchOS', 'tvos': 'tvOS', 'catalyst': 'Catalyst']
-carthageXcodeVersion = '11.4.1'
+carthageXcodeVersion = '11.5'
 objcXcodeVersion = '10.3'
 objcXcodeVersion = '10.3'
-docsSwiftVersion = '5.1.2'
+docsSwiftVersion = '5.2.4'
 
 
 def installationTest(platform, test, language) {
 def installationTest(platform, test, language) {
   return {
   return {
@@ -17,10 +17,8 @@ def installationTest(platform, test, language) {
       }
       }
 
 
       sh """
       sh """
+      hostname
       export REALM_XCODE_VERSION=${carthageXcodeVersion}
       export REALM_XCODE_VERSION=${carthageXcodeVersion}
-      if [ "${platform}" != osx ]; then
-        ./scripts/reset-simulators.sh
-      fi
       cd examples/installation
       cd examples/installation
 
 
       archive=\$(echo \$PWD/realm-${language}-*.zip)
       archive=\$(echo \$PWD/realm-${language}-*.zip)
@@ -64,6 +62,7 @@ def doBuild() {
           deleteDir()
           deleteDir()
           unstash 'source'
           unstash 'source'
           sh """
           sh """
+          hostname
           export REALM_SWIFT_VERSION=${docsSwiftVersion}
           export REALM_SWIFT_VERSION=${docsSwiftVersion}
           export PATH='/Users/realm/.rbenv/bin:/Users/realm/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/realm/.gems/bin'
           export PATH='/Users/realm/.rbenv/bin:/Users/realm/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/realm/.gems/bin'
           ./scripts/reset-simulators.sh
           ./scripts/reset-simulators.sh
@@ -82,7 +81,7 @@ def doBuild() {
         node('osx') {
         node('osx') {
           deleteDir()
           deleteDir()
           unstash 'source'
           unstash 'source'
-          sh 'XCMODE=xcpretty ./build.sh package-examples'
+          sh './build.sh package-examples'
           stash includes: 'realm-examples.zip', name: 'examples'
           stash includes: 'realm-examples.zip', name: 'examples'
         }
         }
       },
       },
@@ -91,7 +90,7 @@ def doBuild() {
         node('osx') {
         node('osx') {
           deleteDir()
           deleteDir()
           unstash 'source'
           unstash 'source'
-          sh "XCMODE=xcpretty REALM_XCODE_VERSION=${objcXcodeVersion} ./build.sh package-ios-static"
+          sh "REALM_XCODE_VERSION=${objcXcodeVersion} ./build.sh package-ios-static"
           dir("build/ios-static") {
           dir("build/ios-static") {
             stash includes: "realm-framework-ios-static.zip", name: "ios-static"
             stash includes: "realm-framework-ios-static.zip", name: "ios-static"
           }
           }
@@ -107,13 +106,21 @@ def doBuild() {
           deleteDir()
           deleteDir()
           unstash 'source'
           unstash 'source'
           sh """
           sh """
+          hostname
           export REALM_XCODE_VERSION=${carthageXcodeVersion}
           export REALM_XCODE_VERSION=${carthageXcodeVersion}
           . ./scripts/swift-version.sh
           . ./scripts/swift-version.sh
           set_xcode_and_swift_versions
           set_xcode_and_swift_versions
 
 
-          if [ "${platform}" != osx ]; then
-            ./scripts/reset-simulators.rb
-          fi
+          # Carthage scans every xcodeproj in the directory looking for
+          # targets. This can be very slow and even spuriously time out, so
+          # remove the ones we don't want it to build.
+          rm -r examples plugin
+
+          # For whatever reason 'xcodebuild -list' is very slow sometimes which
+          # makes Carthage time out, but it's a lot faster if no simulators
+          # exist, so delete them all first and only create a single simulator
+          # for each platform.
+          ./scripts/reset-simulators.rb -firstOnly
 
 
           carthage build --no-skip-current --platform ${platform}
           carthage build --no-skip-current --platform ${platform}
           carthage archive --output Carthage-${platform}.framework.zip
           carthage archive --output Carthage-${platform}.framework.zip
@@ -133,7 +140,7 @@ def doBuild() {
           node('osx') {
           node('osx') {
             deleteDir()
             deleteDir()
             unstash 'source'
             unstash 'source'
-            sh "XCMODE=xcpretty REALM_XCODE_VERSION=${xcodeVersion} ./build.sh package ${platform}"
+            sh "REALM_XCODE_VERSION=${xcodeVersion} ./build.sh package ${platform}"
             dir("build/${platform}") {
             dir("build/${platform}") {
               stash includes: "realm-framework-${platform}-${xcodeVersion}.zip",
               stash includes: "realm-framework-${platform}-${xcodeVersion}.zip",
                     name: "${platform}-${xcodeVersion}"
                     name: "${platform}-${xcodeVersion}"
@@ -220,6 +227,7 @@ def doBuild() {
 
 
           def sha = params.sha
           def sha = params.sha
           sh """
           sh """
+          hostname
           curl -O https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/build.sh
           curl -O https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/build.sh
           mkdir -p scripts
           mkdir -p scripts
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/swift-version.sh -o scripts/swift-version.sh
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/swift-version.sh -o scripts/swift-version.sh
@@ -227,7 +235,7 @@ def doBuild() {
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.rb -o scripts/reset-simulators.rb
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.rb -o scripts/reset-simulators.rb
           chmod +x scripts/reset-simulators.rb
           chmod +x scripts/reset-simulators.rb
 
 
-          XCMODE=xcpretty sh build.sh package-test-examples-objc
+          sh build.sh package-test-examples-objc
           """
           """
         }
         }
       },
       },
@@ -240,6 +248,7 @@ def doBuild() {
 
 
           def sha = params.sha
           def sha = params.sha
           sh """
           sh """
+          hostname
           curl -O https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/build.sh
           curl -O https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/build.sh
           mkdir -p scripts
           mkdir -p scripts
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/swift-version.sh -o scripts/swift-version.sh
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/swift-version.sh -o scripts/swift-version.sh
@@ -247,7 +256,7 @@ def doBuild() {
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.rb -o scripts/reset-simulators.rb
           curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.rb -o scripts/reset-simulators.rb
           chmod +x scripts/reset-simulators.rb
           chmod +x scripts/reset-simulators.rb
 
 
-          XCMODE=xcpretty sh build.sh package-test-examples-swift
+          sh build.sh package-test-examples-swift
           """
           """
         }
         }
       },
       },
@@ -258,7 +267,7 @@ def doBuild() {
           unstash 'source'
           unstash 'source'
 
 
           sh './scripts/reset-simulators.rb'
           sh './scripts/reset-simulators.rb'
-          sh 'XCMODE=xcpretty sh build.sh test-ios-static'
+          sh 'sh build.sh test-ios-static'
         }
         }
       },
       },
 
 
@@ -266,8 +275,7 @@ def doBuild() {
         node('osx') {
         node('osx') {
           deleteDir()
           deleteDir()
           unstash 'source'
           unstash 'source'
-
-          sh 'XCMODE=xcpretty sh build.sh test-osx'
+          sh 'sh build.sh test-osx'
         }
         }
       }
       }
     ]
     ]
@@ -284,7 +292,7 @@ def doBuild() {
 
 
     for (def platform in ["osx", "ios", "watchos"]) {
     for (def platform in ["osx", "ios", "watchos"]) {
       def platformName = platformNames[platform]
       def platformName = platformNames[platform]
-      for (def test in ["cocoapods", "carthage"]) {
+      for (def test in ["dynamic", "cocoapods", "carthage"]) {
         parallelBuilds["Installation - ${platformName} Swift ${test}"] = installationTest(platform, test, 'swift')
         parallelBuilds["Installation - ${platformName} Swift ${test}"] = installationTest(platform, test, 'swift')
       }
       }
     }
     }

+ 2 - 1
Carthage/Checkouts/realm-cocoa/Realm.podspec

@@ -87,9 +87,10 @@ Pod::Spec.new do |s|
   s.private_header_files    = private_header_files
   s.private_header_files    = private_header_files
   s.header_mappings_dir     = 'include'
   s.header_mappings_dir     = 'include'
   s.pod_target_xcconfig     = { 'APPLICATION_EXTENSION_API_ONLY' => 'YES',
   s.pod_target_xcconfig     = { 'APPLICATION_EXTENSION_API_ONLY' => 'YES',
-                                'CLANG_CXX_LANGUAGE_STANDARD' => 'c++14',
+                                'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
                                 'CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF' => 'NO',
                                 'CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF' => 'NO',
                                 'OTHER_CPLUSPLUSFLAGS' => '-isystem "${PODS_ROOT}/Realm/include/core" -fvisibility-inlines-hidden',
                                 'OTHER_CPLUSPLUSFLAGS' => '-isystem "${PODS_ROOT}/Realm/include/core" -fvisibility-inlines-hidden',
+                                'OTHER_CPLUSPLUSFLAGS[arch=armv7]' => '-isystem "${PODS_ROOT}/Realm/include/core" -fvisibility-inlines-hidden -fno-aligned-new',
                                 'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Realm/include" "${PODS_ROOT}/Realm/include/Realm"',
                                 'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Realm/include" "${PODS_ROOT}/Realm/include/Realm"',
                               }
                               }
   s.preserve_paths          = %w(build.sh include)
   s.preserve_paths          = %w(build.sh include)

+ 15 - 48
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm

@@ -1826,54 +1826,6 @@ static const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
     XCTAssertLessThanOrEqual(finalSize, usedSize + 4096U);
     XCTAssertLessThanOrEqual(finalSize, usedSize + 4096U);
 }
 }
 
 
-#pragma mark - Offline Client Reset
-
-- (void)testOfflineClientReset {
-    NSError *error;
-    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
-                                                                                            register:YES]
-                                               server:[RLMObjectServerTests authServerURL]];
-    NSURL *sourceFileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"sync-1.x" withExtension:@"realm"];
-    NSString *fileName = [NSString stringWithFormat:@"%@.realm", [NSUUID new]];
-    NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
-    [NSFileManager.defaultManager copyItemAtURL:sourceFileURL toURL:fileURL error:&error];
-    XCTAssertNil(error);
-    if (error) {
-        return;
-    }
-
-    RLMRealmConfiguration *configuration = [user configurationWithURL:REALM_URL() fullSynchronization:true];
-    RLMSyncConfiguration *syncConfig = configuration.syncConfiguration;
-    syncConfig.customFileURL = fileURL;
-    configuration.syncConfiguration = syncConfig;
-
-    RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:&error];
-    XCTAssertNil(realm);
-    XCTAssertEqualObjects(error.domain, RLMErrorDomain);
-    XCTAssertEqual(error.code, RLMErrorIncompatibleSyncedFile);
-    RLMRealmConfiguration *backupConfiguration = error.userInfo[RLMBackupRealmConfigurationErrorKey];
-    XCTAssertNotNil(backupConfiguration);
-
-    // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
-    // The Person class is declared in SwiftObjectServerTests.swift.
-    backupConfiguration.objectClasses = @[NSClassFromString(@"Person")];
-
-    error = nil;
-    RLMRealm *backupRealm = [RLMRealm realmWithConfiguration:backupConfiguration error:&error];
-    XCTAssertNotNil(backupRealm);
-    XCTAssertNil(error);
-
-    RLMResults *people = [backupRealm allObjects:@"Person"];
-    XCTAssertEqual(people.count, 1u);
-    XCTAssertEqualObjects([people[0] valueForKey:@"FirstName"], @"John");
-    XCTAssertEqualObjects([people[0] valueForKey:@"LastName"], @"Smith");
-
-    error = nil;
-    realm = [RLMRealm realmWithConfiguration:configuration error:&error];
-    XCTAssertNotNil(realm);
-    XCTAssertNil(error);
-}
-
 #pragma mark - Partial sync
 #pragma mark - Partial sync
 
 
 - (void)waitForKeyPath:(NSString *)keyPath object:(id)object value:(id)value {
 - (void)waitForKeyPath:(NSString *)keyPath object:(id)object value:(id)value {
@@ -1967,6 +1919,18 @@ static const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
     return [self openRealmWithConfiguration:configuration];
     return [self openRealmWithConfiguration:configuration];
 }
 }
 
 
+- (void)testAllSubscriptionsChecksThatRealmIsQBS {
+    RLMRealm *nonsyncRealm = [RLMRealm defaultRealm];
+    RLMAssertThrowsWithReason(nonsyncRealm.subscriptions, @"query-based sync");
+
+    NSString *name = NSStringFromSelector(_cmd);
+    NSURL *server = [RLMObjectServerTests authServerURL];
+    RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:YES];
+    RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
+    RLMRealm *fullsyncRealm = [self openRealmWithConfiguration:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"] fullSynchronization:YES]];
+    RLMAssertThrowsWithReason(fullsyncRealm.subscriptions, @"query-based sync");
+}
+
 - (void)testAllSubscriptionsReportsNewlyCreatedSubscription {
 - (void)testAllSubscriptionsReportsNewlyCreatedSubscription {
     RLMRealm *realm = [self partialRealmWithName:_cmd];
     RLMRealm *realm = [self partialRealmWithName:_cmd];
     XCTAssertEqual(0U, realm.subscriptions.count);
     XCTAssertEqual(0U, realm.subscriptions.count);
@@ -2060,6 +2024,9 @@ static const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
 }
 }
 
 
 - (void)testSubscriptionWithName {
 - (void)testSubscriptionWithName {
+    RLMRealm *nonsyncRealm = [RLMRealm defaultRealm];
+    XCTAssertThrows([nonsyncRealm subscriptionWithName:@"name"]);
+
     RLMRealm *realm = [self partialRealmWithName:_cmd];
     RLMRealm *realm = [self partialRealmWithName:_cmd];
     XCTAssertNil([realm subscriptionWithName:@"query"]);
     XCTAssertNil([realm subscriptionWithName:@"query"]);
 
 

+ 0 - 35
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift

@@ -516,41 +516,6 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
         }
         }
     }
     }
 
 
-    // MARK: - Offline client reset
-
-    func testOfflineClientReset() {
-        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
-
-        let sourceFileURL = Bundle(for: type(of: self)).url(forResource: "sync-1.x", withExtension: "realm")!
-        let fileName = "\(UUID()).realm"
-        let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
-        try! FileManager.default.copyItem(at: sourceFileURL, to: fileURL)
-
-        let syncConfig = ObjectiveCSupport.convert(object: user.configuration(realmURL: realmURL, fullSynchronization: true).syncConfiguration!)
-        syncConfig.customFileURL = fileURL
-        let config = Realm.Configuration(syncConfiguration: ObjectiveCSupport.convert(object: syncConfig))
-        do {
-            _ = try Realm(configuration: config)
-        } catch let e as Realm.Error where e.code == .incompatibleSyncedFile {
-            var backupConfiguration = e.backupConfiguration
-            XCTAssertNotNil(backupConfiguration)
-
-            // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
-            backupConfiguration!.objectTypes = [Person.self]
-            let backupRealm = try! Realm(configuration: backupConfiguration!)
-
-            let people = backupRealm.objects(Person.self)
-            XCTAssertEqual(people.count, 1)
-            XCTAssertEqual(people[0].FirstName, "John")
-            XCTAssertEqual(people[0].LastName, "Smith")
-
-            // Verify that we can now successfully open the original synced Realm.
-            _ = try! Realm(configuration: config)
-        } catch {
-            fatalError("Unexpected error: \(error)")
-        }
-    }
-
     // MARK: - Certificate Pinning
     // MARK: - Certificate Pinning
 
 
     func testSecureConnectionToLocalhostWithDefaultSecurity() {
     func testSecureConnectionToLocalhostWithDefaultSecurity() {

+ 4 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake

@@ -32,6 +32,7 @@ if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL
         -Wall
         -Wall
         -Wextra
         -Wextra
         -Wno-missing-field-initializers
         -Wno-missing-field-initializers
+        -Wno-unevaluated-expression
         -Wempty-body
         -Wempty-body
         -Wparentheses
         -Wparentheses
         -Wunknown-pragmas
         -Wunknown-pragmas
@@ -72,11 +73,13 @@ if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
         -Wconditional-uninitialized
         -Wconditional-uninitialized
         -Wconstant-conversion
         -Wconstant-conversion
         -Wenum-conversion
         -Wenum-conversion
+        -Wimplicit-fallthrough
         -Wint-conversion
         -Wint-conversion
         -Wmissing-prototypes
         -Wmissing-prototypes
         -Wnewline-eof
         -Wnewline-eof
         -Wshorten-64-to-32
         -Wshorten-64-to-32
-        -Wimplicit-fallthrough
+        -Wthread-safety
+        -Wthread-safety-negative
     )
     )
 endif()
 endif()
 
 

+ 71 - 87
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake

@@ -1,4 +1,3 @@
-###########################################################################
 #
 #
 # Copyright 2016 Realm Inc.
 # Copyright 2016 Realm Inc.
 #
 #
@@ -39,15 +38,34 @@ if(APPLE)
 
 
     set(CRYPTO_LIBRARIES "")
     set(CRYPTO_LIBRARIES "")
     set(SSL_LIBRARIES ${FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK})
     set(SSL_LIBRARIES ${FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK})
-elseif(REALM_PLATFORM STREQUAL "Android")
-    set(CRYPTO_LIBRARIES crypto)
-    set(SSL_LIBRARIES ssl)
 elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
 elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
     # Windows doesn't do crypto right now, but that is subject to change
     # Windows doesn't do crypto right now, but that is subject to change
     set(CRYPTO_LIBRARIES "")
     set(CRYPTO_LIBRARIES "")
     set(SSL_LIBRARIES "")
     set(SSL_LIBRARIES "")
 else()
 else()
-    find_package(OpenSSL REQUIRED)
+    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(CRYPTO_LIBRARIES OpenSSL::Crypto)
     set(SSL_LIBRARIES OpenSSL::SSL)
     set(SSL_LIBRARIES OpenSSL::SSL)
@@ -61,7 +79,7 @@ function(use_realm_core enable_sync core_prefix sync_prefix)
     if(core_prefix)
     if(core_prefix)
         build_existing_realm_core(${core_prefix})
         build_existing_realm_core(${core_prefix})
         if(sync_prefix)
         if(sync_prefix)
-            build_existing_realm_sync(${core_prefix} ${sync_prefix})
+            build_existing_realm_sync(${sync_prefix})
         endif()
         endif()
     elseif(enable_sync)
     elseif(enable_sync)
         # FIXME: Support building against prebuilt sync binaries.
         # FIXME: Support building against prebuilt sync binaries.
@@ -110,26 +128,6 @@ function(download_realm_tarball url target libraries)
     endif()
     endif()
 endfunction()
 endfunction()
 
 
-function(download_android_openssl)
-    if(ANDROID)
-        string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
-        set(OPENSSL_FILENAME "openssl-${BUILD_TYPE}-${ANDROID_OPENSSL_VERSION}-Android-${ANDROID_ABI}")
-        set(OPENSSL_URL "http://static.realm.io/downloads/openssl/${ANDROID_OPENSSL_VERSION}/Android/${ANDROID_ABI}/${OPENSSL_FILENAME}.tar.gz")
-
-        message(STATUS "Downloading OpenSSL...")
-        file(DOWNLOAD "${OPENSSL_URL}" "${CMAKE_BINARY_DIR}/${OPENSSL_FILENAME}.tar.gz")
-
-        message(STATUS "Uncompressing OpenSSL...")
-        execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz "${OPENSSL_FILENAME}.tar.gz")
-
-        message(STATUS "Importing OpenSSL...")
-        include(${CMAKE_BINARY_DIR}/${OPENSSL_FILENAME}/openssl.cmake)
-        get_target_property(OPENSSL_INCLUDE_DIR crypto INTERFACE_INCLUDE_DIRECTORIES)
-        get_target_property(CRYPTO_LIB crypto IMPORTED_LOCATION)
-        get_target_property(SSL_LIB ssl IMPORTED_LOCATION)
-    endif()
-endfunction()
-
 function(download_realm_core core_version)
 function(download_realm_core core_version)
     if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     if(CMAKE_SYSTEM_NAME MATCHES "Windows")
         set(compression "tar.gz")
         set(compression "tar.gz")
@@ -191,7 +189,6 @@ function(download_realm_core core_version)
         set(core_libraries ${core_library_debug} ${core_library_release} ${core_parser_library_debug} ${core_parser_library_release})
         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}")
         download_realm_tarball(${url} ${core_directory} "${core_libraries}")
-        download_android_openssl()
     endif()
     endif()
 
 
     add_custom_target(realm-core DEPENDS ${core_libraries})
     add_custom_target(realm-core DEPENDS ${core_libraries})
@@ -221,25 +218,17 @@ endfunction()
 macro(build_realm_core)
 macro(build_realm_core)
     set(core_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-core")
     set(core_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-core")
 
 
-    separate_arguments(core_cfg_args UNIX_COMMAND "-D REALM_BUILD_LIB_ONLY=YES -D REALM_SKIP_SHARED_LIB=YES -G Ninja ${CORE_SANITIZER_FLAGS}")
     ExternalProject_Add(realm-core
     ExternalProject_Add(realm-core
         PREFIX ${core_prefix_directory}
         PREFIX ${core_prefix_directory}
         BUILD_IN_SOURCE 1
         BUILD_IN_SOURCE 1
         UPDATE_DISCONNECTED 1
         UPDATE_DISCONNECTED 1
         INSTALL_COMMAND ""
         INSTALL_COMMAND ""
-        CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory build.debug
-                        && cd build.debug
-                        && cmake -D CMAKE_BUILD_TYPE=Debug ${core_cfg_args} ..
-                        && cd ..
-                        && ${CMAKE_COMMAND} -E make_directory build.release
-                        && cd build.release
-                        && cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo ${core_cfg_args} ..
-
-        BUILD_COMMAND cd build.debug
-                   && cmake --build .
-                   && cd ..
-                   && cd build.release
-                   && cmake --build .
+        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}
         ${USES_TERMINAL_BUILD}
         ${ARGN}
         ${ARGN}
         )
         )
@@ -268,7 +257,7 @@ macro(build_realm_core)
     set_property(TARGET realm PROPERTY IMPORTED_LOCATION_RELEASE ${core_library_release})
     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 IMPORTED_LOCATION ${core_library_release})
 
 
-    set_property(TARGET realm PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ${CRYPTO_LIBRARIES})
+    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
     # 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.
     # configure time, when they'd otherwise not be created until we download and build core.
@@ -303,52 +292,42 @@ function(build_existing_realm_core core_directory)
                      )
                      )
 endfunction()
 endfunction()
 
 
-macro(build_realm_sync core_directory)
+macro(build_realm_sync)
     set(sync_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-sync")
     set(sync_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-sync")
 
 
-    separate_arguments(sync_cfg_args UNIX_COMMAND "-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DREALM_BUILD_TESTS=OFF -DREALM_BUILD_COMMANDLINE_TOOLS=OFF -G Ninja ${CORE_SANITIZER_FLAGS}")
+    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
     ExternalProject_Add(realm-sync-lib
+        DEPENDS realm-core
         PREFIX ${sync_prefix_directory}
         PREFIX ${sync_prefix_directory}
         BUILD_IN_SOURCE 1
         BUILD_IN_SOURCE 1
         UPDATE_DISCONNECTED 1
         UPDATE_DISCONNECTED 1
         INSTALL_COMMAND ""
         INSTALL_COMMAND ""
-        CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory build.debug
-                        && cd build.debug
-                        && cmake -D CMAKE_BUILD_TYPE=Debug -DREALM_CORE_BUILDTREE=${core_directory}/build.debug ${sync_cfg_args} -DREALM_BUILD_DOGLESS=OFF ..
-                        && cd ..
-                        && ${CMAKE_COMMAND} -E make_directory build.release
-                        && cd build.release
-                        && cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DREALM_CORE_BUILDTREE=${core_directory}/build.release ${sync_cfg_args} -DREALM_BUILD_DOGLESS=OFF ..
-
-        BUILD_COMMAND cd build.debug
-                   && cmake --build .
-                   && cd ..
-                   && cd build.release
-                   && cmake --build .
-        ${USES_TERMINAL_BUILD}
+        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}
         ${ARGN}
         )
         )
+
     ExternalProject_Get_Property(realm-sync-lib SOURCE_DIR)
     ExternalProject_Get_Property(realm-sync-lib SOURCE_DIR)
-    add_dependencies(realm-sync-lib realm-core)
 
 
     set(sync_debug_binary_dir "${SOURCE_DIR}/build.debug")
     set(sync_debug_binary_dir "${SOURCE_DIR}/build.debug")
     set(sync_release_binary_dir "${SOURCE_DIR}/build.release")
     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_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}")
     set(sync_library_release "${sync_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-sync${CMAKE_STATIC_LIBRARY_SUFFIX}")
-    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-libraries
     ExternalProject_Add_Step(realm-sync-lib ensure-libraries
-        DEPENDEES build
         BYPRODUCTS ${sync_library_debug} ${sync_library_release}
         BYPRODUCTS ${sync_library_debug} ${sync_library_release}
-                   ${sync_server_library_debug} ${sync_server_library_release}
+        DEPENDEES build
         )
         )
 
 
-    set(sync_generated_headers_dir_debug "${sync_debug_binary_dir}/src")
-    set(sync_generated_headers_dir_release "${sync_release_binary_dir}/src")
-
     add_library(realm-sync STATIC IMPORTED)
     add_library(realm-sync STATIC IMPORTED)
     add_dependencies(realm-sync realm-sync-lib)
     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_DEBUG ${sync_library_debug})
     set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_COVERAGE ${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_RELEASE ${sync_library_release})
@@ -358,41 +337,46 @@ macro(build_realm_sync core_directory)
 
 
     # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
     # 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.
     # configure time, when they'd otherwise not be created until we download and build sync.
-    file(MAKE_DIRECTORY "${sync_generated_headers_dir_debug}" "${sync_generated_headers_dir_release}" "${SOURCE_DIR}/src")
+    file(MAKE_DIRECTORY ${SOURCE_DIR}/src)
+    set_property(TARGET realm-sync PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SOURCE_DIR}/src)
 
 
-    set_property(TARGET realm-sync PROPERTY INTERFACE_INCLUDE_DIRECTORIES
-        ${SOURCE_DIR}/src
-        $<$<CONFIG:Debug>:${sync_generated_headers_dir_debug}>
-        $<$<NOT:$<CONFIG:Debug>>:${sync_generated_headers_dir_release}>
-    )
+    # 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_library(realm-sync-server STATIC IMPORTED)
-    add_dependencies(realm realm-sync)
+    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_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_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_RELEASE ${sync_server_library_release})
     set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION ${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()
 endmacro()
 
 
-function(build_existing_realm_sync core_directory sync_directory)
+function(build_existing_realm_sync sync_directory)
     get_filename_component(sync_directory ${sync_directory} ABSOLUTE)
     get_filename_component(sync_directory ${sync_directory} ABSOLUTE)
-    build_realm_sync(
-        ${core_directory}
-        URL ""
-        SOURCE_DIR ${sync_directory}
-        BUILD_ALWAYS 1
-        )
+    build_realm_sync(URL ""
+                     SOURCE_DIR ${sync_directory}
+                     BUILD_ALWAYS 1
+                     )
 
 
 endfunction()
 endfunction()
 
 
 function(clone_and_build_realm_sync branch)
 function(clone_and_build_realm_sync branch)
-    set(core_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-core/src/realm-core")
     set(cmake_files ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
     set(cmake_files ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
-    build_realm_sync(
-        ${core_prefix_directory}
-        GIT_REPOSITORY "git@github.com:realm/realm-sync.git"
-        GIT_TAG ${branch}
-        CONFIGURE_COMMAND ${config_cmd}
-        )
+
+    build_realm_sync(GIT_REPOSITORY "git@github.com:realm/realm-sync.git"
+                     GIT_TAG ${branch}
+                     CONFIGURE_COMMAND ${config_cmd}
+                     )
 
 
 endfunction()
 endfunction()

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.2.0)
 
 
 if(REALM_PLATFORM STREQUAL "Android")
 if(REALM_PLATFORM STREQUAL "Android")
     # This must be before project()
     # This must be before project()
-    set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMake/android.toolchain.cmake")
+    set(CMAKE_TOOLCHAIN_FILE "${ANDROID_NDK}/build/cmake/android.toolchain.cmake")
     set(ANDROID_ABI "x86" CACHE STRING "")
     set(ANDROID_ABI "x86" CACHE STRING "")
     set(ANDROID_NATIVE_API_LEVEL "android-16" CACHE STRING "")
     set(ANDROID_NATIVE_API_LEVEL "android-16" CACHE STRING "")
 endif()
 endif()

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile

@@ -1,7 +1,7 @@
 FROM ubuntu:xenial
 FROM ubuntu:xenial
 
 
 RUN apt-get update && \
 RUN apt-get update && \
-    apt-get install -y wget build-essential lcov curl cmake gcovr libssl-dev \
+    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
       git python-cheetah libuv1-dev ninja-build adb xutils-dev
 
 
 # Install the Android NDK
 # Install the Android NDK

+ 6 - 8
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile

@@ -76,20 +76,18 @@ def doAndroidDockerBuild() {
     node('docker') {
     node('docker') {
       getSourceArchive()
       getSourceArchive()
       wrap([$class: 'AnsiColorBuildWrapper']) {
       wrap([$class: 'AnsiColorBuildWrapper']) {
-        def image = buildDockerEnv('ci/realm-object-store:android')
+        def image = docker.build('realm-object-store:ndk21', '-f android.Dockerfile .')
         docker.image('tracer0tong/android-emulator').withRun { emulator ->
         docker.image('tracer0tong/android-emulator').withRun { emulator ->
           image.inside("--link ${emulator.id}:emulator") {
           image.inside("--link ${emulator.id}:emulator") {
-            sh '''rm -rf build
-              mkdir build
-              cd build
-              cmake -DREALM_PLATFORM=Android -DANDROID_NDK=/opt/android-ndk -GNinja -DCMAKE_MAKE_PROGRAM=ninja ..
-              ninja
+            sh """
+              cmake -B build -DREALM_PLATFORM=Android -DANDROID_NDK=\${ANDROID_NDK} -GNinja -DCMAKE_MAKE_PROGRAM=ninja
+              cmake --build build
               adb connect emulator
               adb connect emulator
               timeout 10m adb wait-for-device
               timeout 10m adb wait-for-device
-              adb push tests/tests /data/local/tmp
+              adb push build/tests/tests /data/local/tmp
               adb shell '/data/local/tmp/tests || echo __ADB_FAIL__' | tee adb.log
               adb shell '/data/local/tmp/tests || echo __ADB_FAIL__' | tee adb.log
               ! grep __ADB_FAIL__ adb.log
               ! grep __ADB_FAIL__ adb.log
-            '''
+            """
           }
           }
         }
         }
       }
       }

+ 3 - 3
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list

@@ -1,4 +1,4 @@
-REALM_CORE_VERSION=5.23.6
-REALM_SYNC_VERSION=4.9.0
-ANDROID_OPENSSL_VERSION=1.0.2k
+REALM_CORE_VERSION=6.0.1
+REALM_SYNC_VERSION=5.0.0
 REALM_CORE_PACKAGING=2
 REALM_CORE_PACKAGING=2
+OPENSSL_VERSION=1.1.1b

+ 10 - 14
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/CMakeLists.txt

@@ -4,6 +4,7 @@ set(SOURCES
     index_set.cpp
     index_set.cpp
     list.cpp
     list.cpp
     object.cpp
     object.cpp
+    object_changeset.cpp
     object_schema.cpp
     object_schema.cpp
     object_store.cpp
     object_store.cpp
     results.cpp
     results.cpp
@@ -15,23 +16,23 @@ set(SOURCES
     impl/collection_notifier.cpp
     impl/collection_notifier.cpp
     impl/list_notifier.cpp
     impl/list_notifier.cpp
     impl/object_notifier.cpp
     impl/object_notifier.cpp
-    impl/primitive_list_notifier.cpp
     impl/realm_coordinator.cpp
     impl/realm_coordinator.cpp
     impl/results_notifier.cpp
     impl/results_notifier.cpp
     impl/transact_log_handler.cpp
     impl/transact_log_handler.cpp
     impl/weak_realm_notifier.cpp
     impl/weak_realm_notifier.cpp
+    util/scheduler.cpp
     util/uuid.cpp)
     util/uuid.cpp)
 
 
 set(HEADERS
 set(HEADERS
     binding_callback_thread_observer.hpp
     binding_callback_thread_observer.hpp
     collection_notifications.hpp
     collection_notifications.hpp
-    execution_context_id.hpp
     feature_checks.hpp
     feature_checks.hpp
     index_set.hpp
     index_set.hpp
     keypath_helpers.hpp
     keypath_helpers.hpp
     list.hpp
     list.hpp
     object.hpp
     object.hpp
     object_accessor.hpp
     object_accessor.hpp
+    object_changeset.hpp
     object_schema.hpp
     object_schema.hpp
     object_store.hpp
     object_store.hpp
     property.hpp
     property.hpp
@@ -52,21 +53,22 @@ set(HEADERS
     impl/notification_wrapper.hpp
     impl/notification_wrapper.hpp
     impl/object_accessor_impl.hpp
     impl/object_accessor_impl.hpp
     impl/object_notifier.hpp
     impl/object_notifier.hpp
-    impl/primitive_list_notifier.hpp
     impl/realm_coordinator.hpp
     impl/realm_coordinator.hpp
     impl/results_notifier.hpp
     impl/results_notifier.hpp
     impl/transact_log_handler.hpp
     impl/transact_log_handler.hpp
     impl/weak_realm_notifier.hpp
     impl/weak_realm_notifier.hpp
 
 
-    util/android/event_loop_signal.hpp
-    util/apple/event_loop_signal.hpp
-    util/generic/event_loop_signal.hpp
-    util/uv/event_loop_signal.hpp
+    util/android/scheduler.hpp
+    util/apple/scheduler.hpp
+    util/generic/scheduler.hpp
+    util/uv/scheduler.hpp
 
 
     util/aligned_union.hpp
     util/aligned_union.hpp
     util/atomic_shared_ptr.hpp
     util/atomic_shared_ptr.hpp
+    util/checked_mutex.hpp
+    util/copyable_atomic.hpp
     util/event_loop_dispatcher.hpp
     util/event_loop_dispatcher.hpp
-    util/event_loop_signal.hpp
+    util/scheduler.hpp
     util/tagged_bool.hpp
     util/tagged_bool.hpp
     util/uuid.hpp)
     util/uuid.hpp)
 
 
@@ -80,10 +82,6 @@ else()
     list(APPEND SOURCES impl/generic/external_commit_helper.cpp)
     list(APPEND SOURCES impl/generic/external_commit_helper.cpp)
 endif()
 endif()
 
 
-if(NOT APPLE AND NOT REALM_PLATFORM STREQUAL "Android")
-    list(APPEND SOURCES util/generic/event_loop_signal.cpp)
-endif()
-
 set(INCLUDE_DIRS
 set(INCLUDE_DIRS
     ${UV_INCLUDE_DIR}
     ${UV_INCLUDE_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR})
     ${CMAKE_CURRENT_SOURCE_DIR})
@@ -95,7 +93,6 @@ if(REALM_ENABLE_SYNC)
         sync/subscription_state.hpp
         sync/subscription_state.hpp
         sync/sync_config.hpp
         sync/sync_config.hpp
         sync/sync_manager.hpp
         sync/sync_manager.hpp
-        sync/sync_permission.hpp
         sync/sync_session.hpp
         sync/sync_session.hpp
         sync/sync_user.hpp
         sync/sync_user.hpp
         sync/impl/sync_client.hpp
         sync/impl/sync_client.hpp
@@ -107,7 +104,6 @@ if(REALM_ENABLE_SYNC)
         sync/partial_sync.cpp
         sync/partial_sync.cpp
         sync/sync_config.cpp
         sync/sync_config.cpp
         sync/sync_manager.cpp
         sync/sync_manager.cpp
-        sync/sync_permission.cpp
         sync/sync_session.cpp
         sync/sync_session.cpp
         sync/sync_user.cpp
         sync/sync_user.cpp
         sync/impl/sync_file.cpp
         sync/impl/sync_file.cpp

+ 8 - 13
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/binding_context.hpp

@@ -21,8 +21,11 @@
 
 
 #include "index_set.hpp"
 #include "index_set.hpp"
 
 
+#include <realm/keys.hpp>
+
 #include <memory>
 #include <memory>
 #include <tuple>
 #include <tuple>
+#include <unordered_map>
 #include <vector>
 #include <vector>
 
 
 namespace realm {
 namespace realm {
@@ -75,10 +78,6 @@ public:
 
 
     std::weak_ptr<Realm> realm;
     std::weak_ptr<Realm> realm;
 
 
-    // If the user adds a notification handler to the Realm, will it ever
-    // actually be called?
-    virtual bool can_deliver_notifications() const noexcept { return true; }
-
     // Called when the Realm is about to send notifications about Realm,
     // Called when the Realm is about to send notifications about Realm,
     // Collection or Object changes. This method will be called even if
     // Collection or Object changes. This method will be called even if
     // no notification callbacks have been registered.
     // no notification callbacks have been registered.
@@ -132,9 +131,6 @@ public:
 
 
     // Change information for a single field of a row
     // Change information for a single field of a row
     struct ColumnInfo {
     struct ColumnInfo {
-        // The index of this column prior to the changes in the tracked
-        // transaction, or -1 for newly inserted columns.
-        size_t initial_column_index = -1;
         // What kind of change occurred?
         // What kind of change occurred?
         // Always Set or None for everything but LinkList columns.
         // Always Set or None for everything but LinkList columns.
         enum class Kind {
         enum class Kind {
@@ -157,10 +153,9 @@ public:
     // The Realm parses the transaction log, and populates the `changes` vector
     // The Realm parses the transaction log, and populates the `changes` vector
     // in each ObserverState with information about what changes were made.
     // in each ObserverState with information about what changes were made.
     struct ObserverState {
     struct ObserverState {
-        // Initial table and row which is observed
-        // May be updated by row insertions and removals
-        size_t table_ndx;
-        size_t row_ndx;
+        // Table and row which is observed
+        realm::TableKey table_key;
+        int64_t obj_key;
 
 
         // Opaque userdata for the delegate's use
         // Opaque userdata for the delegate's use
         void* info;
         void* info;
@@ -168,12 +163,12 @@ public:
         // Populated with information about which columns were changed
         // Populated with information about which columns were changed
         // May be shorter than the actual number of columns if the later columns
         // May be shorter than the actual number of columns if the later columns
         // are not modified
         // are not modified
-        std::vector<ColumnInfo> changes;
+        std::unordered_map<int64_t, ColumnInfo> changes;
 
 
         // Simple lexographic ordering
         // Simple lexographic ordering
         friend bool operator<(ObserverState const& lft, ObserverState const& rgt)
         friend bool operator<(ObserverState const& lft, ObserverState const& rgt)
         {
         {
-            return std::tie(lft.table_ndx, lft.row_ndx) < std::tie(rgt.table_ndx, rgt.row_ndx);
+            return std::tie(lft.table_key, lft.obj_key) < std::tie(rgt.table_key, rgt.obj_key);
         }
         }
     };
     };
 };
 };

+ 2 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp

@@ -25,6 +25,7 @@
 #include <exception>
 #include <exception>
 #include <memory>
 #include <memory>
 #include <type_traits>
 #include <type_traits>
+#include <unordered_map>
 #include <vector>
 #include <vector>
 
 
 namespace realm {
 namespace realm {
@@ -86,7 +87,7 @@ struct CollectionChangeSet {
     std::vector<Move> moves;
     std::vector<Move> moves;
 
 
     // Per-column version of `modifications`
     // Per-column version of `modifications`
-    std::vector<IndexSet> columns;
+    std::unordered_map<int64_t, IndexSet> columns;
 
 
     bool empty() const noexcept
     bool empty() const noexcept
     {
     {

+ 3 - 3
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp

@@ -19,7 +19,7 @@
 #include "impl/external_commit_helper.hpp"
 #include "impl/external_commit_helper.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 
 
-#include <realm/group_shared_options.hpp>
+#include <realm/db_options.hpp>
 #include <realm/util/fifo_helper.hpp>
 #include <realm/util/fifo_helper.hpp>
 
 
 #include <asl.h>
 #include <asl.h>
@@ -109,7 +109,7 @@ ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
     // In order of priority we attempt to write the file in the following locations:
     // In order of priority we attempt to write the file in the following locations:
     //  1) Next to the Realm file itself
     //  1) Next to the Realm file itself
     //  2) A location defined by `Realm::Config::fifo_files_fallback_path`
     //  2) A location defined by `Realm::Config::fifo_files_fallback_path`
-    //  3) A location defined by `SharedGroupOptions::set_sys_tmp_dir()`
+    //  3) A location defined by `DBOptions::set_sys_tmp_dir()`
     //
     //
     // Core has a similar policy for its named pipes.
     // Core has a similar policy for its named pipes.
     //
     //
@@ -119,7 +119,7 @@ ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
 
 
     std::string path;
     std::string path;
     std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
     std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
-    std::string sys_temp_dir = util::normalize_dir(SharedGroupOptions::get_sys_tmp_dir());
+    std::string sys_temp_dir = util::normalize_dir(DBOptions::get_sys_tmp_dir());
 
 
     path = parent.get_path() + ".note";
     path = parent.get_path() + ".note";
     bool fifo_created = realm::util::try_create_fifo(path);
     bool fifo_created = realm::util::try_create_fifo(path);

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp

@@ -62,7 +62,6 @@ CFPtr<CFMutableDictionaryRef> build_search_dictionary(CFStringRef account, CFStr
 
 
     CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword);
     CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword);
     CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue);
     CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue);
-    CFDictionaryAddValue(d.get(), kSecAttrAccessible, kSecAttrAccessibleAlways);
     CFDictionaryAddValue(d.get(), kSecAttrAccount, account);
     CFDictionaryAddValue(d.get(), kSecAttrAccount, account);
     CFDictionaryAddValue(d.get(), kSecAttrService, service);
     CFDictionaryAddValue(d.get(), kSecAttrService, service);
 #if !TARGET_IPHONE_SIMULATOR
 #if !TARGET_IPHONE_SIMULATOR
@@ -97,6 +96,7 @@ util::Optional<std::vector<char>> get_key(CFStringRef account, CFStringRef servi
 void set_key(const std::vector<char>& key, CFStringRef account, CFStringRef service)
 void set_key(const std::vector<char>& key, CFStringRef account, CFStringRef service)
 {
 {
     auto search_dictionary = build_search_dictionary(account, service, none);
     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));
     auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast<const UInt8 *>(key.data()), key_size));
     if (!key_data)
     if (!key_data)
         throw std::bad_alloc();
         throw std::bad_alloc();

+ 112 - 326
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.cpp

@@ -19,7 +19,6 @@
 #include "impl/collection_change_builder.hpp"
 #include "impl/collection_change_builder.hpp"
 
 
 #include <realm/util/assert.hpp>
 #include <realm/util/assert.hpp>
-#include <algorithm>
 
 
 #include <algorithm>
 #include <algorithm>
 
 
@@ -50,15 +49,20 @@ void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c)
     verify();
     verify();
     c.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) {
     auto for_each_col = [&](auto&& f) {
         f(modifications, c.modifications);
         f(modifications, c.modifications);
         if (m_track_columns) {
         if (m_track_columns) {
-            if (columns.size() < c.columns.size())
-                columns.resize(c.columns.size());
-            else if (columns.size() > c.columns.size())
-                c.columns.resize(columns.size());
-            for (size_t i = 0; i < columns.size(); ++i)
-                f(columns[i], c.columns[i]);
+            for (auto col : col_keys)
+                f(columns[col], c.columns[col]);
         }
         }
     };
     };
 
 
@@ -153,32 +157,11 @@ void CollectionChangeBuilder::clean_up_stale_moves()
     }), end(moves));
     }), end(moves));
 }
 }
 
 
-void CollectionChangeBuilder::parse_complete()
-{
-    moves.reserve(m_move_mapping.size());
-    for (auto move : m_move_mapping) {
-        REALM_ASSERT_DEBUG(deletions.contains(move.second));
-        REALM_ASSERT_DEBUG(insertions.contains(move.first));
-        if (move.first == move.second) {
-            deletions.remove(move.second);
-            insertions.remove(move.first);
-        }
-        else
-            moves.push_back({move.second, move.first});
-    }
-    m_move_mapping.clear();
-    std::sort(begin(moves), end(moves),
-              [](auto const& a, auto const& b) { return a.from < b.from; });
-}
-
 void CollectionChangeBuilder::modify(size_t ndx, size_t col)
 void CollectionChangeBuilder::modify(size_t ndx, size_t col)
 {
 {
     modifications.add(ndx);
     modifications.add(ndx);
     if (!m_track_columns || col == IndexSet::npos)
     if (!m_track_columns || col == IndexSet::npos)
         return;
         return;
-
-    if (col >= columns.size())
-        columns.resize(col + 1);
     columns[col].add(ndx);
     columns[col].add(ndx);
 }
 }
 
 
@@ -188,7 +171,7 @@ void CollectionChangeBuilder::for_each_col(Func&& f)
     f(modifications);
     f(modifications);
     if (m_track_columns) {
     if (m_track_columns) {
         for (auto& col : columns)
         for (auto& col : columns)
-            f(col);
+            f(col.second);
     }
     }
 }
 }
 
 
@@ -206,24 +189,6 @@ void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_move
         if (move.to >= index)
         if (move.to >= index)
             move.to += count;
             move.to += count;
     }
     }
-
-    if (m_move_mapping.empty())
-        return;
-
-    // m_move_mapping is new_ndx -> old_ndx, so updating the keys requires
-    // deleting and re-inserting at the new index
-    std::vector<std::pair<size_t, size_t>> shifted;
-    for (auto it = m_move_mapping.begin(); it != m_move_mapping.end(); ) {
-        if (it->first >= index) {
-            shifted.emplace_back(it->first + count, it->second);
-            it = m_move_mapping.erase(it);
-        }
-        else {
-            ++it;
-        }
-    }
-    for (auto& pair : shifted)
-        m_move_mapping.insert(pair);
 }
 }
 
 
 void CollectionChangeBuilder::erase(size_t index)
 void CollectionChangeBuilder::erase(size_t index)
@@ -246,17 +211,14 @@ void CollectionChangeBuilder::erase(size_t index)
 
 
 void CollectionChangeBuilder::clear(size_t old_size)
 void CollectionChangeBuilder::clear(size_t old_size)
 {
 {
-    if (old_size != std::numeric_limits<size_t>::max()) {
-        for (auto range : deletions)
-            old_size += range.second - range.first;
-        for (auto range : insertions)
-            old_size -= range.second - range.first;
-    }
+    for (auto range : deletions)
+        old_size += range.second - range.first;
+    for (auto range : insertions)
+        old_size -= range.second - range.first;
 
 
     modifications.clear();
     modifications.clear();
     insertions.clear();
     insertions.clear();
     moves.clear();
     moves.clear();
-    m_move_mapping.clear();
     columns.clear();
     columns.clear();
     deletions.set(old_size);
     deletions.set(old_size);
 }
 }
@@ -308,174 +270,6 @@ void CollectionChangeBuilder::move(size_t from, size_t to)
     });
     });
 }
 }
 
 
-void CollectionChangeBuilder::move_over(size_t row_ndx, size_t last_row, bool track_moves)
-{
-    REALM_ASSERT(row_ndx <= last_row);
-    REALM_ASSERT(insertions.empty() || prev(insertions.end())->second - 1 <= last_row);
-    REALM_ASSERT(modifications.empty() || prev(modifications.end())->second - 1 <= last_row);
-
-    if (row_ndx == last_row) {
-        if (track_moves) {
-            auto shifted_from = insertions.erase_or_unshift(row_ndx);
-            if (shifted_from != IndexSet::npos)
-                deletions.add_shifted(shifted_from);
-            m_move_mapping.erase(row_ndx);
-        }
-        for_each_col([=](auto& col) { col.remove(row_ndx); });
-        return;
-    }
-
-    for_each_col([=](auto& col) {
-        bool modified = col.contains(last_row);
-        if (modified) {
-            col.remove(last_row);
-            col.add(row_ndx);
-        }
-        else
-            col.remove(row_ndx);
-    });
-
-    if (!track_moves)
-        return;
-
-    bool row_is_insertion = insertions.contains(row_ndx);
-    bool last_is_insertion = !insertions.empty() && prev(insertions.end())->second == last_row + 1;
-    REALM_ASSERT_DEBUG(insertions.empty() || prev(insertions.end())->second <= last_row + 1);
-
-    // Collapse A -> B, B -> C into a single A -> C move
-    bool last_was_already_moved = false;
-    if (last_is_insertion) {
-        auto it = m_move_mapping.find(last_row);
-        if (it != m_move_mapping.end() && it->first == last_row) {
-            m_move_mapping[row_ndx] = it->second;
-            m_move_mapping.erase(it);
-            last_was_already_moved = true;
-        }
-    }
-
-    // Remove moves to the row being deleted
-    if (row_is_insertion && !last_was_already_moved) {
-        auto it = m_move_mapping.find(row_ndx);
-        if (it != m_move_mapping.end() && it->first == row_ndx)
-            m_move_mapping.erase(it);
-    }
-
-    // Don't report deletions/moves if last_row is newly inserted
-    if (last_is_insertion) {
-        insertions.remove(last_row);
-    }
-    // If it was previously moved, the unshifted source row has already been marked as deleted
-    else if (!last_was_already_moved) {
-        auto shifted_last_row = insertions.unshift(last_row);
-        shifted_last_row = deletions.add_shifted(shifted_last_row);
-        m_move_mapping[row_ndx] = shifted_last_row;
-    }
-
-    // Don't mark the moved-over row as deleted if it was a new insertion
-    if (!row_is_insertion) {
-        deletions.add_shifted(insertions.unshift(row_ndx));
-        insertions.add(row_ndx);
-    }
-    verify();
-}
-
-void CollectionChangeBuilder::swap(size_t ndx_1, size_t ndx_2, bool track_moves)
-{
-    REALM_ASSERT(ndx_1 != ndx_2);
-    // The order of the two indices doesn't matter semantically, but making them
-    // consistent simplifies the logic
-    if (ndx_1 > ndx_2)
-        std::swap(ndx_1, ndx_2);
-
-    for_each_col([=](auto& col) {
-        bool row_1_modified = col.contains(ndx_1);
-        bool row_2_modified = col.contains(ndx_2);
-        if (row_1_modified != row_2_modified) {
-            if (row_1_modified) {
-                col.remove(ndx_1);
-                col.add(ndx_2);
-            }
-            else {
-                col.remove(ndx_2);
-                col.add(ndx_1);
-            }
-        }
-    });
-
-    if (!track_moves)
-        return;
-
-    auto update_move = [&](auto existing_it, auto ndx_1, auto ndx_2) {
-        // update the existing move to ndx_2 to point at ndx_1
-        auto original = existing_it->second;
-        m_move_mapping.erase(existing_it);
-        m_move_mapping[ndx_1] = original;
-
-        // add a move from 1 -> 2 unless 1 was a new insertion
-        if (!insertions.contains(ndx_1)) {
-            m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1));
-            insertions.add(ndx_1);
-        }
-        REALM_ASSERT_DEBUG(insertions.contains(ndx_2));
-    };
-
-    auto move_1 = m_move_mapping.find(ndx_1);
-    auto move_2 = m_move_mapping.find(ndx_2);
-    bool have_move_1 = move_1 != end(m_move_mapping) && move_1->first == ndx_1;
-    bool have_move_2 = move_2 != end(m_move_mapping) && move_2->first == ndx_2;
-    if (have_move_1 && have_move_2) {
-        // both are already moves, so just swap the destinations
-        std::swap(move_1->second, move_2->second);
-    }
-    else if (have_move_1) {
-        update_move(move_1, ndx_2, ndx_1);
-    }
-    else if (have_move_2) {
-        update_move(move_2, ndx_1, ndx_2);
-    }
-    else {
-        // ndx_2 needs to be done before 1 to avoid incorrect shifting
-        if (!insertions.contains(ndx_2)) {
-            m_move_mapping[ndx_1] = deletions.add_shifted(insertions.unshift(ndx_2));
-            insertions.add(ndx_2);
-        }
-        if (!insertions.contains(ndx_1)) {
-            m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1));
-            insertions.add(ndx_1);
-        }
-    }
-}
-
-void CollectionChangeBuilder::subsume(size_t old_ndx, size_t new_ndx, bool track_moves)
-{
-    REALM_ASSERT(old_ndx != new_ndx);
-
-    for_each_col([=](auto& col) {
-        if (col.contains(old_ndx)) {
-            col.add(new_ndx);
-        }
-    });
-
-    if (!track_moves)
-        return;
-
-    REALM_ASSERT_DEBUG(insertions.contains(new_ndx));
-    REALM_ASSERT_DEBUG(!m_move_mapping.count(new_ndx));
-
-    // If the source row was already moved, update the existing move
-    auto it = m_move_mapping.find(old_ndx);
-    if (it != m_move_mapping.end() && it->first == old_ndx) {
-        m_move_mapping[new_ndx] = it->second;
-        m_move_mapping.erase(it);
-    }
-    // otherwise add a new move unless it was a new insertion
-    else if (!insertions.contains(old_ndx)) {
-        m_move_mapping[new_ndx] = deletions.shift(insertions.unshift(old_ndx));
-    }
-
-    verify();
-}
-
 void CollectionChangeBuilder::verify()
 void CollectionChangeBuilder::verify()
 {
 {
 #ifdef REALM_DEBUG
 #ifdef REALM_DEBUG
@@ -486,32 +280,14 @@ void CollectionChangeBuilder::verify()
 #endif
 #endif
 }
 }
 
 
-void CollectionChangeBuilder::insert_column(size_t ndx)
-{
-    if (ndx < columns.size())
-        columns.insert(columns.begin() + ndx, IndexSet{});
-}
-
-void CollectionChangeBuilder::move_column(size_t from, size_t to)
-{
-    if (from >= columns.size() && to >= columns.size())
-        return;
-    if (from >= columns.size() || to >= columns.size())
-        columns.resize(std::max(from, to) + 1);
-    if (from < to)
-        std::rotate(begin(columns) + from, begin(columns) + from + 1, begin(columns) + to + 1);
-    else
-        std::rotate(begin(columns) + to, begin(columns) + from, begin(columns) + from + 1);
-}
-
 namespace {
 namespace {
 struct RowInfo {
 struct RowInfo {
-    size_t row_index;
+    int64_t key;
     size_t prev_tv_index;
     size_t prev_tv_index;
     size_t tv_index;
     size_t tv_index;
-    size_t shifted_tv_index;
 };
 };
 
 
+#if 0 // FIXME: this is applicable to backlinks still
 // Calculates the insertions/deletions required for a query on a table without
 // 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
 // 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
 // match the query (but not outright deleted rows, which are filtered out long
@@ -522,10 +298,10 @@ struct RowInfo {
 // produce correct results even for the scenarios where this function is used.
 // produce correct results even for the scenarios where this function is used.
 // However, this function has asymptotically better worst-case performance and
 // However, this function has asymptotically better worst-case performance and
 // extremely cheap best-case performance, and is guaranteed to produce a minimal
 // extremely cheap best-case performance, and is guaranteed to produce a minimal
-// diff when the only row moves are due to move_last_over().
-void calculate_moves_unsorted(std::vector<RowInfo>& new_rows, IndexSet& removed,
-                              IndexSet const& move_candidates,
-                              CollectionChangeSet& changeset)
+// 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()
     // 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.
     // is always the row immediately after the last row which was not moved.
@@ -543,7 +319,7 @@ void calculate_moves_unsorted(std::vector<RowInfo>& new_rows, IndexSet& removed,
         // First check if this row even could have moved. If it can't, just
         // 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
         // treat it as a match and move on, and we'll handle the row we were
         // expecting when we hit it later.
         // expecting when we hit it later.
-        if (!move_candidates.contains(row.row_index)) {
+        if (!move_candidates.contains(row.key)) {
             expected = row.shifted_tv_index + 1;
             expected = row.shifted_tv_index + 1;
             continue;
             continue;
         }
         }
@@ -564,12 +340,13 @@ void calculate_moves_unsorted(std::vector<RowInfo>& new_rows, IndexSet& removed,
         removed.add(row.prev_tv_index);
         removed.add(row.prev_tv_index);
     }
     }
 }
 }
+#endif
 
 
 class LongestCommonSubsequenceCalculator {
 class LongestCommonSubsequenceCalculator {
 public:
 public:
-    // A pair of an index in the table and an index in the table view
+    // A pair of an object key and an index in the table view
     struct Row {
     struct Row {
-        size_t row_index;
+        int64_t key;
         size_t tv_index;
         size_t tv_index;
     };
     };
 
 
@@ -600,7 +377,7 @@ private:
     IndexSet const& m_modified;
     IndexSet const& m_modified;
 
 
     // The two arrays of rows being diffed
     // The two arrays of rows being diffed
-    // a is sorted by tv_index, b is sorted by row_index
+    // a is sorted by tv_index, b is sorted by key
     std::vector<Row> &a, &b;
     std::vector<Row> &a, &b;
 
 
     // Find the longest matching range in (a + begin1, a + end1) and (b + begin2, b + end2)
     // Find the longest matching range in (a + begin1, a + end1) and (b + begin2, b + end2)
@@ -635,14 +412,14 @@ private:
         // Iterate over each `j` which has the same row index as a[i] and falls
         // Iterate over each `j` which has the same row index as a[i] and falls
         // within the range begin2 <= j < end2
         // within the range begin2 <= j < end2
         auto for_each_b_match = [&](size_t i, auto&& f) {
         auto for_each_b_match = [&](size_t i, auto&& f) {
-            size_t ai = a[i].row_index;
+            auto ai = a[i].key;
             // Find the TV indicies at which this row appears in the new results
             // 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
             // There should always be at least one (or it would have been
             // filtered out earlier), but there can be multiple if there are dupes
             // filtered out earlier), but there can be multiple if there are dupes
             auto it = lower_bound(begin(b), end(b), ai,
             auto it = lower_bound(begin(b), end(b), ai,
-                                  [](auto lft, auto rgt) { return lft.row_index < rgt; });
-            REALM_ASSERT(it != end(b) && it->row_index == ai);
-            for (; it != end(b) && it->row_index == ai; ++it) {
+                                  [](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;
                 size_t j = it->tv_index;
                 if (j < begin2)
                 if (j < begin2)
                     continue;
                     continue;
@@ -710,18 +487,17 @@ void calculate_moves_sorted(std::vector<RowInfo>& rows, CollectionChangeSet& cha
     std::vector<LongestCommonSubsequenceCalculator::Row> a, b;
     std::vector<LongestCommonSubsequenceCalculator::Row> a, b;
 
 
     a.reserve(rows.size());
     a.reserve(rows.size());
-    for (auto& row : rows) {
-        a.push_back({row.row_index, row.prev_tv_index});
-    }
+    for (auto& row : rows)
+        a.push_back({row.key, row.prev_tv_index});
     std::sort(begin(a), end(a), [](auto lft, auto rgt) {
     std::sort(begin(a), end(a), [](auto lft, auto rgt) {
-        return std::tie(lft.tv_index, lft.row_index) < std::tie(rgt.tv_index, rgt.row_index);
+        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
     // 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
     // actually differ in `b`, and skip everything else if there aren't any
     size_t first_difference = IndexSet::npos;
     size_t first_difference = IndexSet::npos;
     for (size_t i = 0; i < a.size(); ++i) {
     for (size_t i = 0; i < a.size(); ++i) {
-        if (a[i].row_index != rows[i].row_index) {
+        if (a[i].key != rows[i].key) {
             first_difference = i;
             first_difference = i;
             break;
             break;
         }
         }
@@ -729,12 +505,12 @@ void calculate_moves_sorted(std::vector<RowInfo>& rows, CollectionChangeSet& cha
     if (first_difference == IndexSet::npos)
     if (first_difference == IndexSet::npos)
         return;
         return;
 
 
-    // Note that `b` is sorted by row_index, while `a` is sorted by tv_index
+    // Note that `b` is sorted by key, while `a` is sorted by tv_index
     b.reserve(rows.size());
     b.reserve(rows.size());
     for (size_t i = 0; i < rows.size(); ++i)
     for (size_t i = 0; i < rows.size(); ++i)
-        b.push_back({rows[i].row_index, i});
+        b.push_back({rows[i].key, i});
     std::sort(begin(b), end(b), [](auto lft, auto rgt) {
     std::sort(begin(b), end(b), [](auto lft, auto rgt) {
-        return std::tie(lft.row_index, lft.tv_index) < std::tie(rgt.row_index, rgt.tv_index);
+        return std::tie(lft.key, lft.tv_index) < std::tie(rgt.key, rgt.tv_index);
     });
     });
 
 
     // Calculate the LCS of the two sequences
     // Calculate the LCS of the two sequences
@@ -753,61 +529,51 @@ void calculate_moves_sorted(std::vector<RowInfo>& rows, CollectionChangeSet& cha
     }
     }
 }
 }
 
 
-} // Anonymous namespace
-
-CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector<size_t> const& prev_rows,
-                                                           std::vector<size_t> const& next_rows,
-                                                           std::function<bool (size_t)> row_did_change,
-                                                           util::Optional<IndexSet> const& move_candidates)
+template<typename T>
+void verify_changeset(std::vector<T> const& prev_rows,
+                      std::vector<T> const& next_rows,
+                      CollectionChangeBuilder const& changeset)
 {
 {
-    REALM_ASSERT_DEBUG(!move_candidates || std::is_sorted(begin(next_rows), end(next_rows)));
-
-    CollectionChangeBuilder ret;
+#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);
+        }
 
 
-    size_t deleted = 0;
-    std::vector<RowInfo> old_rows;
-    old_rows.reserve(prev_rows.size());
-    for (size_t i = 0; i < prev_rows.size(); ++i) {
-        if (prev_rows[i] == IndexSet::npos) {
-            ++deleted;
-            ret.deletions.add(i);
+        for (auto i : changeset.insertions.as_indexes()) {
+            rows.insert(rows.begin() + i, next_rows[i]);
         }
         }
-        else
-            old_rows.push_back({prev_rows[i], IndexSet::npos, i, i - deleted});
-    }
-    std::sort(begin(old_rows), end(old_rows), [](auto& lft, auto& rgt) {
-        return lft.row_index < rgt.row_index;
-    });
 
 
-    std::vector<RowInfo> new_rows;
-    new_rows.reserve(next_rows.size());
-    for (size_t i = 0; i < next_rows.size(); ++i) {
-        new_rows.push_back({next_rows[i], IndexSet::npos, i, 0});
+        REALM_ASSERT(rows == next_rows);
     }
     }
-    std::sort(begin(new_rows), end(new_rows), [](auto& lft, auto& rgt) {
-        return lft.row_index < rgt.row_index;
-    });
-
-    // Don't add rows which were modified to not match the query to `deletions`
-    // immediately because the unsorted move logic needs to be able to
-    // distinguish them from rows which were outright deleted
-    IndexSet removed;
+#else
+    static_cast<void>(prev_rows);
+    static_cast<void>(next_rows);
+    static_cast<void>(changeset);
+#endif
+}
 
 
-    // Now that our old and new sets of rows are sorted by row index, we can
+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
     // 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
     // in both, or mark them as inserted/deleted if they appear only in one
     size_t i = 0, j = 0;
     size_t i = 0, j = 0;
     while (i < old_rows.size() && j < new_rows.size()) {
     while (i < old_rows.size() && j < new_rows.size()) {
         auto old_index = old_rows[i];
         auto old_index = old_rows[i];
-        auto new_index = new_rows[j];
-        if (old_index.row_index == new_index.row_index) {
-            new_rows[j].prev_tv_index = old_rows[i].tv_index;
-            new_rows[j].shifted_tv_index = old_rows[i].shifted_tv_index;
+        auto& new_index = new_rows[j];
+        if (old_index.key == new_index.key) {
+            new_index.prev_tv_index = old_rows[i].tv_index;
             ++i;
             ++i;
             ++j;
             ++j;
         }
         }
-        else if (old_index.row_index < new_index.row_index) {
-            removed.add(old_index.tv_index);
+        else if (old_index.key < new_index.key) {
+            ret.deletions.add(old_index.tv_index);
             ++i;
             ++i;
         }
         }
         else {
         else {
@@ -817,7 +583,7 @@ CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector<size_t> c
     }
     }
 
 
     for (; i < old_rows.size(); ++i)
     for (; i < old_rows.size(); ++i)
-        removed.add(old_rows[i].tv_index);
+        ret.deletions.add(old_rows[i].tv_index);
     for (; j < new_rows.size(); ++j)
     for (; j < new_rows.size(); ++j)
         ret.insertions.add(new_rows[j].tv_index);
         ret.insertions.add(new_rows[j].tv_index);
 
 
@@ -830,37 +596,57 @@ CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector<size_t> c
               [](auto& lft, auto& rgt) { return lft.tv_index < rgt.tv_index; });
               [](auto& lft, auto& rgt) { return lft.tv_index < rgt.tv_index; });
 
 
     for (auto& row : new_rows) {
     for (auto& row : new_rows) {
-        if (row_did_change(row.row_index)) {
+        if (key_did_change(row.key)) {
             ret.modifications.add(row.tv_index);
             ret.modifications.add(row.tv_index);
         }
         }
     }
     }
 
 
-    if (move_candidates) {
-        calculate_moves_unsorted(new_rows, removed, *move_candidates, ret);
-    }
-    else {
+    if (!in_table_order)
         calculate_moves_sorted(new_rows, ret);
         calculate_moves_sorted(new_rows, ret);
-    }
-    ret.deletions.add(removed);
-    ret.verify();
+}
 
 
-#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(ret.deletions.end());
-        auto end = util::make_reverse_iterator(ret.deletions.begin());
-        for (; it != end; ++it) {
-            rows.erase(rows.begin() + it->first, rows.begin() + it->second);
-        }
+} // Anonymous namespace
 
 
-        for (auto i : ret.insertions.as_indexes()) {
-            rows.insert(rows.begin() + i, next_rows[i]);
-        }
+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)
+{
 
 
-        REALM_ASSERT(rows == next_rows);
-    }
-#endif
+    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;
     return ret;
 }
 }
 
 

+ 10 - 21
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_change_builder.hpp

@@ -21,12 +21,15 @@
 
 
 #include "collection_notifications.hpp"
 #include "collection_notifications.hpp"
 
 
-#include <realm/util/optional.hpp>
+#include <realm/keys.hpp>
 
 
-#include <unordered_map>
+#include <functional>
+#include <unordered_set>
+#include <vector>
 
 
 namespace realm {
 namespace realm {
 namespace _impl {
 namespace _impl {
+
 class CollectionChangeBuilder : public CollectionChangeSet {
 class CollectionChangeBuilder : public CollectionChangeSet {
 public:
 public:
     CollectionChangeBuilder(CollectionChangeBuilder const&) = default;
     CollectionChangeBuilder(CollectionChangeBuilder const&) = default;
@@ -41,13 +44,13 @@ public:
 
 
     // Calculate where rows need to be inserted or deleted from old_rows to turn
     // 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
     // it into new_rows, and check all matching rows for modifications
-    // If `move_candidates` is supplied they it will be used to do more accurate
-    // determination of which rows moved. This is only supported when the rows
-    // are in table order (i.e. not sorted or from a LinkList)
+    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,
     static CollectionChangeBuilder calculate(std::vector<size_t> const& old_rows,
                                              std::vector<size_t> const& new_rows,
                                              std::vector<size_t> const& new_rows,
-                                             std::function<bool (size_t)> row_did_change,
-                                             util::Optional<IndexSet> const& move_candidates = util::none);
+                                             std::function<bool (int64_t)> key_did_change);
 
 
     // generic operations {
     // generic operations {
     CollectionChangeSet finalize() &&;
     CollectionChangeSet finalize() &&;
@@ -64,21 +67,7 @@ public:
     void move(size_t from, size_t to);
     void move(size_t from, size_t to);
     // }
     // }
 
 
-    // operations only implemented for Row semantics {
-    void move_over(size_t ndx, size_t last_ndx, bool track_moves=true);
-    // must be followed by move_over(old_ndx, ...)
-    // precondition: `new_ndx` must be a new insertion
-    void subsume(size_t old_ndx, size_t new_ndx, bool track_moves=true);
-    void swap(size_t ndx_1, size_t ndx_2, bool track_moves=true);
-
-    void parse_complete();
-    // }
-
-    void insert_column(size_t ndx);
-    void move_column(size_t from, size_t to);
-
 private:
 private:
-    std::unordered_map<size_t, size_t> m_move_mapping;
     bool m_track_columns = true;
     bool m_track_columns = true;
 
 
     template<typename Func>
     template<typename Func>

+ 105 - 102
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.cpp

@@ -21,15 +21,34 @@
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
-#include <realm/group_shared.hpp>
-#include <realm/link_view.hpp>
+#include <realm/db.hpp>
 
 
 using namespace realm;
 using namespace realm;
 using namespace realm::_impl;
 using namespace realm::_impl;
 
 
-std::function<bool (size_t)>
+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,
 CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info,
-                                             Table const& root_table)
+                                             ConstTableRef root_table)
 {
 {
     if (info.schema_changed)
     if (info.schema_changed)
         set_table(root_table);
         set_table(root_table);
@@ -38,26 +57,24 @@ CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info,
     // actually modified. This can be false if there were only insertions, or
     // 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
     // deletions which were not linked to by any row in the linking table
     auto table_modified = [&](auto& tbl) {
     auto table_modified = [&](auto& tbl) {
-        return tbl.table_ndx < info.tables.size()
-            && !info.tables[tbl.table_ndx].modifications.empty();
+        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)) {
     if (!any_of(begin(m_related_tables), end(m_related_tables), table_modified)) {
-        return [](size_t) { return false; };
+        return [](ObjectChangeSet::ObjectKeyType) { return false; };
     }
     }
     if (m_related_tables.size() == 1) {
     if (m_related_tables.size() == 1) {
-        auto& modifications = info.tables[m_related_tables[0].table_ndx].modifications;
-        return [&](size_t row) { return modifications.contains(row); };
+        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);
+    return DeepChangeChecker(info, *root_table, m_related_tables);
 }
 }
 
 
 void DeepChangeChecker::find_related_tables(std::vector<RelatedTable>& out, Table const& table)
 void DeepChangeChecker::find_related_tables(std::vector<RelatedTable>& out, Table const& table)
 {
 {
-    auto table_ndx = table.get_index_in_group();
-    if (table_ndx == npos)
-        return;
-    if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_ndx == table_ndx; }))
+    auto table_key = table.get_key();
+    if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_key == table_key; }))
         return;
         return;
 
 
     // We need to add this table to `out` before recurring so that the check
     // We need to add this table to `out` before recurring so that the check
@@ -65,13 +82,13 @@ void DeepChangeChecker::find_related_tables(std::vector<RelatedTable>& out, Tabl
     // because the recursive calls may resize `out`, so instead look it up by
     // because the recursive calls may resize `out`, so instead look it up by
     // index every time
     // index every time
     size_t out_index = out.size();
     size_t out_index = out.size();
-    out.push_back({table_ndx, {}});
+    out.push_back({table_key, {}});
 
 
-    for (size_t i = 0, count = table.get_column_count(); i != count; ++i) {
-        auto type = table.get_column_type(i);
+    for (auto col_key : table.get_column_keys()) {
+        auto type = table.get_column_type(col_key);
         if (type == type_Link || type == type_LinkList) {
         if (type == type_Link || type == type_LinkList) {
-            out[out_index].links.push_back({i, type == type_LinkList});
-            find_related_tables(out, *table.get_link_target(i));
+            out[out_index].links.push_back({col_key.value, type == type_LinkList});
+            find_related_tables(out, *table.get_link_target(col_key));
         }
         }
     }
     }
 }
 }
@@ -81,60 +98,61 @@ DeepChangeChecker::DeepChangeChecker(TransactionChangeInfo const& info,
                                      std::vector<RelatedTable> const& related_tables)
                                      std::vector<RelatedTable> const& related_tables)
 : m_info(info)
 : m_info(info)
 , m_root_table(root_table)
 , m_root_table(root_table)
-, m_root_table_ndx(root_table.get_index_in_group())
-, m_root_modifications(m_root_table_ndx < info.tables.size() ? &info.tables[m_root_table_ndx].modifications : nullptr)
+, 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)
 , m_related_tables(related_tables)
 {
 {
 }
 }
 
 
-bool DeepChangeChecker::check_outgoing_links(size_t table_ndx,
-                                             Table const& table,
-                                             size_t row_ndx, size_t depth)
+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 it = find_if(begin(m_related_tables), end(m_related_tables),
-                      [&](auto&& tbl) { return tbl.table_ndx == table_ndx; });
+                      [&](auto&& tbl) { return tbl.table_key == table_key; });
     if (it == m_related_tables.end())
     if (it == m_related_tables.end())
         return false;
         return false;
+    if (it->links.empty())
+        return false;
 
 
     // Check if we're already checking if the destination of the link is
     // Check if we're already checking if the destination of the link is
     // modified, and if not add it to the stack
     // modified, and if not add it to the stack
-    auto already_checking = [&](size_t col) {
+    auto already_checking = [&](int64_t col) {
         auto end = m_current_path.begin() + depth;
         auto end = m_current_path.begin() + depth;
         auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) {
         auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) {
-            return p.table == table_ndx && p.row == row_ndx && p.col == col;
+            return p.obj_key == obj_key && p.col_key == col;
         });
         });
         if (match != end) {
         if (match != end) {
             for (; match < end; ++match) match->depth_exceeded = true;
             for (; match < end; ++match) match->depth_exceeded = true;
             return true;
             return true;
         }
         }
-        m_current_path[depth] = {table_ndx, row_ndx, col, false};
+        m_current_path[depth] = {obj_key, col, false};
         return false;
         return false;
     };
     };
 
 
+    ConstObj obj = table.get_object(ObjKey(obj_key));
     auto linked_object_changed = [&](OutgoingLink const& link) {
     auto linked_object_changed = [&](OutgoingLink const& link) {
-        if (already_checking(link.col_ndx))
+        if (already_checking(link.col_key))
             return false;
             return false;
         if (!link.is_list) {
         if (!link.is_list) {
-            if (table.is_null_link(link.col_ndx, row_ndx))
+            if (obj.is_null(ColKey(link.col_key)))
                 return false;
                 return false;
-            auto dst = table.get_link(link.col_ndx, row_ndx);
-            return check_row(*table.get_link_target(link.col_ndx), dst, depth + 1);
+            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(link.col_ndx);
-        auto lvr = table.get_linklist(link.col_ndx, row_ndx);
-        for (size_t j = 0, size = lvr->size(); j < size; ++j) {
-            size_t dst = lvr->get(j).get_index();
-            if (check_row(target, dst, depth + 1))
-                return true;
-        }
-        return false;
+        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);
     return std::any_of(begin(it->links), end(it->links), linked_object_changed);
 }
 }
 
 
-bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth)
+bool DeepChangeChecker::check_row(Table const& table, ObjKeyType key, size_t depth)
 {
 {
     // Arbitrary upper limit on the maximum depth to search
     // Arbitrary upper limit on the maximum depth to search
     if (depth >= m_current_path.size()) {
     if (depth >= m_current_path.size()) {
@@ -145,31 +163,33 @@ bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth)
         return false;
         return false;
     }
     }
 
 
-    size_t table_ndx = table.get_index_in_group();
-    if (depth > 0 && table_ndx < m_info.tables.size() && m_info.tables[table_ndx].modifications.contains(idx))
-        return true;
-
-    if (m_not_modified.size() <= table_ndx)
-        m_not_modified.resize(table_ndx + 1);
-    if (m_not_modified[table_ndx].contains(idx))
+    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;
         return false;
 
 
-    bool ret = check_outgoing_links(table_ndx, table, idx, depth);
+    bool ret = check_outgoing_links(table_key, table, key, depth);
     if (!ret && (depth == 0 || !m_current_path[depth - 1].depth_exceeded))
     if (!ret && (depth == 0 || !m_current_path[depth - 1].depth_exceeded))
-        m_not_modified[table_ndx].add(idx);
+        not_modified.insert(key);
     return ret;
     return ret;
 }
 }
 
 
-bool DeepChangeChecker::operator()(size_t ndx)
+bool DeepChangeChecker::operator()(ObjKeyType key)
 {
 {
-    if (m_root_modifications && m_root_modifications->contains(ndx))
+    if (m_root_object_changes && m_root_object_changes->modifications_contains(key))
         return true;
         return true;
-    return check_row(m_root_table, ndx, 0);
+    return check_row(m_root_table, key, 0);
 }
 }
 
 
 CollectionNotifier::CollectionNotifier(std::shared_ptr<Realm> realm)
 CollectionNotifier::CollectionNotifier(std::shared_ptr<Realm> realm)
 : m_realm(std::move(realm))
 : m_realm(std::move(realm))
-, m_sg_version(Realm::Internal::get_shared_group(*m_realm)->get_version_of_current_transaction())
+, m_sg_version(Realm::Internal::get_transaction(*m_realm).get_version_of_current_transaction())
 {
 {
 }
 }
 
 
@@ -180,11 +200,16 @@ CollectionNotifier::~CollectionNotifier()
     unregister();
     unregister();
 }
 }
 
 
+void CollectionNotifier::release_data() noexcept
+{
+    m_sg = nullptr;
+}
+
 uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback)
 uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback)
 {
 {
     m_realm->verify_thread();
     m_realm->verify_thread();
 
 
-    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    util::CheckedLockGuard lock(m_callback_mutex);
     auto token = m_next_token++;
     auto token = m_next_token++;
     m_callbacks.push_back({std::move(callback), {}, {}, token, false, false});
     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
     if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
@@ -200,7 +225,7 @@ void CollectionNotifier::remove_callback(uint64_t token)
     // it could cause user code to be called
     // it could cause user code to be called
     Callback old;
     Callback old;
     {
     {
-        std::lock_guard<std::mutex> lock(m_callback_mutex);
+        util::CheckedLockGuard lock(m_callback_mutex);
         auto it = find_callback(token);
         auto it = find_callback(token);
         if (it == end(m_callbacks)) {
         if (it == end(m_callbacks)) {
             return;
             return;
@@ -229,7 +254,7 @@ void CollectionNotifier::suppress_next_notification(uint64_t token)
         m_realm->verify_in_write();
         m_realm->verify_in_write();
     }
     }
 
 
-    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    util::CheckedLockGuard lock(m_callback_mutex);
     auto it = find_callback(token);
     auto it = find_callback(token);
     if (it != end(m_callbacks)) {
     if (it != end(m_callbacks)) {
         it->skip_next = true;
         it->skip_next = true;
@@ -264,10 +289,10 @@ std::unique_lock<std::mutex> CollectionNotifier::lock_target()
     return std::unique_lock<std::mutex>{m_realm_mutex};
     return std::unique_lock<std::mutex>{m_realm_mutex};
 }
 }
 
 
-void CollectionNotifier::set_table(Table const& table)
+void CollectionNotifier::set_table(ConstTableRef table)
 {
 {
     m_related_tables.clear();
     m_related_tables.clear();
-    DeepChangeChecker::find_related_tables(m_related_tables, table);
+    DeepChangeChecker::find_related_tables(m_related_tables, *table);
 }
 }
 
 
 void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info)
 void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info)
@@ -276,14 +301,9 @@ void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info)
         return;
         return;
     }
     }
 
 
-    auto max = max_element(begin(m_related_tables), end(m_related_tables),
-                           [](auto&& a, auto&& b) { return a.table_ndx < b.table_ndx; });
-
-    if (max->table_ndx >= info.table_modifications_needed.size())
-        info.table_modifications_needed.resize(max->table_ndx + 1, false);
-    for (auto& tbl : m_related_tables) {
-        info.table_modifications_needed[tbl.table_ndx] = true;
-    }
+    info.tables.reserve(m_related_tables.size());
+    for (auto& tbl : m_related_tables)
+        info.tables[tbl.table_key.value];
 }
 }
 
 
 void CollectionNotifier::prepare_handover()
 void CollectionNotifier::prepare_handover()
@@ -291,10 +311,12 @@ void CollectionNotifier::prepare_handover()
     REALM_ASSERT(m_sg);
     REALM_ASSERT(m_sg);
     m_sg_version = m_sg->get_version_of_current_transaction();
     m_sg_version = m_sg->get_version_of_current_transaction();
     do_prepare_handover(*m_sg);
     do_prepare_handover(*m_sg);
+    add_changes(std::move(m_change));
+    REALM_ASSERT(m_change.empty());
     m_has_run = true;
     m_has_run = true;
 
 
 #ifdef REALM_DEBUG
 #ifdef REALM_DEBUG
-    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    util::CheckedLockGuard lock(m_callback_mutex);
     for (auto& callback : m_callbacks)
     for (auto& callback : m_callbacks)
         REALM_ASSERT(!callback.skip_next);
         REALM_ASSERT(!callback.skip_next);
 #endif
 #endif
@@ -311,7 +333,7 @@ void CollectionNotifier::before_advance()
         // acquire a local reference to the callback so that removing the
         // acquire a local reference to the callback so that removing the
         // callback from within it can't result in a dangling pointer
         // callback from within it can't result in a dangling pointer
         auto cb = callback.fn;
         auto cb = callback.fn;
-        lock.unlock();
+        lock.unlock_unchecked();
         cb.before(changes);
         cb.before(changes);
     });
     });
 }
 }
@@ -328,7 +350,7 @@ void CollectionNotifier::after_advance()
         // acquire a local reference to the callback so that removing the
         // acquire a local reference to the callback so that removing the
         // callback from within it can't result in a dangling pointer
         // callback from within it can't result in a dangling pointer
         auto cb = callback.fn;
         auto cb = callback.fn;
-        lock.unlock();
+        lock.unlock_unchecked();
         cb.after(changes);
         cb.after(changes);
     });
     });
 }
 }
@@ -344,7 +366,7 @@ void CollectionNotifier::deliver_error(std::exception_ptr error)
         // callback from within it can't result in a dangling pointer
         // callback from within it can't result in a dangling pointer
         auto cb = std::move(callback.fn);
         auto cb = std::move(callback.fn);
         auto token = callback.token;
         auto token = callback.token;
-        lock.unlock();
+        lock.unlock_unchecked();
         cb.error(error);
         cb.error(error);
 
 
         // We never want to call the callback again after this, so just remove it
         // We never want to call the callback again after this, so just remove it
@@ -362,7 +384,7 @@ bool CollectionNotifier::package_for_delivery()
 {
 {
     if (!prepare_to_deliver())
     if (!prepare_to_deliver())
         return false;
         return false;
-    std::lock_guard<std::mutex> l(m_callback_mutex);
+    util::CheckedLockGuard lock(m_callback_mutex);
     for (auto& callback : m_callbacks)
     for (auto& callback : m_callbacks)
         callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize();
         callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize();
     m_callback_count = m_callbacks.size();
     m_callback_count = m_callbacks.size();
@@ -372,40 +394,31 @@ bool CollectionNotifier::package_for_delivery()
 template<typename Fn>
 template<typename Fn>
 void CollectionNotifier::for_each_callback(Fn&& fn)
 void CollectionNotifier::for_each_callback(Fn&& fn)
 {
 {
-    std::unique_lock<std::mutex> callback_lock(m_callback_mutex);
+    util::CheckedUniqueLock callback_lock(m_callback_mutex);
     REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
     REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
     for (++m_callback_index; m_callback_index < m_callback_count; ++m_callback_index) {
     for (++m_callback_index; m_callback_index < m_callback_count; ++m_callback_index) {
         fn(callback_lock, m_callbacks[m_callback_index]);
         fn(callback_lock, m_callbacks[m_callback_index]);
         if (!callback_lock.owns_lock())
         if (!callback_lock.owns_lock())
-            callback_lock.lock();
+            callback_lock.lock_unchecked();
     }
     }
 
 
     m_callback_index = npos;
     m_callback_index = npos;
 }
 }
 
 
-void CollectionNotifier::attach_to(SharedGroup& sg)
-{
-    REALM_ASSERT(!m_sg);
-
-    m_sg = &sg;
-    do_attach_to(sg);
-}
-
-void CollectionNotifier::detach()
+void CollectionNotifier::attach_to(std::shared_ptr<Transaction> sg)
 {
 {
-    REALM_ASSERT(m_sg);
-    do_detach_from(*m_sg);
-    m_sg = nullptr;
+    do_attach_to(*sg);
+    m_sg = std::move(sg);
 }
 }
 
 
-SharedGroup& CollectionNotifier::source_shared_group()
+Transaction& CollectionNotifier::source_shared_group()
 {
 {
-    return *Realm::Internal::get_shared_group(*m_realm);
+    return Realm::Internal::get_transaction(*m_realm);
 }
 }
 
 
 void CollectionNotifier::add_changes(CollectionChangeBuilder change)
 void CollectionNotifier::add_changes(CollectionChangeBuilder change)
 {
 {
-    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    util::CheckedLockGuard lock(m_callback_mutex);
     for (auto& callback : m_callbacks) {
     for (auto& callback : m_callbacks) {
         if (callback.skip_next) {
         if (callback.skip_next) {
             REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
             REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
@@ -429,7 +442,8 @@ NotifierPackage::NotifierPackage(std::exception_ptr error,
 {
 {
 }
 }
 
 
-void NotifierPackage::package_and_wait(util::Optional<VersionID::version_type> target_version)
+// 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)
     if (!m_coordinator || m_error || !*this)
         return;
         return;
@@ -468,24 +482,13 @@ void NotifierPackage::before_advance()
         notifier->before_advance();
         notifier->before_advance();
 }
 }
 
 
-void NotifierPackage::deliver(SharedGroup& sg)
+void NotifierPackage::after_advance()
 {
 {
     if (m_error) {
     if (m_error) {
         for (auto& notifier : m_notifiers)
         for (auto& notifier : m_notifiers)
             notifier->deliver_error(m_error);
             notifier->deliver_error(m_error);
         return;
         return;
     }
     }
-    // Can't deliver while in a write transaction
-    if (sg.get_transact_stage() != SharedGroup::transact_Reading)
-        return;
-    for (auto& notifier : m_notifiers)
-        notifier->deliver(sg);
-}
-
-void NotifierPackage::after_advance()
-{
-    if (m_error)
-        return;
     for (auto& notifier : m_notifiers)
     for (auto& notifier : m_notifiers)
         notifier->after_advance();
         notifier->after_advance();
 }
 }

+ 53 - 54
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/collection_notifier.hpp

@@ -19,10 +19,14 @@
 #ifndef REALM_BACKGROUND_COLLECTION_HPP
 #ifndef REALM_BACKGROUND_COLLECTION_HPP
 #define REALM_BACKGROUND_COLLECTION_HPP
 #define REALM_BACKGROUND_COLLECTION_HPP
 
 
+#include "object_changeset.hpp"
 #include "impl/collection_change_builder.hpp"
 #include "impl/collection_change_builder.hpp"
+#include "util/checked_mutex.hpp"
 
 
 #include <realm/util/assert.hpp>
 #include <realm/util/assert.hpp>
 #include <realm/version_id.hpp>
 #include <realm/version_id.hpp>
+#include <realm/keys.hpp>
+#include <realm/table_ref.hpp>
 
 
 #include <array>
 #include <array>
 #include <atomic>
 #include <atomic>
@@ -30,29 +34,29 @@
 #include <functional>
 #include <functional>
 #include <mutex>
 #include <mutex>
 #include <unordered_map>
 #include <unordered_map>
+#include <unordered_set>
 
 
 namespace realm {
 namespace realm {
 class Realm;
 class Realm;
-class SharedGroup;
-class Table;
+class Transaction;
 
 
 namespace _impl {
 namespace _impl {
 class RealmCoordinator;
 class RealmCoordinator;
 
 
 struct ListChangeInfo {
 struct ListChangeInfo {
-    size_t table_ndx;
-    size_t row_ndx;
-    size_t col_ndx;
+    TableKey table_key;
+    int64_t row_key;
+    int64_t col_key;
     CollectionChangeBuilder* changes;
     CollectionChangeBuilder* changes;
 };
 };
 
 
+// FIXME: this should be in core
+using TableKeyType = decltype(TableKey::value);
+using ObjKeyType = decltype(ObjKey::value);
+
 struct TransactionChangeInfo {
 struct TransactionChangeInfo {
-    std::vector<bool> table_modifications_needed;
-    std::vector<bool> table_moves_needed;
     std::vector<ListChangeInfo> lists;
     std::vector<ListChangeInfo> lists;
-    std::vector<CollectionChangeBuilder> tables;
-    std::vector<std::vector<size_t>> column_indices;
-    std::vector<size_t> table_indices;
+    std::unordered_map<TableKeyType, ObjectChangeSet> tables;
     bool track_all;
     bool track_all;
     bool schema_changed;
     bool schema_changed;
 };
 };
@@ -60,18 +64,18 @@ struct TransactionChangeInfo {
 class DeepChangeChecker {
 class DeepChangeChecker {
 public:
 public:
     struct OutgoingLink {
     struct OutgoingLink {
-        size_t col_ndx;
+        int64_t col_key;
         bool is_list;
         bool is_list;
     };
     };
     struct RelatedTable {
     struct RelatedTable {
-        size_t table_ndx;
+        TableKey table_key;
         std::vector<OutgoingLink> links;
         std::vector<OutgoingLink> links;
     };
     };
 
 
     DeepChangeChecker(TransactionChangeInfo const& info, Table const& root_table,
     DeepChangeChecker(TransactionChangeInfo const& info, Table const& root_table,
                       std::vector<RelatedTable> const& related_tables);
                       std::vector<RelatedTable> const& related_tables);
 
 
-    bool operator()(size_t row_ndx);
+    bool operator()(int64_t obj_key);
 
 
     // Recursively add `table` and all tables it links to to `out`, along with
     // Recursively add `table` and all tables it links to to `out`, along with
     // information about the links from them
     // information about the links from them
@@ -80,22 +84,21 @@ public:
 private:
 private:
     TransactionChangeInfo const& m_info;
     TransactionChangeInfo const& m_info;
     Table const& m_root_table;
     Table const& m_root_table;
-    const size_t m_root_table_ndx;
-    IndexSet const* const m_root_modifications;
-    std::vector<IndexSet> m_not_modified;
+    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;
     std::vector<RelatedTable> const& m_related_tables;
 
 
     struct Path {
     struct Path {
-        size_t table;
-        size_t row;
-        size_t col;
+        int64_t obj_key;
+        int64_t col_key;
         bool depth_exceeded;
         bool depth_exceeded;
     };
     };
     std::array<Path, 4> m_current_path;
     std::array<Path, 4> m_current_path;
 
 
-    bool check_row(Table const& table, size_t row_ndx, size_t depth = 0);
-    bool check_outgoing_links(size_t table_ndx, Table const& table,
-                              size_t row_ndx, size_t depth = 0);
+    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
 // A base class for a notifier that keeps a collection up to date and/or
@@ -118,13 +121,13 @@ public:
     // Add a callback to be called each time the collection changes
     // Add a callback to be called each time the collection changes
     // This can only be called from the target collection's thread
     // This can only be called from the target collection's thread
     // Returns a token which can be passed to remove_callback()
     // Returns a token which can be passed to remove_callback()
-    uint64_t add_callback(CollectionChangeCallback callback);
+    uint64_t add_callback(CollectionChangeCallback callback) REQUIRES(!m_callback_mutex);
     // Remove a previously added token. The token is no longer valid after
     // 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
     // calling this function and must not be used again. This function can be
     // called from any thread.
     // called from any thread.
-    void remove_callback(uint64_t token);
+    void remove_callback(uint64_t token) REQUIRES(!m_callback_mutex);
 
 
-    void suppress_next_notification(uint64_t token);
+    void suppress_next_notification(uint64_t token) REQUIRES(!m_callback_mutex);
 
 
     // ------------------------------------------------------------------------
     // ------------------------------------------------------------------------
     // API for RealmCoordinator to manage running things and calling callbacks
     // API for RealmCoordinator to manage running things and calling callbacks
@@ -132,7 +135,7 @@ public:
     bool is_for_realm(Realm&) const noexcept;
     bool is_for_realm(Realm&) const noexcept;
     Realm* get_realm() const noexcept { return m_realm.get(); }
     Realm* get_realm() const noexcept { return m_realm.get(); }
 
 
-    // Get the SharedGroup version which this collection can attach to (if it's
+    // 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)
     // 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
     // precondition: RealmCoordinator::m_notifier_mutex is locked
     VersionID version() const noexcept { return m_sg_version; }
     VersionID version() const noexcept { return m_sg_version; }
@@ -141,38 +144,30 @@ public:
     // This is called on the worker thread to ensure that non-thread-safe things
     // 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
     // can be destroyed on the correct thread, even if the last reference to the
     // CollectionNotifier is released on a different thread
     // CollectionNotifier is released on a different thread
-    virtual void release_data() noexcept = 0;
+    virtual void release_data() noexcept;
 
 
     // Prepare to deliver the new collection and call callbacks.
     // Prepare to deliver the new collection and call callbacks.
     // Returns whether or not it has anything to deliver.
     // Returns whether or not it has anything to deliver.
     // precondition: RealmCoordinator::m_notifier_mutex is locked
     // precondition: RealmCoordinator::m_notifier_mutex is locked
-    bool package_for_delivery();
-
-    // Deliver the new state to the target collection using the given SharedGroup
-    // precondition: RealmCoordinator::m_notifier_mutex is unlocked
-    virtual void deliver(SharedGroup&) { }
+    bool package_for_delivery() REQUIRES(!m_callback_mutex);
 
 
     // Pass the given error to all registered callbacks, then remove them
     // Pass the given error to all registered callbacks, then remove them
     // precondition: RealmCoordinator::m_notifier_mutex is unlocked
     // precondition: RealmCoordinator::m_notifier_mutex is unlocked
-    void deliver_error(std::exception_ptr);
+    void deliver_error(std::exception_ptr) REQUIRES(!m_callback_mutex);
 
 
     // Call each of the given callbacks with the changesets prepared by package_for_delivery()
     // Call each of the given callbacks with the changesets prepared by package_for_delivery()
     // precondition: RealmCoordinator::m_notifier_mutex is unlocked
     // precondition: RealmCoordinator::m_notifier_mutex is unlocked
-    void before_advance();
-    void after_advance();
+    void before_advance() REQUIRES(!m_callback_mutex);
+    void after_advance() REQUIRES(!m_callback_mutex);
 
 
     bool is_alive() const noexcept;
     bool is_alive() const noexcept;
 
 
     // precondition: RealmCoordinator::m_notifier_mutex is locked *or* is called on worker thread
     // precondition: RealmCoordinator::m_notifier_mutex is locked *or* is called on worker thread
     bool has_run() const noexcept { return m_has_run; }
     bool has_run() const noexcept { return m_has_run; }
 
 
-    // Attach the handed-over query to `sg`. Must not be already attached to a SharedGroup.
-    // precondition: RealmCoordinator::m_notifier_mutex is locked
-    void attach_to(SharedGroup& sg);
-    // Create a new query handover object and stop using the previously attached
-    // SharedGroup
+    // Attach the handed-over query to `sg`. Must not be already attached to a Transaction.
     // precondition: RealmCoordinator::m_notifier_mutex is locked
     // precondition: RealmCoordinator::m_notifier_mutex is locked
-    void detach();
+    void attach_to(std::shared_ptr<Transaction> sg);
 
 
     // Set `info` as the new ChangeInfo that will be populated by the next
     // Set `info` as the new ChangeInfo that will be populated by the next
     // transaction advance, and register all required information in it
     // transaction advance, and register all required information in it
@@ -183,24 +178,27 @@ public:
     virtual void run() = 0;
     virtual void run() = 0;
 
 
     // precondition: RealmCoordinator::m_notifier_mutex is locked
     // precondition: RealmCoordinator::m_notifier_mutex is locked
-    void prepare_handover();
+    void prepare_handover() REQUIRES(!m_callback_mutex);
 
 
     template <typename T>
     template <typename T>
     class Handle;
     class Handle;
 
 
     bool have_callbacks() const noexcept { return m_have_callbacks; }
     bool have_callbacks() const noexcept { return m_have_callbacks; }
 protected:
 protected:
-    void add_changes(CollectionChangeBuilder change);
-    void set_table(Table const& table);
+    void add_changes(CollectionChangeBuilder change) REQUIRES(!m_callback_mutex);
+    void set_table(ConstTableRef table);
     std::unique_lock<std::mutex> lock_target();
     std::unique_lock<std::mutex> lock_target();
-    SharedGroup& source_shared_group();
+    Transaction& source_shared_group();
+
+    bool all_related_tables_covered(const TableVersions& versions);
+    std::function<bool (ObjectChangeSet::ObjectKeyType)> get_modification_checker(TransactionChangeInfo const&, ConstTableRef);
 
 
-    std::function<bool (size_t)> get_modification_checker(TransactionChangeInfo const&, Table const&);
+    // The actual change, calculated in run() and delivered in prepare_handover()
+    CollectionChangeBuilder m_change;
 
 
 private:
 private:
-    virtual void do_attach_to(SharedGroup&) = 0;
-    virtual void do_detach_from(SharedGroup&) = 0;
-    virtual void do_prepare_handover(SharedGroup&) = 0;
+    virtual void do_attach_to(Transaction&) { }
+    virtual void do_prepare_handover(Transaction&) { }
     virtual bool do_add_required_change_info(TransactionChangeInfo&) = 0;
     virtual bool do_add_required_change_info(TransactionChangeInfo&) = 0;
     virtual bool prepare_to_deliver() { return true; }
     virtual bool prepare_to_deliver() { return true; }
 
 
@@ -208,7 +206,7 @@ private:
     std::shared_ptr<Realm> m_realm;
     std::shared_ptr<Realm> m_realm;
 
 
     VersionID m_sg_version;
     VersionID m_sg_version;
-    SharedGroup* m_sg = nullptr;
+    std::shared_ptr<Transaction> m_sg;
 
 
     bool m_has_run = false;
     bool m_has_run = false;
     bool m_error = false;
     bool m_error = false;
@@ -225,7 +223,7 @@ private:
 
 
     // Currently registered callbacks and a mutex which must always be held
     // Currently registered callbacks and a mutex which must always be held
     // while doing anything with them or m_callback_index
     // while doing anything with them or m_callback_index
-    std::mutex m_callback_mutex;
+    util::CheckedMutex m_callback_mutex;
     std::vector<Callback> m_callbacks;
     std::vector<Callback> m_callbacks;
 
 
     // Cached value for if m_callbacks is empty, needed to avoid deadlocks in
     // Cached value for if m_callbacks is empty, needed to avoid deadlocks in
@@ -246,7 +244,7 @@ private:
     uint64_t m_next_token = 0;
     uint64_t m_next_token = 0;
 
 
     template<typename Fn>
     template<typename Fn>
-    void for_each_callback(Fn&& fn);
+    void for_each_callback(Fn&& fn) REQUIRES(!m_callback_mutex);
 
 
     std::vector<Callback>::iterator find_callback(uint64_t token);
     std::vector<Callback>::iterator find_callback(uint64_t token);
 };
 };
@@ -279,7 +277,8 @@ public:
         return *this;
         return *this;
     }
     }
 
 
-    Handle& operator=(std::shared_ptr<T>&& other)
+    template<typename U>
+    Handle& operator=(std::shared_ptr<U>&& other)
     {
     {
         reset();
         reset();
         std::shared_ptr<T>::operator=(std::move(other));
         std::shared_ptr<T>::operator=(std::move(other));
@@ -318,7 +317,7 @@ public:
     // Send the before-change notifications
     // Send the before-change notifications
     void before_advance();
     void before_advance();
     // Deliver the payload associated with the contained notifiers and/or the error
     // Deliver the payload associated with the contained notifiers and/or the error
-    void deliver(SharedGroup& sg);
+    void deliver(Transaction& sg);
     // Send the after-change notifications
     // Send the after-change notifications
     void after_advance();
     void after_advance();
 
 

+ 3 - 3
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/epoll/external_commit_helper.cpp

@@ -21,7 +21,7 @@
 #include <realm/util/fifo_helper.hpp>
 #include <realm/util/fifo_helper.hpp>
 
 
 #include <realm/util/assert.hpp>
 #include <realm/util/assert.hpp>
-#include <realm/group_shared_options.hpp>
+#include <realm/db.hpp>
 
 
 #include <algorithm>
 #include <algorithm>
 #include <errno.h>
 #include <errno.h>
@@ -115,7 +115,7 @@ ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
 {
 {
     std::string path;
     std::string path;
     std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
     std::string temp_dir = util::normalize_dir(parent.get_config().fifo_files_fallback_path);
-    std::string sys_temp_dir = util::normalize_dir(SharedGroupOptions::get_sys_tmp_dir());
+    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.
     // 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
     // This can be a problem on some file systems (e.g. FAT32) or due to security policies in SELinux. Most commonly
@@ -126,7 +126,7 @@ ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
     // In order of priority we attempt to write the file in the following locations:
     // In order of priority we attempt to write the file in the following locations:
     //  1) Next to the Realm file itself
     //  1) Next to the Realm file itself
     //  2) A location defined by `Realm::Config::fifo_files_fallback_path`
     //  2) A location defined by `Realm::Config::fifo_files_fallback_path`
-    //  3) A location defined by `SharedGroupOptions::set_sys_tmp_dir()`
+    //  3) A location defined by `DBOptions::set_sys_tmp_dir()`
     //
     //
     // Core has a similar policy for its named pipes.
     // Core has a similar policy for its named pipes.
     //
     //

+ 2 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.cpp

@@ -29,8 +29,8 @@ using namespace realm::_impl;
 ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
 ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent)
 : m_parent(parent)
 : m_parent(parent)
 , m_history(realm::make_in_realm_history(parent.get_path()))
 , m_history(realm::make_in_realm_history(parent.get_path()))
-, m_sg(*m_history, SharedGroupOptions(parent.is_in_memory() ? SharedGroupOptions::Durability::MemOnly
-                                                            : SharedGroupOptions::Durability::Full,
+, m_sg(*m_history, TransactionOptions(parent.is_in_memory() ? TransactionOptions::Durability::MemOnly
+                                                            : TransactionOptions::Durability::Full,
                                       parent.get_encryption_key().data()))
                                       parent.get_encryption_key().data()))
 , m_thread(std::async(std::launch::async, [=] {
 , m_thread(std::async(std::launch::async, [=] {
     m_sg.begin_read();
     m_sg.begin_read();

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/generic/external_commit_helper.hpp

@@ -39,7 +39,7 @@ private:
 
 
     // A shared group used to listen for changes
     // A shared group used to listen for changes
     std::unique_ptr<Replication> m_history;
     std::unique_ptr<Replication> m_history;
-    SharedGroup m_sg;
+    Transaction m_sg;
 
 
     // The listener thread
     // The listener thread
     std::future<void> m_thread;
     std::future<void> m_thread;

+ 42 - 47
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.cpp

@@ -18,53 +18,50 @@
 
 
 #include "impl/list_notifier.hpp"
 #include "impl/list_notifier.hpp"
 
 
-#include "shared_realm.hpp"
+#include "list.hpp"
 
 
-#include <realm/link_view.hpp>
+#include <realm/db.hpp>
+#include <realm/group.hpp>
 
 
 using namespace realm;
 using namespace realm;
 using namespace realm::_impl;
 using namespace realm::_impl;
 
 
-ListNotifier::ListNotifier(LinkViewRef lv, std::shared_ptr<Realm> realm)
+ListNotifier::ListNotifier(std::shared_ptr<Realm> realm, LstBase const& list,
+                           PropertyType type)
 : CollectionNotifier(std::move(realm))
 : CollectionNotifier(std::move(realm))
-, m_prev_size(lv->size())
+, 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())
 {
 {
-    set_table(lv->get_target_table());
-    m_lv_handover = source_shared_group().export_linkview_for_handover(lv);
+    if (m_type == PropertyType::Object) {
+        set_table(static_cast<const LnkLst&>(list).get_target_table());
+    }
 }
 }
 
 
 void ListNotifier::release_data() noexcept
 void ListNotifier::release_data() noexcept
 {
 {
-    m_lv.reset();
-}
-
-void ListNotifier::do_attach_to(SharedGroup& sg)
-{
-    REALM_ASSERT(m_lv_handover);
-    REALM_ASSERT(!m_lv);
-    m_lv = sg.import_linkview_from_handover(std::move(m_lv_handover));
+    m_list = {};
+    CollectionNotifier::release_data();
 }
 }
 
 
-void ListNotifier::do_detach_from(SharedGroup& sg)
+void ListNotifier::do_attach_to(Transaction& sg)
 {
 {
-    REALM_ASSERT(!m_lv_handover);
-    if (m_lv) {
-        m_lv_handover = sg.export_linkview_for_handover(m_lv);
-        m_lv = {};
+    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)
 bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info)
 {
 {
-    REALM_ASSERT(!m_lv_handover);
-    if (!m_lv || !m_lv->is_attached()) {
+    if (!m_list->is_attached())
         return false; // origin row was deleted after the notification was added
         return false; // origin row was deleted after the notification was added
-    }
 
 
-    auto& table = m_lv->get_origin_table();
-    size_t row_ndx = m_lv->get_origin_row_index();
-    size_t col_ndx = find_container_column(table, row_ndx, m_lv, type_LinkList, &Table::get_linklist);
-    info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change});
+    info.lists.push_back({m_table, m_obj.value, m_col.value, &m_change});
 
 
     m_info = &info;
     m_info = &info;
     return true;
     return true;
@@ -72,8 +69,8 @@ bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info)
 
 
 void ListNotifier::run()
 void ListNotifier::run()
 {
 {
-    if (!m_lv || !m_lv->is_attached()) {
-        // LV was deleted, so report all of the rows being removed if this is
+    if (!m_list->is_attached()) {
+        // List was deleted, so report all of the rows being removed if this is
         // the first run after that
         // the first run after that
         if (m_prev_size) {
         if (m_prev_size) {
             m_change.deletions.set(m_prev_size);
             m_change.deletions.set(m_prev_size);
@@ -85,25 +82,23 @@ void ListNotifier::run()
         return;
         return;
     }
     }
 
 
-    auto row_did_change = get_modification_checker(*m_info, m_lv->get_target_table());
-    for (size_t i = 0; i < m_lv->size(); ++i) {
-        if (m_change.modifications.contains(i))
-            continue;
-        if (row_did_change(m_lv->get(i).get_index()))
-            m_change.modifications.add(i);
-    }
-
-    for (auto const& move : m_change.moves) {
-        if (m_change.modifications.contains(move.to))
-            continue;
-        if (row_did_change(m_lv->get(move.to).get_index()))
-            m_change.modifications.add(move.to);
-    }
+    m_prev_size = m_list->size();
 
 
-    m_prev_size = m_lv->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);
+        }
 
 
-void ListNotifier::do_prepare_handover(SharedGroup&)
-{
-    add_changes(std::move(m_change));
+        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);
+        }
+    }
 }
 }

+ 11 - 12
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/list_notifier.hpp

@@ -21,34 +21,33 @@
 
 
 #include "impl/collection_notifier.hpp"
 #include "impl/collection_notifier.hpp"
 
 
-#include <realm/group_shared.hpp>
+#include "property.hpp"
+
+#include <realm/list.hpp>
 
 
 namespace realm {
 namespace realm {
 namespace _impl {
 namespace _impl {
 class ListNotifier : public CollectionNotifier {
 class ListNotifier : public CollectionNotifier {
 public:
 public:
-    ListNotifier(LinkViewRef lv, std::shared_ptr<Realm> realm);
+    ListNotifier(std::shared_ptr<Realm> realm, LstBase const& list, PropertyType type);
 
 
 private:
 private:
-    // The linkview, in handover form if this has not been attached to the main
-    // SharedGroup yet
-    LinkViewRef m_lv;
-    std::unique_ptr<SharedGroup::Handover<LinkView>> m_lv_handover;
+    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
     // The last-seen size of the LinkView so that we can report row deletions
     // when the LinkView itself is deleted
     // when the LinkView itself is deleted
     size_t m_prev_size;
     size_t m_prev_size;
 
 
-    // The actual change, calculated in run() and delivered in prepare_handover()
-    CollectionChangeBuilder m_change;
     TransactionChangeInfo* m_info;
     TransactionChangeInfo* m_info;
 
 
     void run() override;
     void run() override;
 
 
-    void do_prepare_handover(SharedGroup&) override;
-
-    void do_attach_to(SharedGroup& sg) override;
-    void do_detach_from(SharedGroup& sg) override;
+    void do_attach_to(Transaction& sg) override;
 
 
     void release_data() noexcept override;
     void release_data() noexcept override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;

+ 20 - 20
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp

@@ -105,7 +105,7 @@ public:
     util::Any box(util::Optional<double> 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<float> v) const { return v; }
     util::Any box(util::Optional<int64_t> v) const { return v; }
     util::Any box(util::Optional<int64_t> v) const { return v; }
-    util::Any box(RowExpr) const;
+    util::Any box(Obj) const;
 
 
     // Any properties are only supported by the Cocoa binding to enable reading
     // 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
     // old Realm files that may have used them. Other bindings can safely not
@@ -113,12 +113,12 @@ public:
     util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
     util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
 
 
     // Convert from the boxed type to core types. This needs to be implemented
     // Convert from the boxed type to core types. This needs to be implemented
-    // for all of the types which `box()` can take, plus `RowExpr` and optional
+    // for all of the types which `box()` can take, plus `Obj` and optional
     // versions of the numeric types, minus `List` and `Results`.
     // versions of the numeric types, minus `List` and `Results`.
     //
     //
-    // `create` and `update` are only applicable to `unbox<RowExpr>`. If
+    // `create` and `update` are only applicable to `unbox<Obj>`. If
     // `create` is false then when given something which is not a managed Realm
     // `create` is false then when given something which is not a managed Realm
-    // object `unbox()` should simply return a detached row expr, while if it's
+    // 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
     // true then `unbox()` should create a new object in the context's Realm
     // using the provided value. If `update` is true then upsert semantics
     // using the provided value. If `update` is true then upsert semantics
     // should be used for this.
     // should be used for this.
@@ -127,7 +127,7 @@ public:
     // is true, `current_row` may hold a reference to the object that should
     // is true, `current_row` may hold a reference to the object that should
     // be compared against.
     // be compared against.
     template<typename T>
     template<typename T>
-    T unbox(util::Any& v, CreatePolicy = CreatePolicy::Skip, size_t /*current_row*/ = realm::npos) const { return any_cast<T>(v); }
+    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(); }
     bool is_null(util::Any const& v) const noexcept { return !v.has_value(); }
     util::Any null_value() const noexcept { return {}; }
     util::Any null_value() const noexcept { return {}; }
@@ -152,14 +152,14 @@ private:
 
 
 };
 };
 
 
-inline util::Any CppContext::box(RowExpr row) const
+inline util::Any CppContext::box(Obj obj) const
 {
 {
     REALM_ASSERT(object_schema);
     REALM_ASSERT(object_schema);
-    return Object(realm, *object_schema, row);
+    return Object(realm, *object_schema, obj);
 }
 }
 
 
 template<>
 template<>
-inline StringData CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+inline StringData CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
 {
 {
     if (!v.has_value())
     if (!v.has_value())
         return StringData();
         return StringData();
@@ -168,7 +168,7 @@ inline StringData CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
 }
 }
 
 
 template<>
 template<>
-inline BinaryData CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+inline BinaryData CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
 {
 {
     if (!v.has_value())
     if (!v.has_value())
         return BinaryData();
         return BinaryData();
@@ -177,45 +177,45 @@ inline BinaryData CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
 }
 }
 
 
 template<>
 template<>
-inline RowExpr CppContext::unbox(util::Any& v, CreatePolicy policy, size_t current_row) const
+inline Obj CppContext::unbox(util::Any& v, CreatePolicy policy, ObjKey current_obj) const
 {
 {
     if (auto object = any_cast<Object>(&v))
     if (auto object = any_cast<Object>(&v))
-        return object->row();
-    if (auto row = any_cast<RowExpr>(&v))
-        return *row;
+        return object->obj();
+    if (auto obj = any_cast<Obj>(&v))
+        return *obj;
     if (policy == CreatePolicy::Skip)
     if (policy == CreatePolicy::Skip)
-        return RowExpr();
+        return Obj();
 
 
     REALM_ASSERT(object_schema);
     REALM_ASSERT(object_schema);
-    return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, policy, current_row).row();
+    return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, policy, current_obj).obj();
 }
 }
 
 
 template<>
 template<>
-inline util::Optional<bool> CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+inline util::Optional<bool> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
 {
 {
     return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
     return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
 }
 }
 
 
 template<>
 template<>
-inline util::Optional<int64_t> CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+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;
     return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
 }
 }
 
 
 template<>
 template<>
-inline util::Optional<double> CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+inline util::Optional<double> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
 {
 {
     return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
     return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
 }
 }
 
 
 template<>
 template<>
-inline util::Optional<float> CppContext::unbox(util::Any& v, CreatePolicy, size_t) const
+inline util::Optional<float> CppContext::unbox(util::Any& v, CreatePolicy, ObjKey) const
 {
 {
     return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
     return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
 }
 }
 
 
 template<>
 template<>
-inline Mixed CppContext::unbox(util::Any&, CreatePolicy, size_t) const
+inline Mixed CppContext::unbox(util::Any&, CreatePolicy, ObjKey) const
 {
 {
     throw std::logic_error("'Any' type is unsupported");
     throw std::logic_error("'Any' type is unsupported");
 }
 }

+ 18 - 51
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.cpp

@@ -23,75 +23,42 @@
 using namespace realm;
 using namespace realm;
 using namespace realm::_impl;
 using namespace realm::_impl;
 
 
-ObjectNotifier::ObjectNotifier(Row const& row, std::shared_ptr<Realm> realm)
+ObjectNotifier::ObjectNotifier(std::shared_ptr<Realm> realm, TableKey table, ObjKey obj)
 : CollectionNotifier(std::move(realm))
 : CollectionNotifier(std::move(realm))
+, m_table(table)
+, m_obj(obj)
 {
 {
-    REALM_ASSERT(row.get_table());
-    set_table(*row.get_table());
-
-    m_handover = source_shared_group().export_for_handover(row);
-}
-
-void ObjectNotifier::release_data() noexcept
-{
-    m_row = nullptr;
-}
-
-void ObjectNotifier::do_attach_to(SharedGroup& sg)
-{
-    REALM_ASSERT(m_handover);
-    REALM_ASSERT(!m_row);
-    m_row = sg.import_from_handover(std::move(m_handover));
-}
-
-void ObjectNotifier::do_detach_from(SharedGroup& sg)
-{
-    REALM_ASSERT(!m_handover);
-    if (m_row) {
-        m_handover = sg.export_for_handover(*m_row);
-        m_row = nullptr;
-    }
 }
 }
 
 
 bool ObjectNotifier::do_add_required_change_info(TransactionChangeInfo& info)
 bool ObjectNotifier::do_add_required_change_info(TransactionChangeInfo& info)
 {
 {
-    REALM_ASSERT(!m_handover);
     m_info = &info;
     m_info = &info;
-    if (m_row && m_row->is_attached()) {
-        size_t table_ndx = m_row->get_table()->get_index_in_group();
-        if (table_ndx >= info.table_modifications_needed.size())
-            info.table_modifications_needed.resize(table_ndx + 1);
-        info.table_modifications_needed[table_ndx] = true;
-    }
+    info.tables[m_table.value];
     return false;
     return false;
 }
 }
 
 
 void ObjectNotifier::run()
 void ObjectNotifier::run()
 {
 {
-    if (!m_row)
+    if (!m_table)
         return;
         return;
-    if (!m_row->is_attached()) {
+
+    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_change.deletions.add(0);
-        m_row = nullptr;
+        m_table = {};
+        m_obj = {};
         return;
         return;
     }
     }
 
 
-    size_t table_ndx = m_row->get_table()->get_index_in_group();
-    if (table_ndx >= m_info->tables.size())
-        return;
-    auto& change = m_info->tables[table_ndx];
-    if (!change.modifications.contains(m_row->get_index()))
+    auto column_modifications = change.get_columns_modified(m_obj.value);
+    if (!column_modifications)
         return;
         return;
     m_change.modifications.add(0);
     m_change.modifications.add(0);
-    m_change.columns.reserve(change.columns.size());
-    for (auto& col : change.columns) {
-        m_change.columns.emplace_back();
-        if (col.contains(m_row->get_index()))
-            m_change.columns.back().add(0);
+    for (auto col : *column_modifications) {
+        m_change.columns[col].add(0);
     }
     }
 }
 }
-
-void ObjectNotifier::do_prepare_handover(SharedGroup&)
-{
-    add_changes(std::move(m_change));
-}

+ 5 - 13
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_notifier.hpp

@@ -21,30 +21,22 @@
 
 
 #include "impl/collection_notifier.hpp"
 #include "impl/collection_notifier.hpp"
 
 
-#include <realm/group_shared.hpp>
+#include <realm/keys.hpp>
 
 
 namespace realm {
 namespace realm {
+
 namespace _impl {
 namespace _impl {
 class ObjectNotifier : public CollectionNotifier {
 class ObjectNotifier : public CollectionNotifier {
 public:
 public:
-    ObjectNotifier(Row const& row, std::shared_ptr<Realm> realm);
+    ObjectNotifier(std::shared_ptr<Realm> realm, TableKey table, ObjKey obj);
 
 
 private:
 private:
-    std::unique_ptr<Row> m_row;
-    std::unique_ptr<SharedGroup::Handover<Row>> m_handover;
-
-    // The actual change, calculated in run() and delivered in prepare_handover()
-    CollectionChangeBuilder m_change;
+    TableKey m_table;
+    ObjKey m_obj;
     TransactionChangeInfo* m_info;
     TransactionChangeInfo* m_info;
 
 
     void run() override;
     void run() override;
 
 
-    void do_prepare_handover(SharedGroup&) override;
-
-    void do_attach_to(SharedGroup& sg) override;
-    void do_detach_from(SharedGroup& sg) override;
-
-    void release_data() noexcept override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;
 };
 };
 }
 }

File diff suppressed because it is too large
+ 340 - 221
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.cpp


+ 82 - 58
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/realm_coordinator.hpp

@@ -21,17 +21,20 @@
 
 
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
+#include "util/checked_mutex.hpp"
+
 #include <realm/version_id.hpp>
 #include <realm/version_id.hpp>
 
 
 #include <condition_variable>
 #include <condition_variable>
 #include <mutex>
 #include <mutex>
 
 
 namespace realm {
 namespace realm {
+class DB;
 class Replication;
 class Replication;
 class Schema;
 class Schema;
-class SharedGroup;
 class StringData;
 class StringData;
 class SyncSession;
 class SyncSession;
+class Transaction;
 
 
 namespace _impl {
 namespace _impl {
 class CollectionNotifier;
 class CollectionNotifier;
@@ -53,40 +56,45 @@ public:
     // Get the coordinator for the given path, or null if there is none
     // Get the coordinator for the given path, or null if there is none
     static std::shared_ptr<RealmCoordinator> get_existing_coordinator(StringData path);
     static std::shared_ptr<RealmCoordinator> get_existing_coordinator(StringData path);
 
 
-    // Get a thread-local shared Realm with the given configuration
-    // If the Realm is already open on another thread, validates that the given
-    // configuration is compatible with the existing one
-    std::shared_ptr<Realm> get_realm(Realm::Config config);
-    std::shared_ptr<Realm> get_realm();
+    // 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
 #if REALM_ENABLE_SYNC
     // Get a thread-local shared Realm with the given configuration
     // 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 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.
     // 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.
     // 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);
+    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.
     // 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
     // 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.
     // method if the session needs to exist before the Realm does.
-    void create_session(const Realm::Config& config);
+    void create_session(const Realm::Config& config) REQUIRES(!m_realm_mutex, !m_schema_cache_mutex);
 #endif
 #endif
 
 
     // Get a Realm which is not bound to the current execution context
     // Get a Realm which is not bound to the current execution context
-    ThreadSafeReference<Realm> get_unbound_realm();
-
-    // Get the existing cached Realm for the given execution context if it exists
-    std::shared_ptr<Realm> get_cached_realm(Realm::Config const&, AnyExecutionContextID);
+    ThreadSafeReference get_unbound_realm() REQUIRES(!m_realm_mutex);
 
 
     // Bind an unbound Realm to a specific execution context. The Realm must
     // Bind an unbound Realm to a specific execution context. The Realm must
     // be managed by this coordinator.
     // be managed by this coordinator.
-    void bind_to_context(Realm& realm, AnyExecutionContextID);
+    void bind_to_context(Realm& realm) REQUIRES(!m_realm_mutex);
 
 
     Realm::Config get_config() const { return m_config; }
     Realm::Config get_config() const { return m_config; }
 
 
-    uint64_t get_schema_version() const noexcept { return m_schema_version; }
+    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::string& get_path() const noexcept { return m_config.path; }
     const std::vector<char>& get_encryption_key() const noexcept { return m_config.encryption_key; }
     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; }
     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
     // 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
     // new read transaction is begun, RealmCoordinator maintains a cache of the
@@ -97,15 +105,15 @@ public:
 
 
     // Get the latest cached schema and the transaction version which it applies
     // Get the latest cached schema and the transaction version which it applies
     // to. Returns false if there is no cached schema.
     // to. Returns false if there is no cached schema.
-    bool get_cached_schema(Schema& schema, uint64_t& schema_version, uint64_t& transaction) const noexcept;
+    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
     // Cache the state of the schema at the given transaction version
     void cache_schema(Schema const& new_schema, uint64_t new_schema_version,
     void cache_schema(Schema const& new_schema, uint64_t new_schema_version,
-                      uint64_t transaction_version);
+                      uint64_t transaction_version) REQUIRES(!m_schema_cache_mutex);
     // If there is a schema cached for transaction version `previous`, report
     // If there is a schema cached for transaction version `previous`, report
     // that it is still valid at transaction version `next`
     // that it is still valid at transaction version `next`
-    void advance_schema_cache(uint64_t previous, uint64_t next);
-    void clear_schema_cache_and_set_schema_version(uint64_t new_schema_version);
+    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
     // Asynchronously call notify() on every Realm instance for this coordinator's
@@ -130,38 +138,50 @@ public:
 
 
     // Called by Realm's destructor to ensure the cache is cleaned up promptly
     // Called by Realm's destructor to ensure the cache is cleaned up promptly
     // Do not call directly
     // Do not call directly
-    void unregister_realm(Realm* realm);
+    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
     // Called by m_notifier when there's a new commit to send notifications for
-    void on_change();
+    void on_change() REQUIRES(!m_realm_mutex, !m_notifier_mutex);
 
 
     static void register_notifier(std::shared_ptr<CollectionNotifier> notifier);
     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
     // Advance the Realm to the most recent transaction version which all async
     // work is complete for
     // work is complete for
-    void advance_to_ready(Realm& realm);
+    void advance_to_ready(Realm& realm) REQUIRES(!m_notifier_mutex);
 
 
     // Advance the Realm to the most recent transaction version, blocking if
     // Advance the Realm to the most recent transaction version, blocking if
     // async notifiers are not yet ready for that version
     // async notifiers are not yet ready for that version
     // returns whether it actually changed the version
     // returns whether it actually changed the version
-    bool advance_to_latest(Realm& realm);
+    bool advance_to_latest(Realm& realm) REQUIRES(!m_notifier_mutex);
 
 
     // Deliver any notifications which are ready for the Realm's version
     // Deliver any notifications which are ready for the Realm's version
-    void process_available_async(Realm& realm);
+    void process_available_async(Realm& realm) REQUIRES(!m_notifier_mutex);
 
 
     // Register a function which is called whenever sync makes a write to the Realm
     // Register a function which is called whenever sync makes a write to the Realm
-    void set_transaction_callback(std::function<void(VersionID, VersionID)>);
+    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
     // Deliver notifications for the Realm, blocking if some aren't ready yet
     // The calling Realm must be in a write transaction
     // The calling Realm must be in a write transaction
-    void promote_to_write(Realm& realm);
+    void promote_to_write(Realm& realm) REQUIRES(!m_notifier_mutex);
 
 
     // Commit a Realm's current write transaction and send notifications to all
     // Commit a Realm's current write transaction and send notifications to all
     // other Realm instances for that path, including in other processes
     // other Realm instances for that path, including in other processes
-    void commit_write(Realm& realm);
+    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>
     template<typename Pred>
-    std::unique_lock<std::mutex> wait_for_notifiers(Pred&& wait_predicate);
+    util::CheckedUniqueLock wait_for_notifiers(Pred&& wait_predicate) REQUIRES(!m_notifier_mutex);
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
     // A work queue that can be used to perform background work related to partial sync.
     // A work queue that can be used to perform background work related to partial sync.
@@ -171,37 +191,40 @@ public:
     AuditInterface* audit_context() const noexcept { return m_audit_context.get(); }
     AuditInterface* audit_context() const noexcept { return m_audit_context.get(); }
 
 
 private:
 private:
+    friend Realm::Internal;
     Realm::Config m_config;
     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 std::mutex m_schema_cache_mutex;
-    util::Optional<Schema> m_cached_schema;
-    uint64_t m_schema_version = -1;
-    uint64_t m_schema_transaction_version_min = 0;
-    uint64_t m_schema_transaction_version_max = 0;
+    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;
 
 
-    std::mutex m_realm_mutex;
-    std::vector<WeakRealmNotifier> m_weak_realm_notifiers;
+    util::CheckedMutex m_realm_mutex;
+    std::vector<WeakRealmNotifier> m_weak_realm_notifiers GUARDED_BY(m_realm_mutex);
 
 
-    std::mutex m_notifier_mutex;
+    util::CheckedMutex m_notifier_mutex;
     std::condition_variable m_notifier_cv;
     std::condition_variable m_notifier_cv;
-    std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_new_notifiers;
-    std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_notifiers;
-    VersionID m_notifier_skip_version = {0, 0};
+    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};
 
 
-    // SharedGroup used for actually running async notifiers
+    // Transaction used for actually running async notifiers
     // Will have a read transaction iff m_notifiers is non-empty
     // Will have a read transaction iff m_notifiers is non-empty
-    std::unique_ptr<Replication> m_notifier_history;
-    std::unique_ptr<SharedGroup> m_notifier_sg;
+    std::shared_ptr<Transaction> m_notifier_sg;
 
 
-    // SharedGroup used to advance notifiers in m_new_notifiers to the main shared
+    // Transaction used to advance notifiers in m_new_notifiers to the main shared
     // group's transaction version
     // group's transaction version
     // Will have a read transaction iff m_new_notifiers is non-empty
     // Will have a read transaction iff m_new_notifiers is non-empty
-    std::unique_ptr<Replication> m_advancer_history;
-    std::unique_ptr<SharedGroup> m_advancer_sg;
+    std::shared_ptr<Transaction> m_advancer_sg;
     std::exception_ptr m_async_error;
     std::exception_ptr m_async_error;
 
 
     std::unique_ptr<_impl::ExternalCommitHelper> m_notifier;
     std::unique_ptr<_impl::ExternalCommitHelper> m_notifier;
-    std::function<void(VersionID, VersionID)> m_transaction_callback;
+    util::CheckedMutex m_transaction_callback_mutex;
+    std::function<void(VersionID, VersionID)> m_transaction_callback GUARDED_BY(m_transaction_callback_mutex);
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
     std::shared_ptr<SyncSession> m_sync_session;
     std::shared_ptr<SyncSession> m_sync_session;
@@ -210,29 +233,30 @@ private:
 
 
     std::shared_ptr<AuditInterface> m_audit_context;
     std::shared_ptr<AuditInterface> m_audit_context;
 
 
-    // must be called with m_notifier_mutex locked
-    void pin_version(VersionID version);
+    void open_db();
 
 
-    void set_config(const Realm::Config&);
-    void create_sync_session(bool force_client_resync, bool validate_sync_history);
-    void do_get_realm(Realm::Config config, std::shared_ptr<Realm>& realm,
-                      std::unique_lock<std::mutex>& realm_lock, bool bind_to_context=true);
+    void pin_version(VersionID version) REQUIRES(m_notifier_mutex);
 
 
-    void run_async_notifiers();
-    void open_helper_shared_group();
+    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 advance_helper_shared_group_to_latest();
-    void clean_up_dead_notifiers();
+    void clean_up_dead_notifiers() REQUIRES(m_notifier_mutex);
 
 
-    std::vector<std::shared_ptr<_impl::CollectionNotifier>> notifiers_for_realm(Realm&);
+    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>
 template<typename Pred>
-std::unique_lock<std::mutex> RealmCoordinator::wait_for_notifiers(Pred&& wait_predicate)
+util::CheckedUniqueLock RealmCoordinator::wait_for_notifiers(Pred&& wait_predicate)
 {
 {
-    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    util::CheckedUniqueLock lock(m_notifier_mutex);
     bool first = true;
     bool first = true;
-    m_notifier_cv.wait(lock, [&] {
+    m_notifier_cv.wait(lock.native_handle(), [&] {
         if (wait_predicate())
         if (wait_predicate())
             return true;
             return true;
         if (first) {
         if (first) {

+ 246 - 144
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.cpp

@@ -20,151 +20,133 @@
 
 
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
+#include <numeric>
+
 using namespace realm;
 using namespace realm;
 using namespace realm::_impl;
 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)
 ResultsNotifier::ResultsNotifier(Results& target)
-: CollectionNotifier(target.get_realm())
-, m_target_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())
 , m_target_is_in_table_order(target.is_in_table_order())
 {
 {
-    Query q = target.get_query();
-    set_table(*q.get_table());
-    m_query_handover = source_shared_group().export_for_handover(q, MutableSourcePayload::Move);
-    DescriptorOrdering::generate_patch(target.get_descriptor_ordering(), m_ordering_handover);
+    auto table = m_query->get_table();
+    if (table) {
+        set_table(table);
+    }
 }
 }
 
 
-void ResultsNotifier::target_results_moved(Results& old_target, Results& new_target)
+void ResultsNotifier::release_data() noexcept
 {
 {
-    auto lock = lock_target();
-
-    REALM_ASSERT(m_target_results == &old_target);
-    m_target_results = &new_target;
+    m_query = {};
+    m_run_tv = {};
+    m_handover_tv = {};
+    m_handover_transaction = {};
+    m_delivered_tv = {};
+    m_delivered_transaction = {};
+    CollectionNotifier::release_data();
 }
 }
 
 
-void ResultsNotifier::release_data() noexcept
+bool ResultsNotifier::get_tableview(TableView& out)
 {
 {
-    m_query = nullptr;
-}
+    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;
 
 
-// Most of the inter-thread synchronization for run(), prepare_handover(),
-// attach_to(), detach(), release_data() and deliver() is done by
-// RealmCoordinator external to this code, which has some potentially
-// non-obvious results on which members are and are not safe to use without
-// holding a lock.
-//
-// add_required_change_info(), attach_to(), detach(), run(),
-// prepare_handover(), and release_data() are all only ever called on a single
-// background worker thread. call_callbacks() and deliver() are called on the
-// target thread. Calls to prepare_handover() and deliver() are guarded by a
-// lock.
-//
-// In total, this means that the safe data flow is as follows:
-//  - add_Required_change_info(), prepare_handover(), attach_to(), detach() and
-//    release_data() can read members written by each other
-//  - deliver() can read members written to in prepare_handover(), deliver(),
-//    and call_callbacks()
-//  - call_callbacks() and read members written to in deliver()
-//
-// Separately from the handover data flow, m_target_results is guarded by the target lock
+    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)
 bool ResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info)
 {
 {
-    REALM_ASSERT(m_query);
     m_info = &info;
     m_info = &info;
-
-    auto& table = *m_query->get_table();
-    if (!table.is_attached())
-        return false;
-
-    auto table_ndx = table.get_index_in_group();
-    if (table_ndx == npos) { // is a subtable
-        auto& parent = *table.get_parent_table();
-        size_t row_ndx = table.get_parent_row_index();
-        size_t col_ndx = find_container_column(parent, row_ndx, &table, type_Table, &Table::get_subtable);
-        info.lists.push_back({parent.get_index_in_group(), row_ndx, col_ndx, &m_changes});
-    }
-    else { // is a top-level table
-        if (info.table_moves_needed.size() <= table_ndx)
-            info.table_moves_needed.resize(table_ndx + 1);
-        info.table_moves_needed[table_ndx] = true;
-    }
-
-    return has_run() && have_callbacks();
+    return m_query->get_table() && has_run() && have_callbacks();
 }
 }
 
 
 bool ResultsNotifier::need_to_run()
 bool ResultsNotifier::need_to_run()
 {
 {
     REALM_ASSERT(m_info);
     REALM_ASSERT(m_info);
-    REALM_ASSERT(!m_tv.is_attached());
 
 
     {
     {
         auto lock = lock_target();
         auto lock = lock_target();
         // Don't run the query if the results aren't actually going to be used
         // Don't run the query if the results aren't actually going to be used
-        if (!get_realm() || (!have_callbacks() && !m_target_results->wants_background_updates())) {
+        if (!get_realm() || (!have_callbacks() && !m_results_were_used))
             return false;
             return false;
-        }
     }
     }
 
 
     // If we've run previously, check if we need to rerun
     // If we've run previously, check if we need to rerun
     if (has_run() && m_query->sync_view_if_needed() == m_last_seen_version) {
     if (has_run() && m_query->sync_view_if_needed() == m_last_seen_version) {
-        return false;
+        // Does m_last_seen_version match m_related_tables
+        if (all_related_tables_covered(m_last_seen_version)) {
+            return false;
+        }
     }
     }
-
     return true;
     return true;
 }
 }
 
 
 void ResultsNotifier::calculate_changes()
 void ResultsNotifier::calculate_changes()
 {
 {
-    size_t table_ndx = m_query->get_table()->get_index_in_group();
     if (has_run() && have_callbacks()) {
     if (has_run() && have_callbacks()) {
-        CollectionChangeBuilder* changes = nullptr;
-        if (table_ndx == npos)
-            changes = &m_changes;
-        else if (table_ndx < m_info->tables.size())
-            changes = &m_info->tables[table_ndx];
-
-        std::vector<size_t> next_rows;
-        next_rows.reserve(m_tv.size());
-        for (size_t i = 0; i < m_tv.size(); ++i)
-            next_rows.push_back(m_tv[i].get_index());
-
-        util::Optional<IndexSet> move_candidates;
-        if (changes) {
-            auto const& moves = changes->moves;
-            for (auto& idx : m_previous_rows) {
-                if (changes->deletions.contains(idx)) {
-                    // check if this deletion was actually a move
-                    auto it = lower_bound(begin(moves), end(moves), idx,
-                                          [](auto const& a, auto b) { return a.from < b; });
-                    idx = it != moves.end() && it->from == idx ? it->to : npos;
-                }
-                else
-                    idx = changes->insertions.shift(changes->deletions.unshift(idx));
-            }
-            if (m_target_is_in_table_order && !m_descriptor_ordering.will_apply_sort())
-                move_candidates = changes->insertions;
-        }
+        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_changes = CollectionChangeBuilder::calculate(m_previous_rows, next_rows,
-                                                       get_modification_checker(*m_info, *m_query->get_table()),
-                                                       move_candidates);
+        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);
         m_previous_rows = std::move(next_rows);
     }
     }
     else {
     else {
-        m_previous_rows.resize(m_tv.size());
-        for (size_t i = 0; i < m_tv.size(); ++i)
-            m_previous_rows[i] = m_tv[i].get_index();
+        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()
 void ResultsNotifier::run()
 {
 {
     // Table's been deleted, so report all rows as deleted
     // Table's been deleted, so report all rows as deleted
-    if (!m_query->get_table()->is_attached()) {
-        m_changes = {};
-        m_changes.deletions.set(m_previous_rows.size());
+    if (!m_query->get_table()) {
+        m_change = {};
+        m_change.deletions.set(m_previous_rows.size());
         m_previous_rows.clear();
         m_previous_rows.clear();
         return;
         return;
     }
     }
@@ -173,80 +155,200 @@ void ResultsNotifier::run()
         return;
         return;
 
 
     m_query->sync_view_if_needed();
     m_query->sync_view_if_needed();
-    m_tv = m_query->find_all();
-    m_tv.apply_descriptor_ordering(m_descriptor_ordering);
-    m_last_seen_version = m_tv.sync_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();
     calculate_changes();
 }
 }
 
 
-void ResultsNotifier::do_prepare_handover(SharedGroup& sg)
+void ResultsNotifier::do_prepare_handover(Transaction& sg)
 {
 {
-    if (!m_tv.is_attached()) {
-        // if the table version didn't change we can just reuse the same handover
-        // object and bump its version to the current SG version
-        if (m_tv_handover)
-            m_tv_handover->version = sg.get_version_of_current_transaction();
+    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 = {};
+    }
+}
 
 
-        // add_changes() needs to be called even if there are no changes to
-        // clear the skip flag on the callbacks
-        add_changes(std::move(m_changes));
-        return;
+bool ResultsNotifier::prepare_to_deliver()
+{
+    auto lock = lock_target();
+    if (!get_realm()) {
+        m_handover_tv.reset();
+        m_delivered_tv.reset();
+        return false;
     }
     }
+    if (!m_handover_tv)
+        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;
+}
 
 
-    REALM_ASSERT(m_tv.is_in_sync());
+void ResultsNotifier::do_attach_to(Transaction& sg)
+{
+    if (m_query->get_table())
+        m_query = sg.import_copy_of(*m_query, PayloadPolicy::Move);
+}
 
 
-    m_tv_handover = sg.export_for_handover(m_tv, MutableSourcePayload::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;
+    }
 
 
-    add_changes(std::move(m_changes));
-    REALM_ASSERT(m_changes.empty());
+}
 
 
-    // detach the TableView as we won't need it again and keeping it around
-    // makes advance_read() much more expensive
-    m_tv = {};
+void ListResultsNotifier::release_data() noexcept
+{
+    m_list = {};
+    CollectionNotifier::release_data();
 }
 }
 
 
-void ResultsNotifier::deliver(SharedGroup& sg)
+bool ListResultsNotifier::get_list_indices(ListIndices& out)
 {
 {
-    auto lock = lock_target();
+    if (!m_delivered_indices)
+        return false;
+    auto& transaction = source_shared_group();
+    if (m_delivered_transaction_version != transaction.get_version_of_current_transaction())
+        return false;
 
 
-    // Target realm being null here indicates that we were unregistered while we
-    // were in the process of advancing the Realm version and preparing for
-    // delivery, i.e. the results was destroyed from the "wrong" thread
-    if (!get_realm()) {
-        return;
+    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;
     }
     }
 
 
-    REALM_ASSERT(!m_query_handover);
-    if (m_tv_to_deliver) {
-        Results::Internal::set_table_view(*m_target_results,
-                                          std::move(*sg.import_from_handover(std::move(m_tv_to_deliver))));
+    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));
+        });
     }
     }
-    REALM_ASSERT(!m_tv_to_deliver);
+
+    m_previous_indices = *m_run_indices;
 }
 }
 
 
-bool ResultsNotifier::prepare_to_deliver()
+void ListResultsNotifier::run()
 {
 {
-    auto lock = lock_target();
-    if (!get_realm())
-        return false;
-    m_tv_to_deliver = std::move(m_tv_handover);
-    return true;
+    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 ResultsNotifier::do_attach_to(SharedGroup& sg)
+void ListResultsNotifier::do_prepare_handover(Transaction& sg)
 {
 {
-    REALM_ASSERT(m_query_handover);
-    m_query = sg.import_from_handover(std::move(m_query_handover));
-    m_descriptor_ordering = DescriptorOrdering::create_from_and_consume_patch(m_ordering_handover, *m_query->get_table());
+    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();
 }
 }
 
 
-void ResultsNotifier::do_detach_from(SharedGroup& sg)
+bool ListResultsNotifier::prepare_to_deliver()
 {
 {
-    REALM_ASSERT(m_query);
-    REALM_ASSERT(!m_tv.is_attached());
+    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;
+}
 
 
-    DescriptorOrdering::generate_patch(m_descriptor_ordering, m_ordering_handover);
-    m_query_handover = sg.export_for_handover(*m_query, MutableSourcePayload::Move);
-    m_query = nullptr;
+void ListResultsNotifier::do_attach_to(Transaction& sg)
+{
+    if (m_list->is_attached())
+        m_list = sg.import_copy_of(*m_list);
 }
 }

+ 58 - 23
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/results_notifier.hpp

@@ -22,58 +22,93 @@
 #include "collection_notifier.hpp"
 #include "collection_notifier.hpp"
 #include "results.hpp"
 #include "results.hpp"
 
 
-#include <realm/group_shared.hpp>
+#include <realm/db.hpp>
 
 
 namespace realm {
 namespace realm {
 namespace _impl {
 namespace _impl {
-class ResultsNotifier : public CollectionNotifier {
+class ResultsNotifierBase : public CollectionNotifier {
 public:
 public:
-    ResultsNotifier(Results& target);
+    using ListIndices = util::Optional<std::vector<size_t>>;
+    using CollectionNotifier::CollectionNotifier;
 
 
-    void target_results_moved(Results& old_target, Results& new_target);
+    virtual bool get_tableview(TableView&) { return false; }
+    virtual bool get_list_indices(ListIndices&) { return false; }
+};
 
 
-private:
-    // Target Results to update
-    // Can only be used with lock_target() held
-    Results* m_target_results;
+class ResultsNotifier : public ResultsNotifierBase {
+public:
+    ResultsNotifier(Results& target);
+    bool get_tableview(TableView& out) override;
 
 
-    // The source Query, in handover form iff m_sg is null
-    std::unique_ptr<SharedGroup::Handover<Query>> m_query_handover;
+private:
     std::unique_ptr<Query> m_query;
     std::unique_ptr<Query> m_query;
-
-    DescriptorOrdering::HandoverPatch m_ordering_handover;
     DescriptorOrdering m_descriptor_ordering;
     DescriptorOrdering m_descriptor_ordering;
     bool m_target_is_in_table_order;
     bool m_target_is_in_table_order;
 
 
     // The TableView resulting from running the query. Will be detached unless
     // 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
     // the query was (re)run since the last time the handover object was created
-    TableView m_tv;
-    std::unique_ptr<SharedGroup::Handover<TableView>> m_tv_handover;
-    std::unique_ptr<SharedGroup::Handover<TableView>> m_tv_to_deliver;
+    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
     // 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.
     // rerunning the query when there's no chance of it changing.
-    uint_fast64_t m_last_seen_version = -1;
+    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
     // The rows from the previous run of the query, for calculating diffs
-    std::vector<size_t> m_previous_rows;
+    std::vector<size_t> m_previous_indices;
 
 
-    // The changeset calculated during run() and delivered in do_prepare_handover()
-    CollectionChangeBuilder m_changes;
     TransactionChangeInfo* m_info = nullptr;
     TransactionChangeInfo* m_info = nullptr;
+    bool m_results_were_used = true;
 
 
     bool need_to_run();
     bool need_to_run();
     void calculate_changes();
     void calculate_changes();
-    void deliver(SharedGroup&) override;
 
 
     void run() override;
     void run() override;
-    void do_prepare_handover(SharedGroup&) override;
+    void do_prepare_handover(Transaction&) override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;
     bool do_add_required_change_info(TransactionChangeInfo& info) override;
     bool prepare_to_deliver() override;
     bool prepare_to_deliver() override;
 
 
     void release_data() noexcept override;
     void release_data() noexcept override;
-    void do_attach_to(SharedGroup& sg) override;
-    void do_detach_from(SharedGroup& sg) override;
+    void do_attach_to(Transaction& sg) override;
 };
 };
 
 
 } // namespace _impl
 } // namespace _impl

+ 148 - 459
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.cpp

@@ -23,8 +23,7 @@
 #include "index_set.hpp"
 #include "index_set.hpp"
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
-#include <realm/group_shared.hpp>
-#include <realm/lang_bind_helper.hpp>
+#include <realm/db.hpp>
 
 
 #include <algorithm>
 #include <algorithm>
 #include <numeric>
 #include <numeric>
@@ -37,8 +36,8 @@ class KVOAdapter : public _impl::TransactionChangeInfo {
 public:
 public:
     KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context);
     KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context);
 
 
-    void before(SharedGroup& sg);
-    void after(SharedGroup& sg);
+    void before(Transaction& sg);
+    void after(Transaction& sg);
 
 
 private:
 private:
     BindingContext* m_context;
     BindingContext* m_context;
@@ -48,13 +47,10 @@ private:
     struct ListInfo {
     struct ListInfo {
         BindingContext::ObserverState* observer;
         BindingContext::ObserverState* observer;
         _impl::CollectionChangeBuilder builder;
         _impl::CollectionChangeBuilder builder;
-        size_t col;
-        size_t initial_size;
+        ColKey col;
     };
     };
     std::vector<ListInfo> m_lists;
     std::vector<ListInfo> m_lists;
     VersionID m_version;
     VersionID m_version;
-
-    size_t new_table_ndx(size_t ndx) const { return ndx < table_indices.size() ? table_indices[ndx] : ndx; }
 };
 };
 
 
 KVOAdapter::KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context)
 KVOAdapter::KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context)
@@ -65,40 +61,33 @@ KVOAdapter::KVOAdapter(std::vector<BindingContext::ObserverState>& observers, Bi
     if (m_observers.empty())
     if (m_observers.empty())
         return;
         return;
 
 
-    std::vector<size_t> tables_needed;
+    std::vector<TableKey> tables_needed;
     for (auto& observer : observers) {
     for (auto& observer : observers) {
-        tables_needed.push_back(observer.table_ndx);
+        tables_needed.push_back(observer.table_key);
     }
     }
     std::sort(begin(tables_needed), end(tables_needed));
     std::sort(begin(tables_needed), end(tables_needed));
-    tables_needed.erase(std::unique(begin(tables_needed), end(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 realm = context->realm.lock();
     auto& group = realm->read_group();
     auto& group = realm->read_group();
     for (auto& observer : observers) {
     for (auto& observer : observers) {
-        auto table = group.get_table(observer.table_ndx);
-        for (size_t i = 0, count = table->get_column_count(); i < count; ++i) {
-            auto type = table->get_column_type(i);
-            if (type == type_LinkList)
-                m_lists.push_back({&observer, {}, i, size_t(-1)});
-            else if (type == type_Table)
-                m_lists.push_back({&observer, {}, i, table->get_subtable_size(i, observer.row_ndx)});
+        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});
         }
         }
     }
     }
 
 
-    auto max = std::max_element(begin(tables_needed), end(tables_needed));
-    if (*max >= table_modifications_needed.size())
-        table_modifications_needed.resize(*max + 1, false);
-    if (*max >= table_moves_needed.size())
-        table_moves_needed.resize(*max + 1, false);
-    for (auto& tbl : tables_needed) {
-        table_modifications_needed[tbl] = true;
-        table_moves_needed[tbl] = true;
-    }
+    tables.reserve(tables_needed.size());
+    for (auto& tbl : tables_needed)
+        tables[tbl.value] = {};
     for (auto& list : m_lists)
     for (auto& list : m_lists)
-        lists.push_back({list.observer->table_ndx, list.observer->row_ndx, list.col, &list.builder});
+        lists.push_back({list.observer->table_key,
+            list.observer->obj_key, list.col.value, &list.builder});
 }
 }
 
 
-void KVOAdapter::before(SharedGroup& sg)
+void KVOAdapter::before(Transaction& sg)
 {
 {
     if (!m_context)
     if (!m_context)
         return;
         return;
@@ -108,37 +97,20 @@ void KVOAdapter::before(SharedGroup& sg)
         return;
         return;
 
 
     for (auto& observer : m_observers) {
     for (auto& observer : m_observers) {
-        size_t table_ndx = new_table_ndx(observer.table_ndx);
-        if (table_ndx >= tables.size())
+        auto it = tables.find(observer.table_key.value);
+        if (it == tables.end())
             continue;
             continue;
 
 
-        auto const& table = tables[table_ndx];
-        auto const& moves = table.moves;
-        auto idx = observer.row_ndx;
-        auto it = lower_bound(begin(moves), end(moves), idx,
-                              [](auto const& a, auto b) { return a.from < b; });
-        if (it != moves.end() && it->from == idx)
-            idx = it->to;
-        else if (table.deletions.contains(idx)) {
+        auto const& table = it->second;
+        auto key = observer.obj_key;
+        if (table.deletions_contains(key)) {
             m_invalidated.push_back(observer.info);
             m_invalidated.push_back(observer.info);
             continue;
             continue;
         }
         }
-        else
-            idx = table.insertions.shift(table.deletions.unshift(idx));
-        if (table.modifications.contains(idx)) {
-            observer.changes.resize(table.columns.size());
-            size_t i = 0;
-            for (auto& c : table.columns) {
-                auto& change = observer.changes[i];
-                if (table_ndx >= column_indices.size() || column_indices[table_ndx].empty())
-                    change.initial_column_index = i;
-                else if (i >= column_indices[table_ndx].size())
-                    change.initial_column_index = i - column_indices[table_ndx].size() + column_indices[table_ndx].back() + 1;
-                else
-                    change.initial_column_index = column_indices[table_ndx][i];
-                if (change.initial_column_index != npos && c.contains(idx))
-                    change.kind = BindingContext::ColumnInfo::Kind::Set;
-                ++i;
+        auto column_modifications = table.get_columns_modified(key);
+        if (column_modifications) {
+            for (auto col : *column_modifications) {
+                observer.changes[col].kind = BindingContext::ColumnInfo::Kind::Set;
             }
             }
         }
         }
     }
     }
@@ -148,19 +120,19 @@ void KVOAdapter::before(SharedGroup& sg)
             // We may have pre-emptively marked the column as modified if the
             // We may have pre-emptively marked the column as modified if the
             // LinkList was selected but the actual changes made ended up being
             // LinkList was selected but the actual changes made ended up being
             // a no-op
             // a no-op
-            if (list.col < list.observer->changes.size())
-                list.observer->changes[list.col].kind = BindingContext::ColumnInfo::Kind::None;
+            list.observer->changes.erase(list.col.value);
             continue;
             continue;
         }
         }
         // If the containing row was deleted then changes will be empty
         // If the containing row was deleted then changes will be empty
         if (list.observer->changes.empty()) {
         if (list.observer->changes.empty()) {
-            REALM_ASSERT_DEBUG(tables[new_table_ndx(list.observer->table_ndx)].deletions.contains(list.observer->row_ndx));
+            REALM_ASSERT_DEBUG(tables[list.observer->table_key.value].deletions_contains(list.observer->obj_key));
             continue;
             continue;
         }
         }
         // otherwise the column should have been marked as modified
         // otherwise the column should have been marked as modified
-        REALM_ASSERT(list.col < list.observer->changes.size());
+        auto it = list.observer->changes.find(list.col.value);
+        REALM_ASSERT(it != list.observer->changes.end());
         auto& builder = list.builder;
         auto& builder = list.builder;
-        auto& changes = list.observer->changes[list.col];
+        auto& changes = it->second;
 
 
         builder.modifications.remove(builder.insertions);
         builder.modifications.remove(builder.insertions);
 
 
@@ -212,18 +184,13 @@ void KVOAdapter::before(SharedGroup& sg)
         else {
         else {
             REALM_ASSERT(!builder.deletions.empty());
             REALM_ASSERT(!builder.deletions.empty());
             changes.kind = BindingContext::ColumnInfo::Kind::Remove;
             changes.kind = BindingContext::ColumnInfo::Kind::Remove;
-            // Table clears don't come with the size, so we need to fix up the
-            // notification to make it just delete all rows that actually existed
-            if (std::prev(builder.deletions.end())->second > list.initial_size)
-                changes.indices.set(list.initial_size);
-            else
-                changes.indices = builder.deletions;
+            changes.indices = builder.deletions;
         }
         }
     }
     }
     m_context->will_change(m_observers, m_invalidated);
     m_context->will_change(m_observers, m_invalidated);
 }
 }
 
 
-void KVOAdapter::after(SharedGroup& sg)
+void KVOAdapter::after(Transaction& sg)
 {
 {
     if (!m_context)
     if (!m_context)
         return;
         return;
@@ -232,44 +199,9 @@ void KVOAdapter::after(SharedGroup& sg)
                           m_version != sg.get_version_of_current_transaction());
                           m_version != sg.get_version_of_current_transaction());
 }
 }
 
 
-template<typename Derived>
-struct MarkDirtyMixin  {
-    bool mark_dirty(size_t row, size_t col, _impl::Instruction instr=_impl::instr_Set)
-    {
-        // Ignore SetDefault and SetUnique as those conceptually cannot be
-        // changes to existing rows
-        if (instr == _impl::instr_Set)
-            static_cast<Derived *>(this)->mark_dirty(row, col);
-        return true;
-    }
-
-    bool set_int(size_t c, size_t r, int_fast64_t, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); }
-    bool set_bool(size_t c, size_t r, bool, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_float(size_t c, size_t r, float, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_double(size_t c, size_t r, double, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_string(size_t c, size_t r, StringData, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); }
-    bool set_binary(size_t c, size_t r, BinaryData, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_olddatetime(size_t c, size_t r, OldDateTime, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_timestamp(size_t c, size_t r, Timestamp, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_table(size_t c, size_t r, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_mixed(size_t c, size_t r, const Mixed&, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_link(size_t c, size_t r, size_t, size_t, _impl::Instruction i) { return mark_dirty(r, c, i); }
-    bool set_null(size_t c, size_t r, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); }
-
-    bool add_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); }
-    bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); }
-    bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
-    bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
-
-    bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; }
-    bool set_string_unique(size_t, size_t, size_t, StringData) { return true; }
-
-    bool add_row_with_key(size_t, size_t, size_t, int64_t) { return true; }
-};
-
 class TransactLogValidationMixin {
 class TransactLogValidationMixin {
-    // Index of currently selected table
-    size_t m_current_table = 0;
+    // The currently selected table
+    TableKey m_current_table;
 
 
     REALM_NORETURN
     REALM_NORETURN
     REALM_NOINLINE
     REALM_NOINLINE
@@ -279,139 +211,62 @@ class TransactLogValidationMixin {
     }
     }
 
 
 protected:
 protected:
-    size_t current_table() const noexcept { return m_current_table; }
+    TableKey current_table() const noexcept { return m_current_table; }
 
 
 public:
 public:
 
 
-    bool select_table(size_t group_level_ndx, int, const size_t*) noexcept
+    bool select_table(TableKey key) noexcept
     {
     {
-        m_current_table = group_level_ndx;
+        m_current_table = key;
         return true;
         return true;
     }
     }
 
 
     // Removing or renaming things while a Realm is open is never supported
     // Removing or renaming things while a Realm is open is never supported
-    bool erase_group_level_table(size_t, size_t) { schema_error(); }
-    bool rename_group_level_table(size_t, StringData) { schema_error(); }
-    bool erase_column(size_t) { schema_error(); }
-    bool erase_link_column(size_t, size_t, size_t) { schema_error(); }
-    bool rename_column(size_t, StringData) { schema_error(); }
-
-    // Schema changes which don't involve a change in the schema version are
-    // allowed
-    bool add_search_index(size_t) { return true; }
-    bool remove_search_index(size_t) { return true; }
+    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
     // Additive changes and reorderings are supported
-    bool insert_group_level_table(size_t, size_t, StringData) { return true; }
-    bool insert_column(size_t, DataType, StringData, bool) { return true; }
-    bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return true; }
-    bool set_link_type(size_t, LinkType) { return true; }
-    bool move_column(size_t, size_t) { return true; }
-    bool move_group_level_table(size_t, size_t) { return true; }
+    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
     // Non-schema changes are all allowed
     void parse_complete() { }
     void parse_complete() { }
-    bool select_descriptor(int, const size_t*) { return true; }
-    bool select_link_list(size_t, size_t, size_t) { return true; }
-    bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; }
-    bool erase_rows(size_t, size_t, size_t, bool) { return true; }
-    bool swap_rows(size_t, size_t) { return true; }
-    bool move_row(size_t, size_t) { return true; }
+    bool create_object(ObjKey) { return true; }
+    bool remove_object(ObjKey) { return true; }
     bool clear_table(size_t=0) noexcept { return true; }
     bool clear_table(size_t=0) noexcept { return true; }
-    bool link_list_set(size_t, size_t, size_t) { return true; }
-    bool link_list_insert(size_t, size_t, size_t) { return true; }
-    bool link_list_erase(size_t, size_t) { return true; }
-    bool link_list_nullify(size_t, size_t) { return true; }
-    bool link_list_clear(size_t) { return true; }
-    bool link_list_move(size_t, size_t) { return true; }
-    bool link_list_swap(size_t, size_t) { return true; }
-    bool merge_rows(size_t, size_t) { return true; }
-    bool optimize_table() { 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
 // A transaction log handler that just validates that all operations made are
 // ones supported by the object store
 // ones supported by the object store
-struct TransactLogValidator : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogValidator> {
-    void mark_dirty(size_t, size_t) { }
+struct TransactLogValidator : public TransactLogValidationMixin {
+    bool modify_object(ColKey, ObjKey) { return true; }
+    bool select_list(ColKey, ObjKey) { return true; }
 };
 };
 
 
-// Move the value at container[from] to container[to], shifting everything in
-// between, or do nothing if either are out of bounds
-template<typename Container>
-void rotate(Container& container, size_t from, size_t to)
-{
-    REALM_ASSERT(from != to);
-    if (from >= container.size() && to >= container.size())
-        return;
-    if (from >= container.size() || to >= container.size())
-        container.resize(std::max(from, to) + 1);
-    if (from < to)
-        std::rotate(begin(container) + from, begin(container) + from + 1, begin(container) + to + 1);
-    else
-        std::rotate(begin(container) + to, begin(container) + from, begin(container) + from + 1);
-}
-
-// Insert a default-initialized value at pos if there is anything after pos in the container.
-template<typename Container>
-void insert_empty_at(Container& container, size_t pos)
-{
-    if (pos < container.size())
-        container.emplace(container.begin() + pos);
-}
-
-// Shift `value` to reflect a move from `from` to `to`
-void adjust_for_move(size_t& value, size_t from, size_t to)
-{
-    if (value == from)
-        value = to;
-    else if (value > from && value <= to)
-        --value;
-    else if (value < from && value >= to)
-        ++value;
-}
-
-void adjust_for_move(std::vector<size_t>& values, size_t from, size_t to)
-{
-    for (auto& value : values)
-        adjust_for_move(value, from, to);
-}
-
-void expand_to(std::vector<size_t>& cols, size_t i)
-{
-    auto old_size = cols.size();
-    if (old_size > i)
-        return;
-
-    cols.resize(std::max(old_size * 2, i + 1));
-    std::iota(begin(cols) + old_size, end(cols), old_size == 0 ? 0 : cols[old_size - 1] + 1);
-}
-
-void adjust_ge(std::vector<size_t>& values, size_t i)
-{
-    for (auto& value : values) {
-        if (value >= i)
-            ++value;
-    }
-}
-
 // Extends TransactLogValidator to track changes made to LinkViews
 // Extends TransactLogValidator to track changes made to LinkViews
-class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogObserver> {
+class TransactLogObserver : public TransactLogValidationMixin {
     _impl::TransactionChangeInfo& m_info;
     _impl::TransactionChangeInfo& m_info;
     _impl::CollectionChangeBuilder* m_active_list = nullptr;
     _impl::CollectionChangeBuilder* m_active_list = nullptr;
-    _impl::CollectionChangeBuilder* m_active_table = nullptr;
-    _impl::CollectionChangeBuilder* m_active_descriptor = nullptr;
+    ObjectChangeSet* m_active_table = nullptr;
 
 
-    bool m_need_move_info = false;
-    bool m_is_top_level_table = true;
-
-    _impl::CollectionChangeBuilder* find_list(size_t tbl, size_t col, size_t row)
+    _impl::CollectionChangeBuilder* find_list(ObjKey obj, ColKey col)
     {
     {
         // When there are multiple source versions there could be multiple
         // When there are multiple source versions there could be multiple
         // change objects for a single LinkView, in which case we need to use
         // change objects for a single LinkView, in which case we need to use
         // the last one
         // the last one
+        auto table = current_table();
         for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) {
         for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) {
-            if (it->table_ndx == tbl && it->row_ndx == row && it->col_ndx == col)
+            if (it->table_key == table && it->row_key == obj.value && it->col_key == col.value)
                 return it->changes;
                 return it->changes;
         }
         }
         return nullptr;
         return nullptr;
@@ -421,322 +276,160 @@ public:
     TransactLogObserver(_impl::TransactionChangeInfo& info)
     TransactLogObserver(_impl::TransactionChangeInfo& info)
     : m_info(info) { }
     : m_info(info) { }
 
 
-    void mark_dirty(size_t row, size_t col)
-    {
-        if (m_active_table)
-            m_active_table->modify(row, col);
-    }
-
     void parse_complete()
     void parse_complete()
     {
     {
-        for (auto& table : m_info.tables)
-            table.parse_complete();
         for (auto& list : m_info.lists)
         for (auto& list : m_info.lists)
             list.changes->clean_up_stale_moves();
             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_descriptor(int levels, const size_t*) noexcept
-    {
-        if (levels == 0) // schema of selected table is being modified
-            m_active_descriptor = m_active_table;
-        else // schema of subtable is being modified; currently don't need to track this
-            m_active_descriptor = nullptr;
-        return true;
-    }
-
-    bool select_table(size_t group_level_ndx, int len, size_t const* path) noexcept
+    bool select_table(TableKey key) noexcept
     {
     {
-        TransactLogValidationMixin::select_table(group_level_ndx, len, path);
-        m_active_table = nullptr;
-        m_is_top_level_table = true;
+        TransactLogValidationMixin::select_table(key);
 
 
-        // Nested subtables currently not supported
-        if (len > 1) {
-            m_is_top_level_table = false;
-            return true;
-        }
-
-        auto tbl_ndx = current_table();
-        if (!m_info.track_all && (tbl_ndx >= m_info.table_modifications_needed.size() || !m_info.table_modifications_needed[tbl_ndx]))
-            return true;
-
-        m_need_move_info = m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() &&
-                                                m_info.table_moves_needed[tbl_ndx]);
-        if (m_info.tables.size() <= tbl_ndx)
-            m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1));
-        m_active_table = &m_info.tables[tbl_ndx];
-
-        if (len == 1) {
-            // Mark the cell containing the subtable as modified since selecting
-            // a table is always followed by a modification of some sort
-            size_t col = path[0];
-            size_t row = path[1];
-            mark_dirty(row, col);
-
-            m_active_table = nullptr;
-            m_is_top_level_table = false;
-            if (auto table = find_list(current_table(), col, row)) {
-                m_active_table = table;
-                m_need_move_info = true;
-            }
+        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;
         return true;
     }
     }
 
 
-    bool select_link_list(size_t col, size_t row, size_t)
+    bool select_list(ColKey col, ObjKey obj)
     {
     {
-        mark_dirty(row, col);
-        m_active_list = find_list(current_table(), col, row);
+        modify_object(col, obj);
+        m_active_list = find_list(obj, col);
         return true;
         return true;
     }
     }
 
 
-    bool link_list_set(size_t index, size_t, size_t)
+    bool list_set(size_t index)
     {
     {
         if (m_active_list)
         if (m_active_list)
             m_active_list->modify(index);
             m_active_list->modify(index);
         return true;
         return true;
     }
     }
 
 
-    bool link_list_insert(size_t index, size_t, size_t)
+    bool list_insert(size_t index)
     {
     {
         if (m_active_list)
         if (m_active_list)
             m_active_list->insert(index);
             m_active_list->insert(index);
         return true;
         return true;
     }
     }
 
 
-    bool link_list_erase(size_t index, size_t)
+    bool list_erase(size_t index)
     {
     {
         if (m_active_list)
         if (m_active_list)
             m_active_list->erase(index);
             m_active_list->erase(index);
         return true;
         return true;
     }
     }
 
 
-    bool link_list_nullify(size_t index, size_t prior_size)
-    {
-        return link_list_erase(index, prior_size);
-    }
-
-    bool link_list_swap(size_t index1, size_t index2)
+    bool list_swap(size_t index1, size_t index2)
     {
     {
-        link_list_set(index1, 0, npos);
-        link_list_set(index2, 0, npos);
+        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;
         return true;
     }
     }
 
 
-    bool link_list_clear(size_t old_size)
+    bool list_clear(size_t old_size)
     {
     {
         if (m_active_list)
         if (m_active_list)
             m_active_list->clear(old_size);
             m_active_list->clear(old_size);
         return true;
         return true;
     }
     }
 
 
-    bool link_list_move(size_t from, size_t to)
+    bool list_move(size_t from, size_t to)
     {
     {
         if (m_active_list)
         if (m_active_list)
             m_active_list->move(from, to);
             m_active_list->move(from, to);
         return true;
         return true;
     }
     }
 
 
-    bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t, bool)
+    bool create_object(ObjKey key)
     {
     {
         if (m_active_table)
         if (m_active_table)
-            m_active_table->insert(row_ndx, num_rows_to_insert, m_need_move_info);
-        if (!m_is_top_level_table)
-            return true;
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx == current_table() && list.row_ndx >= row_ndx)
-                list.row_ndx += num_rows_to_insert;
-        }
-        return true;
-    }
-
-    bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t)
-    {
-        insert_empty_rows(row_ndx, 1, prior_num_rows, false);
+            m_active_table->insertions_add(key.value);
         return true;
         return true;
     }
     }
 
 
-    bool erase_rows(size_t row_ndx, size_t, size_t prior_num_rows, bool unordered)
+    bool remove_object(ObjKey key)
     {
     {
-        if (!unordered) {
-            if (m_active_table)
-                m_active_table->erase(row_ndx);
+        if (!m_active_table)
             return true;
             return true;
-        }
-        size_t last_row = prior_num_rows - 1;
-        if (m_active_table)
-            m_active_table->move_over(row_ndx, last_row, m_need_move_info);
+        if (!m_active_table->insertions_remove(key.value))
+            m_active_table->deletions_add(key.value);
+        m_active_table->modifications_remove(key.value);
 
 
-        if (!m_is_top_level_table)
-            return true;
         for (size_t i = 0; i < m_info.lists.size(); ++i) {
         for (size_t i = 0; i < m_info.lists.size(); ++i) {
             auto& list = m_info.lists[i];
             auto& list = m_info.lists[i];
-            if (list.table_ndx != current_table())
+            if (list.table_key != current_table())
                 continue;
                 continue;
-            if (list.row_ndx == row_ndx) {
+            if (list.row_key == key.value) {
                 if (i + 1 < m_info.lists.size())
                 if (i + 1 < m_info.lists.size())
                     m_info.lists[i] = std::move(m_info.lists.back());
                     m_info.lists[i] = std::move(m_info.lists.back());
                 m_info.lists.pop_back();
                 m_info.lists.pop_back();
                 continue;
                 continue;
             }
             }
-            if (list.row_ndx == last_row)
-                list.row_ndx = row_ndx;
-        }
-
-        return true;
-    }
-
-    bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) {
-        REALM_ASSERT(row_ndx_1 < row_ndx_2);
-        if (!m_is_top_level_table) {
-            if (m_active_table) {
-                m_active_table->move(row_ndx_1, row_ndx_2);
-                if (row_ndx_1 + 1 != row_ndx_2)
-                    m_active_table->move(row_ndx_2 - 1, row_ndx_1);
-            }
-            return true;
-        }
-
-        if (m_active_table)
-            m_active_table->swap(row_ndx_1, row_ndx_2, m_need_move_info);
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx == current_table()) {
-                if (list.row_ndx == row_ndx_1)
-                    list.row_ndx = row_ndx_2;
-                else if (list.row_ndx == row_ndx_2)
-                    list.row_ndx = row_ndx_1;
-            }
         }
         }
-        return true;
-    }
-
-    bool move_row(size_t from_ndx, size_t to_ndx) {
-        // Move row is not supported for top level tables:
-        REALM_ASSERT(!m_active_table || !m_is_top_level_table);
 
 
-        if (m_active_table)
-            m_active_table->move(from_ndx, to_ndx);
         return true;
         return true;
     }
     }
 
 
-    bool merge_rows(size_t from, size_t to)
+    bool modify_object(ColKey col, ObjKey key)
     {
     {
         if (m_active_table)
         if (m_active_table)
-            m_active_table->subsume(from, to, m_need_move_info);
-        if (!m_is_top_level_table)
-            return true;
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx == current_table() && list.row_ndx == from)
-                list.row_ndx = to;
-        }
+            m_active_table->modifications_add(key.value, col.value);
         return true;
         return true;
     }
     }
 
 
-    bool clear_table(size_t=0)
+    bool clear_table(size_t old_size)
     {
     {
-        auto tbl_ndx = current_table();
+        auto cur_table = current_table();
         if (m_active_table)
         if (m_active_table)
-            m_active_table->clear(std::numeric_limits<size_t>::max());
-        if (!m_is_top_level_table)
-            return true;
+            m_active_table->clear(old_size);
         auto it = remove_if(begin(m_info.lists), end(m_info.lists),
         auto it = remove_if(begin(m_info.lists), end(m_info.lists),
-                            [&](auto const& lv) { return lv.table_ndx == tbl_ndx; });
+                            [&](auto const& lv) { return lv.table_key == cur_table; });
         m_info.lists.erase(it, end(m_info.lists));
         m_info.lists.erase(it, end(m_info.lists));
         return true;
         return true;
     }
     }
 
 
-    bool insert_column(size_t ndx, DataType, StringData, bool)
+    bool insert_column(ColKey)
     {
     {
         m_info.schema_changed = true;
         m_info.schema_changed = true;
-
-        if (m_active_descriptor)
-            m_active_descriptor->insert_column(ndx);
-        if (m_active_descriptor != m_active_table || !m_is_top_level_table)
-            return true;
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx == current_table() && list.col_ndx >= ndx)
-                ++list.col_ndx;
-        }
-        if (m_info.column_indices.size() <= current_table())
-            m_info.column_indices.resize(current_table() + 1);
-        auto& indices = m_info.column_indices[current_table()];
-        expand_to(indices, ndx);
-        insert_empty_at(indices, ndx);
-        indices[ndx] = npos;
         return true;
         return true;
     }
     }
 
 
-    void prepare_table_indices()
-    {
-        if (m_info.table_indices.empty() && !m_info.table_modifications_needed.empty()) {
-            m_info.table_indices.resize(m_info.table_modifications_needed.size());
-            std::iota(begin(m_info.table_indices), end(m_info.table_indices), 0);
-        }
-    }
-
-    bool insert_group_level_table(size_t ndx, size_t, StringData)
+    bool insert_group_level_table(TableKey)
     {
     {
         m_info.schema_changed = true;
         m_info.schema_changed = true;
-
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx >= ndx)
-                ++list.table_ndx;
-        }
-        prepare_table_indices();
-        adjust_ge(m_info.table_indices, ndx);
-        insert_empty_at(m_info.tables, ndx);
-        insert_empty_at(m_info.table_moves_needed, ndx);
-        insert_empty_at(m_info.table_modifications_needed, ndx);
         return true;
         return true;
     }
     }
-
-    bool move_column(size_t from, size_t to)
-    {
-        m_info.schema_changed = true;
-
-        if (m_active_descriptor)
-            m_active_descriptor->move_column(from, to);
-        if (m_active_descriptor != m_active_table || !m_is_top_level_table)
-            return true;
-        for (auto& list : m_info.lists) {
-            if (list.table_ndx == current_table())
-                adjust_for_move(list.col_ndx, from, to);
-        }
-        if (m_info.column_indices.size() <= current_table())
-            m_info.column_indices.resize(current_table() + 1);
-        expand_to(m_info.column_indices[current_table()], std::max(from, to) + 1);
-        rotate(m_info.column_indices[current_table()], from, to);
-        return true;
-    }
-
-    bool move_group_level_table(size_t from, size_t to)
-    {
-        m_info.schema_changed = true;
-
-        for (auto& list : m_info.lists)
-            adjust_for_move(list.table_ndx, from, to);
-
-        prepare_table_indices();
-        adjust_for_move(m_info.table_indices, from, to);
-        rotate(m_info.tables, from, to);
-        rotate(m_info.table_modifications_needed, from, to);
-        rotate(m_info.table_moves_needed, from, to);
-        return true;
-    }
-
-    bool insert_link_column(size_t ndx, DataType type, StringData name, size_t, size_t) { return insert_column(ndx, type, name, false); }
 };
 };
 
 
 class KVOTransactLogObserver : public TransactLogObserver {
 class KVOTransactLogObserver : public TransactLogObserver {
     KVOAdapter m_adapter;
     KVOAdapter m_adapter;
     _impl::NotifierPackage& m_notifiers;
     _impl::NotifierPackage& m_notifiers;
-    SharedGroup& m_sg;
+    Transaction& m_sg;
 
 
 public:
 public:
     KVOTransactLogObserver(std::vector<BindingContext::ObserverState>& observers,
     KVOTransactLogObserver(std::vector<BindingContext::ObserverState>& observers,
                            BindingContext* context,
                            BindingContext* context,
                            _impl::NotifierPackage& notifiers,
                            _impl::NotifierPackage& notifiers,
-                           SharedGroup& sg)
+                           Transaction& sg)
     : TransactLogObserver(m_adapter)
     : TransactLogObserver(m_adapter)
     , m_adapter(observers, context)
     , m_adapter(observers, context)
     , m_notifiers(notifiers)
     , m_notifiers(notifiers)
@@ -754,14 +447,14 @@ public:
         TransactLogObserver::parse_complete();
         TransactLogObserver::parse_complete();
         m_adapter.before(m_sg);
         m_adapter.before(m_sg);
 
 
-        using sgf = _impl::SharedGroupFriend;
-        m_notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(m_sg));
+        m_notifiers.package_and_wait(m_sg.get_version_of_latest_snapshot());
         m_notifiers.before_advance();
         m_notifiers.before_advance();
     }
     }
 };
 };
 
 
 template<typename Func>
 template<typename Func>
-void advance_with_notifications(BindingContext* context, const std::unique_ptr<SharedGroup>& sg,
+void advance_with_notifications(BindingContext* context,
+                                const std::shared_ptr<Transaction>& sg,
                                 Func&& func, _impl::NotifierPackage& notifiers)
                                 Func&& func, _impl::NotifierPackage& notifiers)
 {
 {
     auto old_version = sg->get_version_of_current_transaction();
     auto old_version = sg->get_version_of_current_transaction();
@@ -775,31 +468,34 @@ void advance_with_notifications(BindingContext* context, const std::unique_ptr<S
     // version we're going to before we actually advance to that version
     // version we're going to before we actually advance to that version
     if (observers.empty() && (!notifiers || notifiers.version())) {
     if (observers.empty() && (!notifiers || notifiers.version())) {
         notifiers.before_advance();
         notifiers.before_advance();
-        func(TransactLogValidator());
+        TransactLogValidator validator;
+        func(&validator);
         auto new_version = sg->get_version_of_current_transaction();
         auto new_version = sg->get_version_of_current_transaction();
         if (context && old_version != new_version)
         if (context && old_version != new_version)
             context->did_change({}, {});
             context->did_change({}, {});
-        if (!sg) // did_change() could close the Realm. Just return if it does.
+        // did_change() could close the Realm. Just return if it does.
+        if (sg->get_transact_stage() == DB::transact_Ready)
             return;
             return;
         if (context)
         if (context)
             context->will_send_notifications();
             context->will_send_notifications();
-        if (!sg) // will_send_notifications() could close the Realm. Just return if it does.
+        // will_send_notifications() could close the Realm. Just return if it does.
+        if (sg->get_transact_stage() == DB::transact_Ready)
             return;
             return;
-        // did_change() can change the read version, and if it does we can't
-        // deliver notifiers
-        if (new_version == sg->get_version_of_current_transaction())
-            notifiers.deliver(*sg);
         notifiers.after_advance();
         notifiers.after_advance();
-        if (sg && context)
+        if (sg->get_transact_stage() == DB::transact_Ready)
+            return;
+        if (context)
             context->did_send_notifications();
             context->did_send_notifications();
         return;
         return;
     }
     }
 
 
     if (context)
     if (context)
         context->will_send_notifications();
         context->will_send_notifications();
-    func(KVOTransactLogObserver(observers, context, notifiers, *sg));
+    {
+        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.package_and_wait(sg->get_version_of_current_transaction().version); // is a no-op if parse_complete() was called
-    notifiers.deliver(*sg);
     notifiers.after_advance();
     notifiers.after_advance();
     if (context)
     if (context)
         context->did_send_notifications();
         context->did_send_notifications();
@@ -816,57 +512,50 @@ UnsupportedSchemaChange::UnsupportedSchemaChange()
 }
 }
 
 
 namespace transaction {
 namespace transaction {
-void advance(SharedGroup& sg, BindingContext*, VersionID version)
+void advance(Transaction& tr, BindingContext*, VersionID version)
 {
 {
-    LangBindHelper::advance_read(sg, TransactLogValidator(), version);
+    TransactLogValidator validator;
+    tr.advance_read(&validator, version);
 }
 }
 
 
-void advance(const std::unique_ptr<SharedGroup>& sg, BindingContext* context, NotifierPackage& notifiers)
+void advance(const std::shared_ptr<Transaction>& tr, BindingContext* context, NotifierPackage& notifiers)
 {
 {
-    advance_with_notifications(context, sg, [&](auto&&... args) {
-        LangBindHelper::advance_read(*sg, std::move(args)..., notifiers.version().value_or(VersionID{}));
+    advance_with_notifications(context, tr, [&](auto&&... args) {
+        tr->advance_read(std::move(args)..., notifiers.version().value_or(VersionID{}));
     }, notifiers);
     }, notifiers);
 }
 }
 
 
-void begin_without_validation(SharedGroup& sg)
-{
-    LangBindHelper::promote_to_write(sg);
-}
-
-void begin(const std::unique_ptr<SharedGroup>& sg, BindingContext* context, NotifierPackage& notifiers)
+void begin(const std::shared_ptr<Transaction>& tr, BindingContext* context, NotifierPackage& notifiers)
 {
 {
-    advance_with_notifications(context, sg, [&](auto&&... args) {
-        LangBindHelper::promote_to_write(*sg, std::move(args)...);
+    advance_with_notifications(context, tr, [&](auto&&... args) {
+        tr->promote_to_write(std::move(args)...);
     }, notifiers);
     }, notifiers);
 }
 }
 
 
-void commit(SharedGroup& sg)
-{
-    LangBindHelper::commit_and_continue_as_read(sg);
-}
-
-void cancel(SharedGroup& sg, BindingContext* context)
+void cancel(Transaction& tr, BindingContext* context)
 {
 {
     std::vector<BindingContext::ObserverState> observers;
     std::vector<BindingContext::ObserverState> observers;
     if (context) {
     if (context) {
         observers = context->get_observed_rows();
         observers = context->get_observed_rows();
     }
     }
     if (observers.empty()) {
     if (observers.empty()) {
-        LangBindHelper::rollback_and_continue_as_read(sg);
+        tr.rollback_and_continue_as_read();
         return;
         return;
     }
     }
 
 
     _impl::NotifierPackage notifiers;
     _impl::NotifierPackage notifiers;
-    LangBindHelper::rollback_and_continue_as_read(sg, KVOTransactLogObserver(observers, context, notifiers, sg));
+    KVOTransactLogObserver o(observers, context, notifiers, tr);
+    tr.rollback_and_continue_as_read(&o);
 }
 }
 
 
-void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version)
+void advance(Transaction& tr, TransactionChangeInfo& info, VersionID version)
 {
 {
-    if (!info.track_all && info.table_modifications_needed.empty() && info.lists.empty()) {
-        LangBindHelper::advance_read(sg, version);
+    if (!info.track_all && info.tables.empty() && info.lists.empty()) {
+        tr.advance_read(version);
     }
     }
     else {
     else {
-        LangBindHelper::advance_read(sg, TransactLogObserver(info), version);
+        TransactLogObserver o(info);
+        tr.advance_read(&o, version);
     }
     }
 }
 }
 
 

+ 7 - 10
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/transact_log_handler.hpp

@@ -27,7 +27,7 @@
 
 
 namespace realm {
 namespace realm {
 class BindingContext;
 class BindingContext;
-class SharedGroup;
+class Transaction;
 
 
 namespace _impl {
 namespace _impl {
 class NotifierPackage;
 class NotifierPackage;
@@ -40,24 +40,21 @@ struct UnsupportedSchemaChange : std::logic_error {
 namespace transaction {
 namespace transaction {
 // Advance the read transaction version, with change notifications sent to delegate
 // Advance the read transaction version, with change notifications sent to delegate
 // Must not be called from within a write transaction.
 // Must not be called from within a write transaction.
-void advance(const std::unique_ptr<SharedGroup>& sg, BindingContext* binding_context, NotifierPackage&);
-void advance(SharedGroup& sg, BindingContext* binding_context, VersionID);
+void advance(const std::shared_ptr<Transaction>& sg, BindingContext* binding_context, NotifierPackage&);
+void advance(Transaction& sg, BindingContext* binding_context, VersionID);
 
 
 // Begin a write transaction
 // Begin a write transaction
 // If the read transaction version is not up to date, will first advance to the
 // If the read transaction version is not up to date, will first advance to the
 // most recent read transaction and sent notifications to delegate
 // most recent read transaction and sent notifications to delegate
-void begin(const std::unique_ptr<SharedGroup>& sg, BindingContext* binding_context, NotifierPackage&);
-void begin_without_validation(SharedGroup& sg);
-
-// Commit a write transaction
-void commit(SharedGroup& sg);
+void begin(const std::shared_ptr<Transaction>& sg,
+           BindingContext* binding_context, NotifierPackage&);
 
 
 // Cancel a write transaction and roll back all changes, with change notifications
 // Cancel a write transaction and roll back all changes, with change notifications
 // for reverting to the old values sent to delegate
 // for reverting to the old values sent to delegate
-void cancel(SharedGroup& sg, BindingContext* binding_context);
+void cancel(Transaction& sg, BindingContext* binding_context);
 
 
 // Advance the read transaction version, with change information gathered in info
 // Advance the read transaction version, with change information gathered in info
-void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version=VersionID{});
+void advance(Transaction& sg, TransactionChangeInfo& info, VersionID version=VersionID{});
 } // namespace transaction
 } // namespace transaction
 } // namespace _impl
 } // namespace _impl
 } // namespace realm
 } // namespace realm

+ 16 - 18
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp

@@ -19,38 +19,36 @@
 #include "impl/weak_realm_notifier.hpp"
 #include "impl/weak_realm_notifier.hpp"
 
 
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
-#include "util/event_loop_signal.hpp"
+#include "util/scheduler.hpp"
 
 
 using namespace realm;
 using namespace realm;
 using namespace realm::_impl;
 using namespace realm::_impl;
 
 
-WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr<Realm>& realm, bool cache, bool bind_to_context)
+
+WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr<Realm>& realm)
 : m_realm(realm)
 : m_realm(realm)
-, m_execution_context(realm->config().execution_context)
 , m_realm_key(realm.get())
 , m_realm_key(realm.get())
-, m_cache(cache)
-, m_signal(bind_to_context ? std::make_shared<util::EventLoopSignal<Callback>>(Callback{realm}) : nullptr)
 {
 {
+    bind_to_scheduler();
 }
 }
 
 
 WeakRealmNotifier::~WeakRealmNotifier() = default;
 WeakRealmNotifier::~WeakRealmNotifier() = default;
 
 
-void WeakRealmNotifier::Callback::operator()() const
-{
-    if (auto realm = weak_realm.lock()) {
-        realm->notify();
-    }
-}
-
 void WeakRealmNotifier::notify()
 void WeakRealmNotifier::notify()
 {
 {
-    if (m_signal)
-        m_signal->notify();
+    if (m_scheduler)
+        m_scheduler->notify();
 }
 }
 
 
-void WeakRealmNotifier::bind_to_execution_context(AnyExecutionContextID context)
+void WeakRealmNotifier::bind_to_scheduler()
 {
 {
-    REALM_ASSERT(!m_signal);
-    m_signal = std::make_shared<util::EventLoopSignal<Callback>>(Callback{m_realm});
-    m_execution_context = context;
+    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();
+            }
+        });
+    }
 }
 }

+ 6 - 19
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/weak_realm_notifier.hpp

@@ -19,8 +19,6 @@
 #ifndef REALM_WEAK_REALM_NOTIFIER_HPP
 #ifndef REALM_WEAK_REALM_NOTIFIER_HPP
 #define REALM_WEAK_REALM_NOTIFIER_HPP
 #define REALM_WEAK_REALM_NOTIFIER_HPP
 
 
-#include "execution_context_id.hpp"
-
 #include <memory>
 #include <memory>
 #include <thread>
 #include <thread>
 
 
@@ -28,7 +26,7 @@ namespace realm {
 class Realm;
 class Realm;
 
 
 namespace util {
 namespace util {
-template<typename> class EventLoopSignal;
+class Scheduler;
 }
 }
 
 
 namespace _impl {
 namespace _impl {
@@ -39,39 +37,28 @@ namespace _impl {
 // a Realm instance is released from within a function holding the cache lock.
 // a Realm instance is released from within a function holding the cache lock.
 class WeakRealmNotifier {
 class WeakRealmNotifier {
 public:
 public:
-    WeakRealmNotifier(const std::shared_ptr<Realm>& realm, bool cache, bool bind_to_context);
+    WeakRealmNotifier(const std::shared_ptr<Realm>& realm);
     ~WeakRealmNotifier();
     ~WeakRealmNotifier();
 
 
     // Get a strong reference to the cached realm
     // Get a strong reference to the cached realm
     std::shared_ptr<Realm> realm() const { return m_realm.lock(); }
     std::shared_ptr<Realm> realm() const { return m_realm.lock(); }
 
 
-    // Does this WeakRealmNotifier store a Realm instance that should be used on the current thread?
-    bool is_cached_for_execution_context(const AnyExecutionContextID& execution_context) const
-    {
-        return m_cache && m_execution_context == execution_context;
-    }
-
     // Has the Realm instance been destroyed?
     // Has the Realm instance been destroyed?
     bool expired() const { return m_realm.expired(); }
     bool expired() const { return m_realm.expired(); }
 
 
     // Is this a WeakRealmNotifier for the given Realm instance?
     // Is this a WeakRealmNotifier for the given Realm instance?
     bool is_for_realm(Realm* realm) const { return realm == m_realm_key; }
     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();
     void notify();
 
 
-    void bind_to_execution_context(AnyExecutionContextID context);
+    // Bind this notifier to the Realm's scheduler.
+    void bind_to_scheduler();
 
 
 private:
 private:
     std::weak_ptr<Realm> m_realm;
     std::weak_ptr<Realm> m_realm;
-    AnyExecutionContextID m_execution_context;
     void* m_realm_key;
     void* m_realm_key;
-    bool m_cache = false;
-
-    struct Callback {
-        const std::weak_ptr<Realm> weak_realm;
-        void operator()() const;
-    };
-    std::shared_ptr<util::EventLoopSignal<Callback>> m_signal;
+    std::shared_ptr<util::Scheduler> m_scheduler;
 };
 };
 
 
 } // namespace _impl
 } // namespace _impl

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/windows/external_commit_helper.hpp

@@ -16,7 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////
 
 
-#include <realm/group_shared.hpp>
+#include <realm/db.hpp>
 
 
 #include <future>
 #include <future>
 #include <windows.h>
 #include <windows.h>

+ 206 - 167
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.cpp

@@ -19,7 +19,6 @@
 #include "list.hpp"
 #include "list.hpp"
 
 
 #include "impl/list_notifier.hpp"
 #include "impl/list_notifier.hpp"
-#include "impl/primitive_list_notifier.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "object_schema.hpp"
 #include "object_schema.hpp"
 #include "object_store.hpp"
 #include "object_store.hpp"
@@ -27,10 +26,23 @@
 #include "schema.hpp"
 #include "schema.hpp"
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
-#include <realm/link_view.hpp>
+namespace {
+using namespace realm;
+
+template<typename T>
+struct ListType {
+    using type = Lst<T>;
+};
+
+template<>
+struct ListType<Obj> {
+    using type = LnkLst;
+};
+
+}
 
 
 namespace realm {
 namespace realm {
-using namespace realm::_impl;
+using namespace _impl;
 
 
 List::List() noexcept = default;
 List::List() noexcept = default;
 List::~List() = default;
 List::~List() = default;
@@ -40,30 +52,17 @@ List& List::operator=(const List&) = default;
 List::List(List&&) = default;
 List::List(List&&) = default;
 List& List::operator=(List&&) = default;
 List& List::operator=(List&&) = default;
 
 
-List::List(std::shared_ptr<Realm> r, Table& parent_table, size_t col, size_t row)
+List::List(std::shared_ptr<Realm> r, const Obj& parent_obj, ColKey col)
 : m_realm(std::move(r))
 : 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))
 {
 {
-    auto type = parent_table.get_column_type(col);
-    REALM_ASSERT(type == type_LinkList || type == type_Table);
-    if (type == type_LinkList) {
-        m_link_view = parent_table.get_linklist(col, row);
-        m_table.reset(&m_link_view->get_target_table());
-    }
-    else {
-        m_table = parent_table.get_subtable(col, row);
-    }
 }
 }
 
 
-List::List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept
+List::List(std::shared_ptr<Realm> r, const LstBase& list)
 : m_realm(std::move(r))
 : m_realm(std::move(r))
-, m_link_view(std::move(l))
-{
-    m_table.reset(&m_link_view->get_target_table());
-}
-
-List::List(std::shared_ptr<Realm> r, TableRef t) noexcept
-: m_realm(std::move(r))
-, m_table(std::move(t))
+, m_type(ObjectSchema::from_core_type(*list.get_table(), list.get_col_key()) & ~PropertyType::Array)
+, m_list_base(list.clone())
 {
 {
 }
 }
 
 
@@ -75,28 +74,42 @@ static StringData object_name(Table const& table)
 ObjectSchema const& List::get_object_schema() const
 ObjectSchema const& List::get_object_schema() const
 {
 {
     verify_attached();
     verify_attached();
-    REALM_ASSERT(m_link_view);
 
 
-    if (!m_object_schema) {
-        REALM_ASSERT(get_type() == PropertyType::Object);
-        auto object_type = object_name(m_link_view->get_target_table());
+    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);
         auto it = m_realm->schema().find(object_type);
         REALM_ASSERT(it != m_realm->schema().end());
         REALM_ASSERT(it != m_realm->schema().end());
-        m_object_schema = &*it;
+        m_object_schema = object_schema = &*it;
     }
     }
-    return *m_object_schema;
+    return *object_schema;
 }
 }
 
 
 Query List::get_query() const
 Query List::get_query() const
 {
 {
     verify_attached();
     verify_attached();
-    return m_link_view ? m_table->where(m_link_view) : m_table->where();
+    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();
 }
 }
 
 
-size_t List::get_origin_row_index() const
+ColKey List::get_parent_column_key() const
 {
 {
     verify_attached();
     verify_attached();
-    return m_link_view ? m_link_view->get_origin_row_index() : m_table->get_parent_row_index();
+    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
 void List::verify_valid_row(size_t row_ndx, bool insertion) const
@@ -107,14 +120,15 @@ void List::verify_valid_row(size_t row_ndx, bool insertion) const
     }
     }
 }
 }
 
 
-void List::validate(RowExpr row) const
+void List::validate(const Obj& obj) const
 {
 {
-    if (!row.is_attached())
+    if (!obj.is_valid())
         throw std::invalid_argument("Object has been deleted or invalidated");
         throw std::invalid_argument("Object has been deleted or invalidated");
-    if (row.get_table() != &m_link_view->get_target_table())
+    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)",
         throw std::invalid_argument(util::format("Object of type (%1) does not match List type (%2)",
-                                                 object_name(*row.get_table()),
-                                                 object_name(m_link_view->get_target_table())));
+                                                 object_name(*obj.get_table()),
+                                                 object_name(*target)));
 }
 }
 
 
 bool List::is_valid() const
 bool List::is_valid() const
@@ -122,9 +136,9 @@ bool List::is_valid() const
     if (!m_realm)
     if (!m_realm)
         return false;
         return false;
     m_realm->verify_thread();
     m_realm->verify_thread();
-    if (m_link_view)
-        return m_link_view->is_attached();
-    return m_table && m_table->is_attached();
+    if (!m_realm->is_in_read_transaction())
+        return false;
+    return m_list_base->is_attached();
 }
 }
 
 
 void List::verify_attached() const
 void List::verify_attached() const
@@ -143,101 +157,65 @@ void List::verify_in_transaction() const
 size_t List::size() const
 size_t List::size() const
 {
 {
     verify_attached();
     verify_attached();
-    return m_link_view ? m_link_view->size() : m_table->size();
+    return m_list_base->size();
 }
 }
 
 
-size_t List::to_table_ndx(size_t row) const noexcept
-{
-    return m_link_view ? m_link_view->get(row).get_index() : row;
-}
-
-PropertyType List::get_type() const
-{
-    verify_attached();
-    return m_link_view ? PropertyType::Object
-                       : ObjectSchema::from_core_type(*m_table->get_descriptor(), 0);
-}
-
-namespace {
 template<typename T>
 template<typename T>
-auto get(Table& table, size_t row)
+T List::get(size_t row_ndx) const
 {
 {
-    return table.get<T>(0, row);
+    verify_valid_row(row_ndx);
+    return as<T>().get(row_ndx);
 }
 }
 
 
 template<>
 template<>
-auto get<RowExpr>(Table& table, size_t row)
-{
-    return table.get(row);
-}
-}
-
-template<typename T>
-T List::get(size_t row_ndx) const
+Obj List::get(size_t row_ndx) const
 {
 {
     verify_valid_row(row_ndx);
     verify_valid_row(row_ndx);
-    return realm::get<T>(*m_table, to_table_ndx(row_ndx));
+    auto& list = as<Obj>();
+    return list.get_target_table()->get_object(list.get(row_ndx));
 }
 }
 
 
-template RowExpr List::get(size_t) const;
-
 template<typename T>
 template<typename T>
 size_t List::find(T const& value) const
 size_t List::find(T const& value) const
 {
 {
     verify_attached();
     verify_attached();
-    return m_table->find_first(0, value);
+    return as<T>().find_first(value);
 }
 }
 
 
 template<>
 template<>
-size_t List::find(RowExpr const& row) const
+size_t List::find(Obj const& o) const
 {
 {
     verify_attached();
     verify_attached();
-    if (!row.is_attached())
+    if (!o.is_valid())
         return not_found;
         return not_found;
-    validate(row);
+    validate(o);
 
 
-    return m_link_view ? m_link_view->find(row.get_index()) : row.get_index();
+    return as<Obj>().ConstLstIf<ObjKey>::find_first(o.get_key());
 }
 }
 
 
 size_t List::find(Query&& q) const
 size_t List::find(Query&& q) const
 {
 {
     verify_attached();
     verify_attached();
-    if (m_link_view) {
-        size_t index = get_query().and_query(std::move(q)).find();
-        return index == not_found ? index : m_link_view->find(index);
+    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;
     }
     }
-    return q.find();
+    throw std::runtime_error("not implemented");
 }
 }
 
 
 template<typename T>
 template<typename T>
 void List::add(T value)
 void List::add(T value)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
-    m_table->set(0, m_table->add_empty_row(), value);
-}
-
-template<>
-void List::add(size_t target_row_ndx)
-{
-    verify_in_transaction();
-    m_link_view->add(target_row_ndx);
-}
-
-template<>
-void List::add(RowExpr row)
-{
-    validate(row);
-    add(row.get_index());
+    as<T>().add(value);
 }
 }
 
 
 template<>
 template<>
-void List::add(int value)
+void List::add(Obj o)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
-    if (m_link_view)
-        add(static_cast<size_t>(value));
-    else
-        add(static_cast<int64_t>(value));
+    validate(o);
+    as<Obj>().add(o.get_key());
 }
 }
 
 
 template<typename T>
 template<typename T>
@@ -245,23 +223,16 @@ void List::insert(size_t row_ndx, T value)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx, true);
     verify_valid_row(row_ndx, true);
-    m_table->insert_empty_row(row_ndx);
-    m_table->set(0, row_ndx, value);
+    as<T>().insert(row_ndx, value);
 }
 }
 
 
 template<>
 template<>
-void List::insert(size_t row_ndx, size_t target_row_ndx)
+void List::insert(size_t row_ndx, Obj o)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx, true);
     verify_valid_row(row_ndx, true);
-    m_link_view->insert(row_ndx, target_row_ndx);
-}
-
-template<>
-void List::insert(size_t row_ndx, RowExpr row)
-{
-    validate(row);
-    insert(row_ndx, row.get_index());
+    validate(o);
+    as<Obj>().insert(row_ndx, o.get_key());
 }
 }
 
 
 void List::move(size_t source_ndx, size_t dest_ndx)
 void List::move(size_t source_ndx, size_t dest_ndx)
@@ -272,29 +243,20 @@ void List::move(size_t source_ndx, size_t dest_ndx)
     if (source_ndx == dest_ndx)
     if (source_ndx == dest_ndx)
         return;
         return;
 
 
-    if (m_link_view)
-        m_link_view->move(source_ndx, dest_ndx);
-    else
-        m_table->move_row(source_ndx, dest_ndx);
+    m_list_base->move(source_ndx, dest_ndx);
 }
 }
 
 
 void List::remove(size_t row_ndx)
 void List::remove(size_t row_ndx)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx);
     verify_valid_row(row_ndx);
-    if (m_link_view)
-        m_link_view->remove(row_ndx);
-    else
-        m_table->remove(row_ndx);
+    m_list_base->remove(row_ndx, row_ndx + 1);
 }
 }
 
 
 void List::remove_all()
 void List::remove_all()
 {
 {
     verify_in_transaction();
     verify_in_transaction();
-    if (m_link_view)
-        m_link_view->clear();
-    else
-        m_table->clear();
+    m_list_base->clear();
 }
 }
 
 
 template<typename T>
 template<typename T>
@@ -302,22 +264,17 @@ void List::set(size_t row_ndx, T value)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx);
     verify_valid_row(row_ndx);
-    m_table->set(0, row_ndx, value);
+//    validate(row);
+    as<T>().set(row_ndx, value);
 }
 }
 
 
 template<>
 template<>
-void List::set(size_t row_ndx, size_t target_row_ndx)
+void List::set(size_t row_ndx, Obj o)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx);
     verify_valid_row(row_ndx);
-    m_link_view->set(row_ndx, target_row_ndx);
-}
-
-template<>
-void List::set(size_t row_ndx, RowExpr row)
-{
-    validate(row);
-    set(row_ndx, row.get_index());
+    validate(o);
+    as<Obj>().set(row_ndx, o.get_key());
 }
 }
 
 
 void List::swap(size_t ndx1, size_t ndx2)
 void List::swap(size_t ndx1, size_t ndx2)
@@ -325,40 +282,39 @@ void List::swap(size_t ndx1, size_t ndx2)
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(ndx1);
     verify_valid_row(ndx1);
     verify_valid_row(ndx2);
     verify_valid_row(ndx2);
-    if (m_link_view)
-        m_link_view->swap(ndx1, ndx2);
-    else
-        m_table->swap_rows(ndx1, ndx2);
+    m_list_base->swap(ndx1, ndx2);
 }
 }
 
 
 void List::delete_at(size_t row_ndx)
 void List::delete_at(size_t row_ndx)
 {
 {
     verify_in_transaction();
     verify_in_transaction();
     verify_valid_row(row_ndx);
     verify_valid_row(row_ndx);
-    if (m_link_view)
-        m_link_view->remove_target_row(row_ndx);
+    if (m_type == PropertyType::Object)
+        as<Obj>().remove_target_row(row_ndx);
     else
     else
-        m_table->remove(row_ndx);
+        m_list_base->remove(row_ndx, row_ndx + 1);
 }
 }
 
 
 void List::delete_all()
 void List::delete_all()
 {
 {
     verify_in_transaction();
     verify_in_transaction();
-    if (m_link_view)
-        m_link_view->remove_all_target_rows();
+    if (m_type == PropertyType::Object)
+        as<Obj>().remove_all_target_rows();
     else
     else
-        m_table->clear();
+        m_list_base->clear();
 }
 }
 
 
 Results List::sort(SortDescriptor order) const
 Results List::sort(SortDescriptor order) const
 {
 {
     verify_attached();
     verify_attached();
-    if (m_link_view)
-        return Results(m_realm, m_link_view, util::none, std::move(order));
-
-    DescriptorOrdering new_order;
-    new_order.append_sort(std::move(order));
-    return Results(m_realm, get_query(), std::move(new_order));
+    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
 Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
@@ -369,15 +325,15 @@ Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) co
 Results List::filter(Query q) const
 Results List::filter(Query q) const
 {
 {
     verify_attached();
     verify_attached();
-    if (m_link_view)
-        return Results(m_realm, m_link_view, get_query().and_query(std::move(q)));
-    return Results(m_realm, get_query().and_query(std::move(q)));
+    return Results(m_realm, std::dynamic_pointer_cast<LnkLst>(m_list_base), get_query().and_query(std::move(q)));
 }
 }
 
 
 Results List::as_results() const
 Results List::as_results() const
 {
 {
     verify_attached();
     verify_attached();
-    return m_link_view ? Results(m_realm, m_link_view) : Results(m_realm, *m_table);
+    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
 Results List::snapshot() const
@@ -385,37 +341,99 @@ Results List::snapshot() const
     return as_results().snapshot();
     return as_results().snapshot();
 }
 }
 
 
-util::Optional<Mixed> List::max(size_t column)
-{
-    return as_results().max(column);
+// 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(size_t column)
+util::Optional<Mixed> List::min(ColKey col) const
 {
 {
-    return as_results().min(column);
+    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(size_t column)
+Mixed List::sum(ColKey col) const
 {
 {
-    // Results::sum() returns none only for Mode::Empty Results, so we can
-    // safely ignore that possibility here
-    return *as_results().sum(column);
+    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(size_t column)
+util::Optional<double> List::average(ColKey col) const
 {
 {
-    return as_results().average(column);
+    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());
 }
 }
 
 
-// These definitions rely on that LinkViews and Tables are interned by core
 bool List::operator==(List const& rgt) const noexcept
 bool List::operator==(List const& rgt) const noexcept
 {
 {
-    return m_link_view == rgt.m_link_view && m_table.get() == rgt.m_table.get();
+    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) &
 NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
 {
 {
     verify_attached();
     verify_attached();
+    m_realm->verify_notifications_available();
     // Adding a new callback to a notifier which had all of its callbacks
     // Adding a new callback to a notifier which had all of its callbacks
     // removed does not properly reinitialize the notifier. Work around this by
     // removed does not properly reinitialize the notifier. Work around this by
     // recreating it instead.
     // recreating it instead.
@@ -425,15 +443,22 @@ NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
     if (m_notifier && !m_notifier->have_callbacks())
     if (m_notifier && !m_notifier->have_callbacks())
         m_notifier.reset();
         m_notifier.reset();
     if (!m_notifier) {
     if (!m_notifier) {
-        if (get_type() == PropertyType::Object)
-            m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<ListNotifier>(m_link_view, m_realm));
-        else
-            m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<PrimitiveListNotifier>(m_table, m_realm));
+        m_notifier = std::make_shared<ListNotifier>(m_realm, *m_list_base, m_type);
         RealmCoordinator::register_notifier(m_notifier);
         RealmCoordinator::register_notifier(m_notifier);
     }
     }
     return {m_notifier, m_notifier->add_callback(std::move(cb))};
     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)
 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))
 : std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1))
 , requested(r), valid_count(c) {}
 , requested(r), valid_count(c) {}
@@ -452,6 +477,7 @@ REALM_PRIMITIVE_LIST_TYPE(double)
 REALM_PRIMITIVE_LIST_TYPE(StringData)
 REALM_PRIMITIVE_LIST_TYPE(StringData)
 REALM_PRIMITIVE_LIST_TYPE(BinaryData)
 REALM_PRIMITIVE_LIST_TYPE(BinaryData)
 REALM_PRIMITIVE_LIST_TYPE(Timestamp)
 REALM_PRIMITIVE_LIST_TYPE(Timestamp)
+REALM_PRIMITIVE_LIST_TYPE(ObjKey)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<bool>)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<bool>)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<int64_t>)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<int64_t>)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<float>)
 REALM_PRIMITIVE_LIST_TYPE(util::Optional<float>)
@@ -460,9 +486,22 @@ REALM_PRIMITIVE_LIST_TYPE(util::Optional<double>)
 #undef REALM_PRIMITIVE_LIST_TYPE
 #undef REALM_PRIMITIVE_LIST_TYPE
 } // namespace realm
 } // 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 {
 namespace std {
-size_t hash<realm::List>::operator()(realm::List const& list) const
+size_t hash<List>::operator()(List const& list) const
 {
 {
-    return std::hash<void*>()(list.m_link_view ? list.m_link_view.get() : (void*)list.m_table.get());
+    auto& impl = *list.m_list_base;
+    return hash_combine(impl.get_key().value, impl.get_table()->get_key().value,
+                        impl.get_col_key().value);
 }
 }
 }
 }

+ 57 - 29
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp

@@ -23,21 +23,24 @@
 #include "impl/collection_notifier.hpp"
 #include "impl/collection_notifier.hpp"
 #include "object.hpp"
 #include "object.hpp"
 #include "property.hpp"
 #include "property.hpp"
+#include "util/copyable_atomic.hpp"
 
 
-#include <realm/link_view_fwd.hpp>
-#include <realm/row.hpp>
-#include <realm/table_ref.hpp>
+#include <realm/mixed.hpp>
+#include <realm/list.hpp>
 
 
 #include <functional>
 #include <functional>
 #include <memory>
 #include <memory>
 
 
 namespace realm {
 namespace realm {
+class Obj;
 class ObjectSchema;
 class ObjectSchema;
 class Query;
 class Query;
 class Realm;
 class Realm;
 class Results;
 class Results;
 class SortDescriptor;
 class SortDescriptor;
-template <typename T> class ThreadSafeReference;
+class ThreadSafeReference;
+struct ColKey;
+struct ObjKey;
 
 
 namespace _impl {
 namespace _impl {
 class ListNotifier;
 class ListNotifier;
@@ -46,9 +49,8 @@ class ListNotifier;
 class List {
 class List {
 public:
 public:
     List() noexcept;
     List() noexcept;
-    List(std::shared_ptr<Realm> r, Table& parent_table, size_t col, size_t row);
-    List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept;
-    List(std::shared_ptr<Realm> r, TableRef l) noexcept;
+    List(std::shared_ptr<Realm> r, const Obj& parent_obj, ColKey col);
+    List(std::shared_ptr<Realm> r, const LstBase& list);
     ~List();
     ~List();
 
 
     List(const List&);
     List(const List&);
@@ -58,10 +60,13 @@ public:
 
 
     const std::shared_ptr<Realm>& get_realm() const { return m_realm; }
     const std::shared_ptr<Realm>& get_realm() const { return m_realm; }
     Query get_query() const;
     Query get_query() const;
-    size_t get_origin_row_index() 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
     // Get the type of the values contained in this List
-    PropertyType get_type() const;
+    PropertyType get_type() const { return m_type; }
 
 
     // Get the ObjectSchema of the values in this List
     // Get the ObjectSchema of the values in this List
     // Only valid if get_type() returns PropertyType::Object
     // Only valid if get_type() returns PropertyType::Object
@@ -80,7 +85,7 @@ public:
     void delete_at(size_t list_ndx);
     void delete_at(size_t list_ndx);
     void delete_all();
     void delete_all();
 
 
-    template<typename T = RowExpr>
+    template<typename T = Obj>
     T get(size_t row_ndx) const;
     T get(size_t row_ndx) const;
     template<typename T>
     template<typename T>
     size_t find(T const& value) const;
     size_t find(T const& value) const;
@@ -105,15 +110,21 @@ public:
     // Return a Results representing a snapshot of this List.
     // Return a Results representing a snapshot of this List.
     Results snapshot() const;
     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
     // Get the min/max/average/sum of the given column
     // All but sum() returns none when there are zero matching rows
     // All but sum() returns none when there are zero matching rows
     // sum() returns 0,
     // sum() returns 0,
     // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
     // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
     // Throws OutOfBoundsIndexException for an out-of-bounds column
     // Throws OutOfBoundsIndexException for an out-of-bounds column
-    util::Optional<Mixed> max(size_t column=0);
-    util::Optional<Mixed> min(size_t column=0);
-    util::Optional<double> average(size_t column=0);
-    Mixed sum(size_t column=0);
+    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;
     bool operator==(List const& rgt) const noexcept;
 
 
@@ -150,31 +161,42 @@ public:
     };
     };
 
 
 private:
 private:
-    friend ThreadSafeReference<List>;
-
     std::shared_ptr<Realm> m_realm;
     std::shared_ptr<Realm> m_realm;
-    mutable const ObjectSchema* m_object_schema = nullptr;
-    LinkViewRef m_link_view;
-    TableRef m_table;
-    _impl::CollectionNotifier::Handle<_impl::CollectionNotifier> m_notifier;
+    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 verify_valid_row(size_t row_ndx, bool insertion = false) const;
-    void validate(RowExpr) const;
+    void validate(const Obj&) const;
 
 
     template<typename Fn>
     template<typename Fn>
     auto dispatch(Fn&&) const;
     auto dispatch(Fn&&) const;
+    template<typename T>
+    auto& as() const;
 
 
     template<typename T, typename Context>
     template<typename T, typename Context>
     void set_if_different(Context&, size_t row_ndx, T&& value, CreatePolicy);
     void set_if_different(Context&, size_t row_ndx, T&& value, CreatePolicy);
 
 
-    size_t to_table_ndx(size_t row) const noexcept;
-
     friend struct std::hash<List>;
     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>
 template<typename Fn>
 auto List::dispatch(Fn&& fn) const
 auto List::dispatch(Fn&& fn) const
 {
 {
+    verify_attached();
     return switch_on_type(get_type(), std::forward<Fn>(fn));
     return switch_on_type(get_type(), std::forward<Fn>(fn));
 }
 }
 
 
@@ -210,15 +232,21 @@ void List::set(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
 
 
 namespace _impl {
 namespace _impl {
 template <class T>
 template <class T>
-inline size_t help_get_current_row(const T&)
+inline ObjKey help_get_current_row(const T&)
+{
+    return ObjKey();
+}
+
+template <>
+inline ObjKey help_get_current_row(const ConstObj& v)
 {
 {
-    return size_t(-1);
+    return v.get_key();
 }
 }
 
 
 template <>
 template <>
-inline size_t help_get_current_row(const RowExpr& v)
+inline ObjKey help_get_current_row(const Obj& v)
 {
 {
-    return v.get_index();
+    return v.get_key();
 }
 }
 
 
 template <class T>
 template <class T>
@@ -227,9 +255,9 @@ inline bool help_compare_values(const T& v1, const T& v2)
     return v1 != v2;
     return v1 != v2;
 }
 }
 template <>
 template <>
-inline bool help_compare_values(const RowExpr& v1, const RowExpr& v2)
+inline bool help_compare_values(const Obj& v1, const Obj& v2)
 {
 {
-    return v1.get_table() != v2.get_table() || v1.get_index() != v2.get_index();
+    return v1.get_table() != v2.get_table() || v1.get_key() != v2.get_key();
 }
 }
 }
 }
 
 

+ 48 - 22
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp

@@ -23,8 +23,20 @@
 #include "object_schema.hpp"
 #include "object_schema.hpp"
 #include "object_store.hpp"
 #include "object_store.hpp"
 
 
+#include <realm/table.hpp>
+
 using namespace realm;
 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)
 InvalidatedObjectException::InvalidatedObjectException(const std::string& object_type)
 : std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted")
 : std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted")
 , object_type(object_type)
 , object_type(object_type)
@@ -50,17 +62,32 @@ ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_t
 , object_type(object_type), property_name(property_name) {}
 , object_type(object_type), property_name(property_name) {}
 
 
 ModifyPrimaryKeyException::ModifyPrimaryKeyException(const std::string& object_type, const std::string& 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) {}
+: 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, ObjectSchema const& s, RowExpr const& o)
-: m_realm(std::move(r)), m_object_schema(&s), m_row(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 ndx)
+Object::Object(SharedRealm r, StringData object_type, size_t index)
 : m_realm(std::move(r))
 : m_realm(std::move(r))
 , m_object_schema(&*m_realm->schema().find(object_type))
 , m_object_schema(&*m_realm->schema().find(object_type))
-, m_row(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get(ndx))
-{ }
+, m_obj(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get_object(index))
+{
+}
 
 
 Object::Object() = default;
 Object::Object() = default;
 Object::~Object() = default;
 Object::~Object() = default;
@@ -72,8 +99,9 @@ Object& Object::operator=(Object&&) = default;
 NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) &
 NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) &
 {
 {
     verify_attached();
     verify_attached();
+    m_realm->verify_notifications_available();
     if (!m_notifier) {
     if (!m_notifier) {
-        m_notifier = std::make_shared<_impl::ObjectNotifier>(m_row, m_realm);
+        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);
         _impl::RealmCoordinator::register_notifier(m_notifier);
     }
     }
     return {m_notifier, m_notifier->add_callback(std::move(callback))};
     return {m_notifier, m_notifier->add_callback(std::move(callback))};
@@ -82,7 +110,7 @@ NotificationToken Object::add_notification_callback(CollectionChangeCallback cal
 void Object::verify_attached() const
 void Object::verify_attached() const
 {
 {
     m_realm->verify_thread();
     m_realm->verify_thread();
-    if (!m_row.is_attached()) {
+    if (!m_obj.is_valid()) {
         throw InvalidatedObjectException(m_object_schema->name);
         throw InvalidatedObjectException(m_object_schema->name);
     }
     }
 }
 }
@@ -99,22 +127,20 @@ Property const& Object::property_for_name(StringData prop_name) const
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
 void Object::ensure_user_in_everyone_role()
 void Object::ensure_user_in_everyone_role()
 {
 {
-    auto role_table = m_realm->read_group().get_table("class___Role");
-    if (!role_table)
-        return;
-    size_t ndx = role_table->find_first_string(role_table->get_column_index("name"), "everyone");
-    if (ndx == npos)
-        return;
-    auto users = role_table->get_linklist(role_table->get_column_index("members"), ndx);
-    if (users->find(m_row.get_index()) != not_found)
-        return;
-
-    users->add(m_row.get_index());
+    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()
 void Object::ensure_private_role_exists_for_user()
 {
 {
-    auto user_id = m_row.get<StringData>(m_row.get_table()->get_column_index("id"));
-    ObjectStore::ensure_private_role_exists_for_user(m_realm->read_group(), user_id);
+    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
 #endif

+ 29 - 14
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp

@@ -21,12 +21,11 @@
 
 
 #include "impl/collection_notifier.hpp"
 #include "impl/collection_notifier.hpp"
 
 
-#include <realm/row.hpp>
+#include <realm/obj.hpp>
 
 
 namespace realm {
 namespace realm {
 class ObjectSchema;
 class ObjectSchema;
 struct Property;
 struct Property;
-using RowExpr = BasicRowExpr<Table>;
 
 
 namespace _impl {
 namespace _impl {
     class ObjectNotifier;
     class ObjectNotifier;
@@ -47,8 +46,10 @@ enum class CreatePolicy : int8_t {
 class Object {
 class Object {
 public:
 public:
     Object();
     Object();
-    Object(std::shared_ptr<Realm> r, ObjectSchema const& s, RowExpr const& o);
-    Object(std::shared_ptr<Realm> r, StringData object_type, size_t ndx);
+    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 const&);
     Object(Object&&);
     Object(Object&&);
@@ -58,16 +59,29 @@ public:
     ~Object();
     ~Object();
 
 
     std::shared_ptr<Realm> const& realm() const { return m_realm; }
     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; }
     ObjectSchema const& get_object_schema() const { return *m_object_schema; }
-    RowExpr row() const { return m_row; }
+    Obj obj() const { return m_obj; }
 
 
-    bool is_valid() const { return m_row.is_attached(); }
+    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) &;
     NotificationToken add_notification_callback(CollectionChangeCallback callback) &;
 
 
     void ensure_user_in_everyone_role();
     void ensure_user_in_everyone_role();
     void ensure_private_role_exists_for_user();
     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 following functions require an accessor context which converts from
     // the binding's native data types to the core data types. See CppContext
     // the binding's native data types to the core data types. See CppContext
     // for a reference implementation of such a context.
     // for a reference implementation of such a context.
@@ -80,23 +94,23 @@ public:
                             ValueType value, CreatePolicy policy = CreatePolicy::ForceCreate);
                             ValueType value, CreatePolicy policy = CreatePolicy::ForceCreate);
 
 
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
-    ValueType get_property_value(ContextType& ctx, StringData prop_name);
+    ValueType get_property_value(ContextType& ctx, StringData prop_name) const;
 
 
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
-    ValueType get_property_value(ContextType& ctx, const Property& property);
+    ValueType get_property_value(ContextType& ctx, const Property& property) const;
 
 
     // create an Object from a native representation
     // create an Object from a native representation
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                          const ObjectSchema &object_schema, ValueType value,
                          const ObjectSchema &object_schema, ValueType value,
                          CreatePolicy policy = CreatePolicy::ForceCreate,
                          CreatePolicy policy = CreatePolicy::ForceCreate,
-                         size_t current_row = size_t(-1), Row* = nullptr);
+                         ObjKey current_obj = ObjKey(), Obj* = nullptr);
 
 
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                          StringData object_type, ValueType value,
                          StringData object_type, ValueType value,
                          CreatePolicy policy = CreatePolicy::ForceCreate,
                          CreatePolicy policy = CreatePolicy::ForceCreate,
-                         size_t current_row = size_t(-1), Row* = nullptr);
+                         ObjKey current_obj = ObjKey(), Obj* = nullptr);
 
 
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
     static Object get_for_primary_key(ContextType& ctx,
     static Object get_for_primary_key(ContextType& ctx,
@@ -115,7 +129,7 @@ private:
 
 
     std::shared_ptr<Realm> m_realm;
     std::shared_ptr<Realm> m_realm;
     const ObjectSchema *m_object_schema;
     const ObjectSchema *m_object_schema;
-    Row m_row;
+    Obj m_obj;
     _impl::CollectionNotifier::Handle<_impl::ObjectNotifier> m_notifier;
     _impl::CollectionNotifier::Handle<_impl::ObjectNotifier> m_notifier;
 
 
 
 
@@ -123,11 +137,12 @@ private:
     void set_property_value_impl(ContextType& ctx, const Property &property,
     void set_property_value_impl(ContextType& ctx, const Property &property,
                                  ValueType value, CreatePolicy policy, bool is_default);
                                  ValueType value, CreatePolicy policy, bool is_default);
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
-    ValueType get_property_value_impl(ContextType& ctx, const Property &property);
+    ValueType get_property_value_impl(ContextType& ctx, const Property &property) const;
 
 
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
-    static size_t get_for_primary_key_impl(ContextType& ctx, Table const& table,
-                                           const Property &primary_prop, ValueType primary_value);
+    static ObjKey get_for_primary_key_impl(ContextType& ctx, Table const& table,
+                                           const Property &primary_prop,
+                                           ValueType primary_value);
 
 
     void verify_attached() const;
     void verify_attached() const;
     Property const& property_for_name(StringData prop_name) const;
     Property const& property_for_name(StringData prop_name) const;

+ 75 - 91
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp

@@ -29,7 +29,6 @@
 #include "schema.hpp"
 #include "schema.hpp"
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 
 
-#include <realm/link_view.hpp>
 #include <realm/util/assert.hpp>
 #include <realm/util/assert.hpp>
 #include <realm/table_view.hpp>
 #include <realm/table_view.hpp>
 
 
@@ -41,7 +40,8 @@
 
 
 namespace realm {
 namespace realm {
 template <typename ValueType, typename ContextType>
 template <typename ValueType, typename ContextType>
-void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueType value, CreatePolicy policy)
+void Object::set_property_value(ContextType& ctx, StringData prop_name,
+                                ValueType value, CreatePolicy policy)
 {
 {
     verify_attached();
     verify_attached();
     m_realm->verify_in_write();
     m_realm->verify_in_write();
@@ -57,13 +57,13 @@ void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueTyp
 }
 }
 
 
 template <typename ValueType, typename ContextType>
 template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value(ContextType& ctx, const Property& property)
+ValueType Object::get_property_value(ContextType& ctx, const Property& property) const
 {
 {
     return get_property_value_impl<ValueType>(ctx, property);
     return get_property_value_impl<ValueType>(ctx, property);
 }
 }
 
 
 template <typename ValueType, typename ContextType>
 template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value(ContextType& ctx, StringData prop_name)
+ValueType Object::get_property_value(ContextType& ctx, StringData prop_name) const
 {
 {
     return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
     return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
 }
 }
@@ -74,18 +74,18 @@ struct ValueUpdater {
     ContextType& ctx;
     ContextType& ctx;
     Property const& property;
     Property const& property;
     ValueType& value;
     ValueType& value;
-    RowExpr row;
-    size_t col;
+    Obj& obj;
+    ColKey col;
     CreatePolicy policy;
     CreatePolicy policy;
     bool is_default;
     bool is_default;
 
 
-    void operator()(RowExpr*)
+    void operator()(Obj*)
     {
     {
         ContextType child_ctx(ctx, property);
         ContextType child_ctx(ctx, property);
-        auto curr_link = row.get_link(col);
-        auto link = child_ctx.template unbox<RowExpr>(value, policy, curr_link);
-        if (policy != CreatePolicy::UpdateModified || curr_link != link.get_index()) {
-            row.set_link(col, link.get_index());
+        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());
         }
         }
     }
     }
 
 
@@ -93,8 +93,8 @@ struct ValueUpdater {
     void operator()(T*)
     void operator()(T*)
     {
     {
         auto new_val = ctx.template unbox<T>(value);
         auto new_val = ctx.template unbox<T>(value);
-        if (policy != CreatePolicy::UpdateModified || row.get<T>(col) != new_val) {
-            row.set(col, new_val, is_default);
+        if (policy != CreatePolicy::UpdateModified || obj.get<T>(col) != new_val) {
+            obj.set(col, new_val, is_default);
         }
         }
     }
     }
 };
 };
@@ -106,17 +106,15 @@ void Object::set_property_value_impl(ContextType& ctx, const Property &property,
 {
 {
     ctx.will_change(*this, property);
     ctx.will_change(*this, property);
 
 
-    auto& table = *m_row.get_table();
-    size_t col = property.table_column;
-    size_t row = m_row.get_index();
+    ColKey col{property.column_key};
     if (is_nullable(property.type) && ctx.is_null(value)) {
     if (is_nullable(property.type) && ctx.is_null(value)) {
-        if (policy != CreatePolicy::UpdateModified || !table.is_null(col, row)) {
+        if (policy != CreatePolicy::UpdateModified || !m_obj.is_null(col)) {
             if (property.type == PropertyType::Object) {
             if (property.type == PropertyType::Object) {
                 if (!is_default)
                 if (!is_default)
-                    table.nullify_link(col, row);
+                    m_obj.set_null(col);
             }
             }
             else {
             else {
-                table.set_null(col, row, is_default);
+                m_obj.set_null(col, is_default);
             }
             }
         }
         }
 
 
@@ -129,48 +127,48 @@ void Object::set_property_value_impl(ContextType& ctx, const Property &property,
             throw ReadOnlyPropertyException(m_object_schema->name, property.name);
             throw ReadOnlyPropertyException(m_object_schema->name, property.name);
 
 
         ContextType child_ctx(ctx, property);
         ContextType child_ctx(ctx, property);
-        List list(m_realm, table, col, m_row.get_index());
+        List list(m_realm, m_obj, col);
         list.assign(child_ctx, value, policy);
         list.assign(child_ctx, value, policy);
         ctx.did_change();
         ctx.did_change();
         return;
         return;
     }
     }
 
 
     ValueUpdater<ValueType, ContextType> updater{ctx, property, value,
     ValueUpdater<ValueType, ContextType> updater{ctx, property, value,
-        table.get(row),col, policy, is_default};
+        m_obj, col, policy, is_default};
     switch_on_type(property.type, updater);
     switch_on_type(property.type, updater);
     ctx.did_change();
     ctx.did_change();
 }
 }
 
 
 template <typename ValueType, typename ContextType>
 template <typename ValueType, typename ContextType>
-ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property)
+ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property) const
 {
 {
     verify_attached();
     verify_attached();
 
 
-    size_t column = property.table_column;
-    if (is_nullable(property.type) && m_row.is_null(column))
+    ColKey column{property.column_key};
+    if (is_nullable(property.type) && m_obj.is_null(column))
         return ctx.null_value();
         return ctx.null_value();
     if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
     if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
-        return ctx.box(List(m_realm, *m_row.get_table(), column, m_row.get_index()));
+        return ctx.box(List(m_realm, m_obj, column));
 
 
     switch (property.type & ~PropertyType::Flags) {
     switch (property.type & ~PropertyType::Flags) {
-        case PropertyType::Bool:   return ctx.box(m_row.get_bool(column));
-        case PropertyType::Int:    return ctx.box(m_row.get_int(column));
-        case PropertyType::Float:  return ctx.box(m_row.get_float(column));
-        case PropertyType::Double: return ctx.box(m_row.get_double(column));
-        case PropertyType::String: return ctx.box(m_row.get_string(column));
-        case PropertyType::Data:   return ctx.box(m_row.get_binary(column));
-        case PropertyType::Date:   return ctx.box(m_row.get_timestamp(column));
-        case PropertyType::Any:    return ctx.box(m_row.get_mixed(column));
+        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: {
         case PropertyType::Object: {
             auto linkObjectSchema = m_realm->schema().find(property.object_type);
             auto linkObjectSchema = m_realm->schema().find(property.object_type);
-            TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), property.object_type);
-            return ctx.box(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column))));
+            return ctx.box(Object(m_realm, *linkObjectSchema,
+                                  const_cast<Obj&>(m_obj).get_linked_object(column)));
         }
         }
         case PropertyType::LinkingObjects: {
         case PropertyType::LinkingObjects: {
             auto target_object_schema = m_realm->schema().find(property.object_type);
             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 link_property = target_object_schema->property_for_name(property.link_origin_property_name);
-            TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name);
-            auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column);
+            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)));
             return ctx.box(Results(m_realm, std::move(tv)));
         }
         }
         default: REALM_UNREACHABLE();
         default: REALM_UNREACHABLE();
@@ -180,17 +178,17 @@ ValueType Object::get_property_value_impl(ContextType& ctx, const Property &prop
 template<typename ValueType, typename ContextType>
 template<typename ValueType, typename ContextType>
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                       StringData object_type, ValueType value,
                       StringData object_type, ValueType value,
-                      CreatePolicy policy, size_t current_row, Row* out_row)
+                      CreatePolicy policy, ObjKey current_obj, Obj* out_row)
 {
 {
     auto object_schema = realm->schema().find(object_type);
     auto object_schema = realm->schema().find(object_type);
     REALM_ASSERT(object_schema != realm->schema().end());
     REALM_ASSERT(object_schema != realm->schema().end());
-    return create(ctx, realm, *object_schema, value, policy, current_row, out_row);
+    return create(ctx, realm, *object_schema, value, policy, current_obj, out_row);
 }
 }
 
 
 template<typename ValueType, typename ContextType>
 template<typename ValueType, typename ContextType>
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                       ObjectSchema const& object_schema, ValueType value,
                       ObjectSchema const& object_schema, ValueType value,
-                      CreatePolicy policy, size_t current_row, Row* out_row)
+                      CreatePolicy policy, ObjKey current_obj, Obj* out_row)
 {
 {
     realm->verify_in_write();
     realm->verify_in_write();
 
 
@@ -198,8 +196,8 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
     bool created = false;
     bool created = false;
 
 
     // try to get existing row if updating
     // try to get existing row if updating
-    size_t row_index = realm::not_found;
-    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
+    Obj obj;
+    auto table = realm->read_group().get_table(object_schema.table_key);
 
 
     bool skip_primary = true;
     bool skip_primary = true;
     if (auto primary_prop = object_schema.primary_key_property()) {
     if (auto primary_prop = object_schema.primary_key_property()) {
@@ -213,40 +211,15 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                 throw MissingPropertyValueException(object_schema.name, primary_prop->name);
                 throw MissingPropertyValueException(object_schema.name, primary_prop->name);
             primary_value = ctx.null_value();
             primary_value = ctx.null_value();
         }
         }
-        row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, *primary_value);
-
-        if (row_index == realm::not_found) {
-            created = true;
-            if (primary_prop->type == PropertyType::Int) {
-#if REALM_ENABLE_SYNC
-                row_index = sync::create_object_with_primary_key(realm->read_group(), *table, ctx.template unbox<util::Optional<int64_t>>(*primary_value));
-#else
-                row_index = table->add_empty_row();
-                if (ctx.is_null(*primary_value))
-                    table->set_null_unique(primary_prop->table_column, row_index);
-                else
-                    table->set_unique(primary_prop->table_column, row_index, ctx.template unbox<int64_t>(*primary_value));
-#endif // REALM_ENABLE_SYNC
-            }
-            else if (primary_prop->type == PropertyType::String) {
-                auto value = ctx.template unbox<StringData>(*primary_value);
-#if REALM_ENABLE_SYNC
-                row_index = sync::create_object_with_primary_key(realm->read_group(), *table, value);
-#else
-                row_index = table->add_empty_row();
-                table->set_unique(primary_prop->table_column, row_index, value);
-#endif // REALM_ENABLE_SYNC
-            }
-            else {
-                REALM_TERMINATE("Unsupported primary key type.");
-            }
-        }
-        else if (policy == CreatePolicy::ForceCreate) {
-            if (realm->is_in_migration()) {
+        auto key = get_for_primary_key_impl(ctx, *table, *primary_prop, *primary_value);
+        if (key) {
+            if (policy != CreatePolicy::ForceCreate)
+                obj = table->get_object(key);
+            else if (realm->is_in_migration()) {
                 // Creating objects with duplicate primary keys is allowed in migrations
                 // Creating objects with duplicate primary keys is allowed in migrations
                 // as long as there are no duplicates at the end, as adding an entirely
                 // as long as there are no duplicates at the end, as adding an entirely
                 // new column which is the PK will inherently result in duplicates at first
                 // new column which is the PK will inherently result in duplicates at first
-                row_index = table->add_empty_row();
+                obj = table->create_object();
                 created = true;
                 created = true;
                 skip_primary = false;
                 skip_primary = false;
             }
             }
@@ -255,25 +228,35 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                                                     object_schema.name, ctx.print(*primary_value)));
                                                     object_schema.name, ctx.print(*primary_value)));
             }
             }
         }
         }
+        else {
+            created = true;
+            Mixed primary_key;
+            if (primary_prop->type == PropertyType::Int) {
+                primary_key = ctx.template unbox<util::Optional<int64_t>>(*primary_value);
+            }
+            else if (primary_prop->type == PropertyType::String) {
+                primary_key = ctx.template unbox<StringData>(*primary_value);
+            }
+            else {
+                REALM_TERMINATE("Unsupported primary key type.");
+            }
+            obj = table->create_object_with_primary_key(primary_key);
+        }
     }
     }
     else {
     else {
-        if (policy == CreatePolicy::UpdateModified && current_row != realm::not_found) {
-            row_index = current_row;
+        if (policy == CreatePolicy::UpdateModified && current_obj) {
+            obj = table->get_object(current_obj);
         }
         }
         else {
         else {
-#if REALM_ENABLE_SYNC
-            row_index = sync::create_object(realm->read_group(), *table);
-#else
-            row_index = table->add_empty_row();
-#endif // REALM_ENABLE_SYNC
+        obj = table->create_object();
             created = true;
             created = true;
         }
         }
     }
     }
 
 
     // populate
     // populate
-    Object object(realm, object_schema, table->get(row_index));
+    Object object(realm, object_schema, obj);
     if (out_row)
     if (out_row)
-        *out_row = object.row();
+        *out_row = obj;
     for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
     for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
         auto& prop = object_schema.persisted_properties[i];
         auto& prop = object_schema.persisted_properties[i];
         if (skip_primary && prop.is_primary)
         if (skip_primary && prop.is_primary)
@@ -323,29 +306,30 @@ Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> cons
         throw MissingPrimaryKeyException(object_schema.name);
         throw MissingPrimaryKeyException(object_schema.name);
     }
     }
 
 
-    auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
+    TableRef table;
+    if (object_schema.table_key)
+        table = realm->read_group().get_table(object_schema.table_key);
     if (!table)
     if (!table)
-        return Object(realm, object_schema, RowExpr());
-    auto row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, primary_value);
-
-    return Object(realm, object_schema, row_index == realm::not_found ? Row() : Row(table->get(row_index)));
+        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>
 template<typename ValueType, typename ContextType>
-size_t Object::get_for_primary_key_impl(ContextType& ctx, Table const& table,
+ObjKey Object::get_for_primary_key_impl(ContextType& ctx, Table const& table,
                                         const Property &primary_prop,
                                         const Property &primary_prop,
                                         ValueType primary_value) {
                                         ValueType primary_value) {
     bool is_null = ctx.is_null(primary_value);
     bool is_null = ctx.is_null(primary_value);
     if (is_null && !is_nullable(primary_prop.type))
     if (is_null && !is_nullable(primary_prop.type))
         throw std::logic_error("Invalid null value for non-nullable primary key.");
         throw std::logic_error("Invalid null value for non-nullable primary key.");
     if (primary_prop.type == PropertyType::String) {
     if (primary_prop.type == PropertyType::String) {
-        return table.find_first(primary_prop.table_column,
+        return table.find_first(primary_prop.column_key,
                                 ctx.template unbox<StringData>(primary_value));
                                 ctx.template unbox<StringData>(primary_value));
     }
     }
     if (is_nullable(primary_prop.type))
     if (is_nullable(primary_prop.type))
-        return table.find_first(primary_prop.table_column,
+        return table.find_first(primary_prop.column_key,
                                 ctx.template unbox<util::Optional<int64_t>>(primary_value));
                                 ctx.template unbox<util::Optional<int64_t>>(primary_value));
-    return table.find_first(primary_prop.table_column,
+    return table.find_first(primary_prop.column_key,
                             ctx.template unbox<int64_t>(primary_value));
                             ctx.template unbox<int64_t>(primary_value));
 }
 }
 
 

+ 52 - 28
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.cpp

@@ -23,9 +23,7 @@
 #include "property.hpp"
 #include "property.hpp"
 #include "schema.hpp"
 #include "schema.hpp"
 
 
-
 #include <realm/data_type.hpp>
 #include <realm/data_type.hpp>
-#include <realm/descriptor.hpp>
 #include <realm/group.hpp>
 #include <realm/group.hpp>
 #include <realm/table.hpp>
 #include <realm/table.hpp>
 
 
@@ -53,47 +51,73 @@ ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> per
     }
     }
 }
 }
 
 
-PropertyType ObjectSchema::from_core_type(Descriptor const& table, size_t col)
+PropertyType ObjectSchema::from_core_type(Table const& table, ColKey col)
 {
 {
-    auto optional = table.is_nullable(col) ? PropertyType::Nullable : PropertyType::Required;
+    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)) {
     switch (table.get_column_type(col)) {
-        case type_Int:       return PropertyType::Int | optional;
-        case type_Float:     return PropertyType::Float | optional;
-        case type_Double:    return PropertyType::Double | optional;
-        case type_Bool:      return PropertyType::Bool | optional;
-        case type_String:    return PropertyType::String | optional;
-        case type_Binary:    return PropertyType::Data | optional;
-        case type_Timestamp: return PropertyType::Date | optional;
-        case type_Mixed:     return PropertyType::Any | optional;
+        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_Link:      return PropertyType::Object | PropertyType::Nullable;
         case type_LinkList:  return PropertyType::Object | PropertyType::Array;
         case type_LinkList:  return PropertyType::Object | PropertyType::Array;
-        case type_Table:     return from_core_type(*table.get_subdescriptor(col), 0) | PropertyType::Array;
         default: REALM_UNREACHABLE();
         default: REALM_UNREACHABLE();
     }
     }
 }
 }
 
 
-ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : name(name) {
+ObjectSchema::ObjectSchema(Group const& group, StringData name, TableKey key)
+: name(name)
+{
     ConstTableRef table;
     ConstTableRef table;
-    if (index < group.size()) {
-        table = group.get_table(index);
+    if (key) {
+        table = group.get_table(key);
     }
     }
     else {
     else {
         table = ObjectStore::table_for_object_type(group, name);
         table = ObjectStore::table_for_object_type(group, name);
     }
     }
+    table_key = table->get_key();
 
 
     size_t count = table->get_column_count();
     size_t count = table->get_column_count();
     persisted_properties.reserve(count);
     persisted_properties.reserve(count);
-    for (size_t col = 0; col < count; col++) {
-        if (auto property = ObjectStore::property_for_column_index(table, col)) {
-            persisted_properties.push_back(std::move(property.value()));
+
+    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 = realm::ObjectStore::get_primary_key_for_object(group, name);
+    primary_key = ObjectStore::get_primary_key_for_object(group, name);
     set_primary_key_property();
     set_primary_key_property();
 }
 }
 
 
-Property *ObjectSchema::property_for_name(StringData name)
+Property *ObjectSchema::property_for_name(StringData name) noexcept
 {
 {
     for (auto& prop : persisted_properties) {
     for (auto& prop : persisted_properties) {
         if (StringData(prop.name) == name) {
         if (StringData(prop.name) == name) {
@@ -108,7 +132,7 @@ Property *ObjectSchema::property_for_name(StringData name)
     return nullptr;
     return nullptr;
 }
 }
 
 
-Property *ObjectSchema::property_for_public_name(StringData public_name)
+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.
     // If no `public_name` is defined, the internal `name` is also considered the public name.
     for (auto& prop : persisted_properties) {
     for (auto& prop : persisted_properties) {
@@ -120,29 +144,29 @@ Property *ObjectSchema::property_for_public_name(StringData public_name)
     // are a bit pointless since the internal name is already the "public name", but since
     // 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.
     // this distinction isn't visible in the Property struct we allow it anyway.
     for (auto& prop : computed_properties) {
     for (auto& prop : computed_properties) {
-        if ((prop.public_name.empty() ? StringData(prop.name) :  StringData(prop.public_name)) == public_name)
+        if (StringData(prop.public_name.empty() ? prop.name : prop.public_name) == public_name)
             return &prop;
             return &prop;
     }
     }
     return nullptr;
     return nullptr;
 }
 }
 
 
-const Property *ObjectSchema::property_for_public_name(StringData public_name) const
+const Property *ObjectSchema::property_for_public_name(StringData public_name) const noexcept
 {
 {
     return const_cast<ObjectSchema *>(this)->property_for_public_name(public_name);
     return const_cast<ObjectSchema *>(this)->property_for_public_name(public_name);
 }
 }
 
 
-const Property *ObjectSchema::property_for_name(StringData name) const
+const Property *ObjectSchema::property_for_name(StringData name) const noexcept
 {
 {
     return const_cast<ObjectSchema *>(this)->property_for_name(name);
     return const_cast<ObjectSchema *>(this)->property_for_name(name);
 }
 }
 
 
-bool ObjectSchema::property_is_computed(Property const& property) const
+bool ObjectSchema::property_is_computed(Property const& property) const noexcept
 {
 {
     auto end = computed_properties.end();
     auto end = computed_properties.end();
     return std::find(computed_properties.begin(), end, property) != end;
     return std::find(computed_properties.begin(), end, property) != end;
 }
 }
 
 
-void ObjectSchema::set_primary_key_property()
+void ObjectSchema::set_primary_key_property() noexcept
 {
 {
     if (primary_key.length()) {
     if (primary_key.length()) {
         if (auto primary_key_prop = primary_key_property()) {
         if (auto primary_key_prop = primary_key_property()) {
@@ -310,7 +334,7 @@ void ObjectSchema::validate(Schema const& schema, std::vector<ObjectSchemaValida
 }
 }
 
 
 namespace realm {
 namespace realm {
-bool operator==(ObjectSchema const& a, ObjectSchema const& b)
+bool operator==(ObjectSchema const& a, ObjectSchema const& b) noexcept
 {
 {
     return std::tie(a.name, a.primary_key, a.persisted_properties, a.computed_properties)
     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);
         == std::tie(b.name, b.primary_key, b.persisted_properties, b.computed_properties);

+ 20 - 13
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_schema.hpp

@@ -19,15 +19,16 @@
 #ifndef REALM_OBJECT_SCHEMA_HPP
 #ifndef REALM_OBJECT_SCHEMA_HPP
 #define REALM_OBJECT_SCHEMA_HPP
 #define REALM_OBJECT_SCHEMA_HPP
 
 
+#include <realm/keys.hpp>
 #include <realm/string_data.hpp>
 #include <realm/string_data.hpp>
 
 
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
 namespace realm {
 namespace realm {
-class Descriptor;
 class Group;
 class Group;
 class Schema;
 class Schema;
+class Table;
 enum class PropertyType: unsigned char;
 enum class PropertyType: unsigned char;
 struct ObjectSchemaValidationException;
 struct ObjectSchemaValidationException;
 struct Property;
 struct Property;
@@ -40,35 +41,41 @@ public:
                  std::initializer_list<Property> computed_properties);
                  std::initializer_list<Property> computed_properties);
     ~ObjectSchema();
     ~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
     // create object schema from existing table
-    // if no table is provided it is looked up in the group
-    ObjectSchema(Group const& group, StringData name, size_t index=-1);
+    // 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::string name;
     std::vector<Property> persisted_properties;
     std::vector<Property> persisted_properties;
     std::vector<Property> computed_properties;
     std::vector<Property> computed_properties;
     std::string primary_key;
     std::string primary_key;
+    TableKey table_key;
 
 
-    Property *property_for_public_name(StringData public_name);
-    const Property *property_for_public_name(StringData public_name) const;
-    Property *property_for_name(StringData name);
-    const Property *property_for_name(StringData name) const;
-    Property *primary_key_property() {
+    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);
         return property_for_name(primary_key);
     }
     }
-    const Property *primary_key_property() const {
+    const Property *primary_key_property() const noexcept {
         return property_for_name(primary_key);
         return property_for_name(primary_key);
     }
     }
-    bool property_is_computed(Property const& property) const;
+    bool property_is_computed(Property const& property) const noexcept;
 
 
     void validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const;
     void validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const;
 
 
-    friend bool operator==(ObjectSchema const& a, ObjectSchema const& b);
+    friend bool operator==(ObjectSchema const& a, ObjectSchema const& b) noexcept;
 
 
-    static PropertyType from_core_type(Descriptor const& table, size_t col);
+    static PropertyType from_core_type(Table const& table, ColKey col);
 
 
 private:
 private:
-    void set_primary_key_property();
+    void set_primary_key_property() noexcept;
 };
 };
 }
 }
 
 

+ 116 - 234
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp

@@ -24,7 +24,6 @@
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 #include "sync/partial_sync.hpp"
 #include "sync/partial_sync.hpp"
 
 
-#include <realm/descriptor.hpp>
 #include <realm/group.hpp>
 #include <realm/group.hpp>
 #include <realm/table.hpp>
 #include <realm/table.hpp>
 #include <realm/table_view.hpp>
 #include <realm/table_view.hpp>
@@ -45,42 +44,21 @@ constexpr uint64_t ObjectStore::NotVersioned;
 namespace {
 namespace {
 const char * const c_metadataTableName = "metadata";
 const char * const c_metadataTableName = "metadata";
 const char * const c_versionColumnName = "version";
 const char * const c_versionColumnName = "version";
-const size_t c_versionColumnIndex = 0;
-
-const char * const c_primaryKeyTableName = "pk";
-const char * const c_primaryKeyObjectClassColumnName = "pk_table";
-const size_t c_primaryKeyObjectClassColumnIndex =  0;
-const char * const c_primaryKeyPropertyNameColumnName = "pk_property";
-const size_t c_primaryKeyPropertyNameColumnIndex =  1;
-
-const size_t c_zeroRowIndex = 0;
 
 
 const char c_object_table_prefix[] = "class_";
 const char c_object_table_prefix[] = "class_";
 
 
 void create_metadata_tables(Group& group) {
 void create_metadata_tables(Group& group) {
-    // The tables 'pk' and 'metadata' are treated specially by Sync. The 'pk' table
-    // is populated by `sync::create_table` and friends, while the 'metadata' table
-    // is simply ignored.
-    TableRef pk_table = group.get_or_add_table(c_primaryKeyTableName);
+    // The 'metadata' table is simply ignored by Sync
     TableRef metadata_table = group.get_or_add_table(c_metadataTableName);
     TableRef metadata_table = group.get_or_add_table(c_metadataTableName);
 
 
     if (metadata_table->get_column_count() == 0) {
     if (metadata_table->get_column_count() == 0) {
-        metadata_table->insert_column(c_versionColumnIndex, type_Int, c_versionColumnName);
-        metadata_table->add_empty_row();
-        // set initial version
-        metadata_table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned);
-    }
-
-    if (pk_table->get_column_count() == 0) {
-        pk_table->insert_column(c_primaryKeyObjectClassColumnIndex, type_String, c_primaryKeyObjectClassColumnName);
-        pk_table->insert_column(c_primaryKeyPropertyNameColumnIndex, type_String, c_primaryKeyPropertyNameColumnName);
+        metadata_table->add_column(type_Int, c_versionColumnName);
+        metadata_table->create_object().set(c_versionColumnName, int64_t(ObjectStore::NotVersioned));
     }
     }
-    pk_table->add_search_index(c_primaryKeyObjectClassColumnIndex);
 }
 }
 
 
 void set_schema_version(Group& group, uint64_t version) {
 void set_schema_version(Group& group, uint64_t version) {
-    TableRef table = group.get_table(c_metadataTableName);
-    table->set_int(c_versionColumnIndex, c_zeroRowIndex, version);
+    group.get_table(c_metadataTableName)->get_object(0).set<int64_t>(c_versionColumnName, version);
 }
 }
 
 
 template<typename Group>
 template<typename Group>
@@ -105,41 +83,42 @@ DataType to_core_type(PropertyType type)
     }
     }
 }
 }
 
 
-void insert_column(Group& group, Table& table, Property const& property, size_t col_ndx)
+ColKey add_column(Group& group, Table& table, Property const& property)
 {
 {
     // Cannot directly insert a LinkingObjects column (a computed property).
     // Cannot directly insert a LinkingObjects column (a computed property).
     // LinkingObjects must be an artifact of an existing link column.
     // LinkingObjects must be an artifact of an existing link column.
     REALM_ASSERT(property.type != PropertyType::LinkingObjects);
     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) {
     if (property.type == PropertyType::Object) {
         auto target_name = ObjectStore::table_name_for_object_type(property.object_type);
         auto target_name = ObjectStore::table_name_for_object_type(property.object_type);
         TableRef link_table = group.get_table(target_name);
         TableRef link_table = group.get_table(target_name);
         REALM_ASSERT(link_table);
         REALM_ASSERT(link_table);
-        table.insert_column_link(col_ndx, is_array(property.type) ? type_LinkList : type_Link,
-                                 property.name, *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)) {
     else if (is_array(property.type)) {
-        DescriptorRef desc;
-        table.insert_column(col_ndx, type_Table, property.name, &desc);
-        desc->add_column(to_core_type(property.type & ~PropertyType::Flags), ObjectStore::ArrayColumnName,
-                         nullptr, is_nullable(property.type));
+        return table.add_column_list(to_core_type(property.type & ~PropertyType::Flags),
+                                     property.name, is_nullable(property.type));
     }
     }
     else {
     else {
-        table.insert_column(col_ndx, to_core_type(property.type), property.name, is_nullable(property.type));
+        auto key = table.add_column(to_core_type(property.type), property.name, is_nullable(property.type));
         if (property.requires_index())
         if (property.requires_index())
-            table.add_search_index(col_ndx);
+            table.add_search_index(key);
+        return key;
     }
     }
 }
 }
 
 
-void add_column(Group& group, Table& table, Property const& property)
+void replace_column(Group& group, Table& table, Property const& old_property,
+                    Property const& new_property)
 {
 {
-    insert_column(group, table, property, table.get_column_count());
-}
-
-void replace_column(Group& group, Table& table, Property const& old_property, Property const& new_property)
-{
-    insert_column(group, table, new_property, old_property.table_column);
-    table.remove_column(old_property.table_column + 1);
+    table.remove_column(old_property.column_key);
+    add_column(group, table, new_property);
 }
 }
 
 
 TableRef create_table(Group& group, ObjectSchema const& object_schema)
 TableRef create_table(Group& group, ObjectSchema const& object_schema)
@@ -147,18 +126,16 @@ TableRef create_table(Group& group, ObjectSchema const& object_schema)
     auto name = ObjectStore::table_name_for_object_type(object_schema.name);
     auto name = ObjectStore::table_name_for_object_type(object_schema.name);
 
 
     TableRef table;
     TableRef table;
-#if REALM_ENABLE_SYNC
     if (auto* pk_property = object_schema.primary_key_property()) {
     if (auto* pk_property = object_schema.primary_key_property()) {
-        table = sync::create_table_with_primary_key(group, name, to_core_type(pk_property->type),
-                                                    pk_property->name, is_nullable(pk_property->type));
+        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 {
     else {
-        table = sync::create_table(group, name);
+        table = group.get_or_add_table(name);
     }
     }
-#else
-    table = group.get_or_add_table(name);
-    ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key);
-#endif // REALM_ENABLE_SYNC
 
 
     return table;
     return table;
 }
 }
@@ -178,75 +155,20 @@ void add_initial_columns(Group& group, ObjectSchema const& object_schema)
     }
     }
 }
 }
 
 
-void copy_property_values(Property const& prop, Table& table)
-{
-    auto copy_property_values = [&](auto getter, auto setter) {
-        for (size_t i = 0, count = table.size(); i < count; i++) {
-            bool is_default = false;
-            (table.*setter)(prop.table_column, i, (table.*getter)(prop.table_column + 1, i),
-                            is_default);
-        }
-    };
-
-    switch (prop.type & ~PropertyType::Flags) {
-        case PropertyType::Int:
-            copy_property_values(&Table::get_int, &Table::set_int);
-            break;
-        case PropertyType::Bool:
-            copy_property_values(&Table::get_bool, &Table::set_bool);
-            break;
-        case PropertyType::Float:
-            copy_property_values(&Table::get_float, &Table::set_float);
-            break;
-        case PropertyType::Double:
-            copy_property_values(&Table::get_double, &Table::set_double);
-            break;
-        case PropertyType::String:
-            copy_property_values(&Table::get_string, &Table::set_string);
-            break;
-        case PropertyType::Data:
-            copy_property_values(&Table::get_binary, &Table::set_binary);
-            break;
-        case PropertyType::Date:
-            copy_property_values(&Table::get_timestamp, &Table::set_timestamp);
-            break;
-        default:
-            break;
-    }
-}
-
-void make_property_optional(Group& group, Table& table, Property property)
+void make_property_optional(Table& table, Property property)
 {
 {
     property.type |= PropertyType::Nullable;
     property.type |= PropertyType::Nullable;
-    insert_column(group, table, property, property.table_column);
-    copy_property_values(property, table);
-    table.remove_column(property.table_column + 1);
+    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)
 void make_property_required(Group& group, Table& table, Property property)
 {
 {
     property.type &= ~PropertyType::Nullable;
     property.type &= ~PropertyType::Nullable;
-    insert_column(group, table, property, property.table_column);
-    table.remove_column(property.table_column + 1);
-}
-
-void validate_primary_column_uniqueness(Group const& group, StringData object_type, StringData primary_property)
-{
-    auto table = ObjectStore::table_for_object_type(group, object_type);
-    if (table->get_distinct_view(table->get_column_index(primary_property)).size() != table->size()) {
-        throw DuplicatePrimaryKeyValueException(object_type, primary_property);
-    }
+    table.remove_column(property.column_key);
+    property.column_key = add_column(group, table, property).value;
 }
 }
 
 
-void validate_primary_column_uniqueness(Group const& group)
-{
-    auto pk_table = group.get_table(c_primaryKeyTableName);
-    for (size_t i = 0, count = pk_table->size(); i < count; ++i) {
-        validate_primary_column_uniqueness(group,
-                                           pk_table->get_string(c_primaryKeyObjectClassColumnIndex, i),
-                                           pk_table->get_string(c_primaryKeyPropertyNameColumnIndex, i));
-    }
-}
 } // anonymous namespace
 } // anonymous namespace
 
 
 void ObjectStore::set_schema_version(Group& group, uint64_t version) {
 void ObjectStore::set_schema_version(Group& group, uint64_t version) {
@@ -259,54 +181,26 @@ uint64_t ObjectStore::get_schema_version(Group const& group) {
     if (!table || table->get_column_count() == 0) {
     if (!table || table->get_column_count() == 0) {
         return ObjectStore::NotVersioned;
         return ObjectStore::NotVersioned;
     }
     }
-    return table->get_int(c_versionColumnIndex, c_zeroRowIndex);
+    return table->get_object(0).get<int64_t>(c_versionColumnName);
 }
 }
 
 
 StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) {
 StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) {
-    ConstTableRef table = group.get_table(c_primaryKeyTableName);
-    if (!table) {
-        return "";
-    }
-    size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
-    if (row == not_found) {
-        return "";
+    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 table->get_string(c_primaryKeyPropertyNameColumnIndex, row);
+    return "";
 }
 }
 
 
 void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) {
 void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) {
-    TableRef table = group.get_table(c_primaryKeyTableName);
-
-    size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
-
-#if REALM_ENABLE_SYNC
-    // sync::create_table* functions should have already updated the pk table.
-    if (sync::has_object_ids(group)) {
-        if (primary_key.size() == 0)
-            REALM_ASSERT(row == not_found);
-        else {
-             REALM_ASSERT(row != not_found);
-             REALM_ASSERT(table->get_string(c_primaryKeyPropertyNameColumnIndex, row) == primary_key);
-        }
-        return;
-    }
-#endif // REALM_ENABLE_SYNC
-
-    if (row == not_found && primary_key.size()) {
-        row = table->add_empty_row();
-        table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type);
-        table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key);
-        return;
-    }
-    // set if changing, or remove if setting to nil
-    if (primary_key.size() == 0) {
-        if (row != not_found) {
-            table->move_last_over(row);
-        }
-    }
-    else {
-        table->set_string(c_primaryKeyPropertyNameColumnIndex, row, 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) {
 StringData ObjectStore::object_type_for_table_name(StringData table_name) {
@@ -548,8 +442,8 @@ static void apply_non_migration_changes(Group& group, std::vector<SchemaChange>
 
 
         void operator()(AddTable op) { create_table(group, *op.object); }
         void operator()(AddTable op) { create_table(group, *op.object); }
         void operator()(AddInitialProperties op) { add_initial_columns(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->table_column); }
-        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+        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};
     } applier{group};
     verify_no_errors<SchemaMismatchException>(applier, changes);
     verify_no_errors<SchemaMismatchException>(applier, changes);
 }
 }
@@ -572,12 +466,12 @@ static void create_initial_tables(Group& group, std::vector<SchemaChange> const&
         // not-quite-correct files produced by other things and has no obvious
         // not-quite-correct files produced by other things and has no obvious
         // downside.
         // downside.
         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
-        void operator()(RemoveProperty op) { table(op.object).remove_column(op.property->table_column); }
-        void operator()(MakePropertyNullable op) { make_property_optional(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()(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()(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->table_column); }
-        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+        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)
         void operator()(ChangePropertyType op)
         {
         {
@@ -604,8 +498,8 @@ void ObjectStore::apply_additive_changes(Group& group, std::vector<SchemaChange>
         void operator()(RemoveTable) { }
         void operator()(RemoveTable) { }
         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
         void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
         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->table_column); }
-        void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->table_column); }
+        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) { }
         void operator()(RemoveProperty) { }
 
 
         // No need for errors for these, as we've already verified that they aren't present
         // No need for errors for these, as we've already verified that they aren't present
@@ -634,11 +528,11 @@ static void apply_pre_migration_changes(Group& group, std::vector<SchemaChange>
         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
         void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
         void operator()(RemoveProperty) { /* delayed until after the migration */ }
         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()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); }
-        void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.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()(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.c_str(), op.property ? op.property->name.c_str() : ""); }
-        void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
-        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+        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};
     } applier{group};
 
 
     for (auto& change : changes) {
     for (auto& change : changes) {
@@ -648,7 +542,9 @@ static void apply_pre_migration_changes(Group& group, std::vector<SchemaChange>
 
 
 enum class DidRereadSchema { Yes, No };
 enum class DidRereadSchema { Yes, No };
 
 
-static void apply_post_migration_changes(Group& group, std::vector<SchemaChange> const& changes, Schema const& initial_schema,
+static void apply_post_migration_changes(Group& group,
+                                         std::vector<SchemaChange> const& changes,
+                                         Schema const& initial_schema,
                                          DidRereadSchema did_reread_schema)
                                          DidRereadSchema did_reread_schema)
 {
 {
     using namespace schema_change;
     using namespace schema_change;
@@ -667,13 +563,19 @@ static void apply_post_migration_changes(Group& group, std::vector<SchemaChange>
             if (!initial_schema.empty() && !initial_schema.find(op.object->name)->property_for_name(op.property->name))
             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));
                 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);
             auto table = table_for_object_schema(group, *op.object);
-            table->remove_column(op.property->table_column);
+            table->remove_column(op.property->column_key);
         }
         }
 
 
         void operator()(ChangePrimaryKey op)
         void operator()(ChangePrimaryKey op)
         {
         {
+            Table& t = table(op.object);
             if (op.property) {
             if (op.property) {
-                validate_primary_column_uniqueness(group, op.object->name, op.property->name);
+                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());
             }
             }
         }
         }
 
 
@@ -688,8 +590,8 @@ static void apply_post_migration_changes(Group& group, std::vector<SchemaChange>
             }
             }
         }
         }
 
 
-        void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
-        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+        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()(RemoveTable) { }
         void operator()(ChangePropertyType) { }
         void operator()(ChangePropertyType) { }
@@ -703,7 +605,7 @@ static void apply_post_migration_changes(Group& group, std::vector<SchemaChange>
     }
     }
 }
 }
 
 
-static void create_default_permissions(Group& group, std::vector<SchemaChange> const& changes,
+static void create_default_permissions(Transaction& group, std::vector<SchemaChange> const& changes,
                                        std::string const& sync_user_id)
                                        std::string const& sync_user_id)
 {
 {
 #if !REALM_ENABLE_SYNC
 #if !REALM_ENABLE_SYNC
@@ -725,7 +627,7 @@ static void create_default_permissions(Group& group, std::vector<SchemaChange> c
     // sure that the permissions tables actually exist.
     // sure that the permissions tables actually exist.
     using namespace schema_change;
     using namespace schema_change;
     struct Applier {
     struct Applier {
-        Group& group;
+        Transaction& group;
         void operator()(AddTable op)
         void operator()(AddTable op)
         {
         {
             sync::set_class_permissions_for_role(group, op.object->name, "everyone",
             sync::set_class_permissions_for_role(group, op.object->name, "everyone",
@@ -751,13 +653,13 @@ static void create_default_permissions(Group& group, std::vector<SchemaChange> c
 }
 }
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
-void ObjectStore::ensure_private_role_exists_for_user(Group& group, StringData sync_user_id)
+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);
     std::string private_role_name = util::format("__User:%1", sync_user_id);
 
 
     TableRef roles = ObjectStore::table_for_object_type(group, "__Role");
     TableRef roles = ObjectStore::table_for_object_type(group, "__Role");
-    size_t private_role_ndx = roles->find_first_string(roles->get_column_index("name"), private_role_name);
-    if (private_role_ndx != npos) {
+    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.
         // The private role already exists, so there's nothing for us to do.
         return;
         return;
     }
     }
@@ -766,14 +668,14 @@ void ObjectStore::ensure_private_role_exists_for_user(Group& group, StringData s
     sync::add_user_to_role(group, sync_user_id, private_role_name);
     sync::add_user_to_role(group, sync_user_id, private_role_name);
 
 
     // Set the private role on the user.
     // Set the private role on the user.
-    private_role_ndx = roles->find_first_string(roles->get_column_index("name"), private_role_name);
+    private_role_ndx = roles->find_first_string(roles->get_column_key("name"), private_role_name);
     TableRef users = ObjectStore::table_for_object_type(group, "__User");
     TableRef users = ObjectStore::table_for_object_type(group, "__User");
-    size_t user_ndx = users->find_first_string(users->get_column_index("id"), sync_user_id);
-    users->set_link(users->get_column_index("role"), user_ndx, private_role_ndx);
+    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
 #endif
 
 
-void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version,
+void ObjectStore::apply_schema_changes(Transaction& group, uint64_t schema_version,
                                        Schema& target_schema, uint64_t target_schema_version,
                                        Schema& target_schema, uint64_t target_schema_version,
                                        SchemaMode mode, std::vector<SchemaChange> const& changes,
                                        SchemaMode mode, std::vector<SchemaChange> const& changes,
                                        util::Optional<std::string> sync_user_id,
                                        util::Optional<std::string> sync_user_id,
@@ -795,121 +697,112 @@ void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version,
         if (sync_user_id)
         if (sync_user_id)
             create_default_permissions(group, changes, *sync_user_id);
             create_default_permissions(group, changes, *sync_user_id);
 
 
-        set_schema_columns(group, target_schema);
+        set_schema_keys(group, target_schema);
         return;
         return;
     }
     }
 
 
     if (schema_version == ObjectStore::NotVersioned) {
     if (schema_version == ObjectStore::NotVersioned) {
         create_initial_tables(group, changes);
         create_initial_tables(group, changes);
         set_schema_version(group, target_schema_version);
         set_schema_version(group, target_schema_version);
-        set_schema_columns(group, target_schema);
+        set_schema_keys(group, target_schema);
         return;
         return;
     }
     }
 
 
     if (mode == SchemaMode::Manual) {
     if (mode == SchemaMode::Manual) {
-        set_schema_columns(group, target_schema);
+        set_schema_keys(group, target_schema);
         if (migration_function) {
         if (migration_function) {
             migration_function();
             migration_function();
         }
         }
 
 
         verify_no_changes_required(schema_from_group(group).compare(target_schema));
         verify_no_changes_required(schema_from_group(group).compare(target_schema));
-        validate_primary_column_uniqueness(group);
-        set_schema_columns(group, target_schema);
+        group.validate_primary_columns();
+        set_schema_keys(group, target_schema);
         set_schema_version(group, target_schema_version);
         set_schema_version(group, target_schema_version);
         return;
         return;
     }
     }
 
 
     if (schema_version == target_schema_version) {
     if (schema_version == target_schema_version) {
         apply_non_migration_changes(group, changes);
         apply_non_migration_changes(group, changes);
-        set_schema_columns(group, target_schema);
+        set_schema_keys(group, target_schema);
         return;
         return;
     }
     }
 
 
     auto old_schema = schema_from_group(group);
     auto old_schema = schema_from_group(group);
     apply_pre_migration_changes(group, changes);
     apply_pre_migration_changes(group, changes);
     if (migration_function) {
     if (migration_function) {
-        set_schema_columns(group, target_schema);
+        set_schema_keys(group, target_schema);
         migration_function();
         migration_function();
 
 
         // Migration function may have changed the schema, so we need to re-read it
         // Migration function may have changed the schema, so we need to re-read it
         auto schema = schema_from_group(group);
         auto schema = schema_from_group(group);
         apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes);
         apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes);
-        validate_primary_column_uniqueness(group);
+        group.validate_primary_columns();
     }
     }
     else {
     else {
         apply_post_migration_changes(group, changes, {}, DidRereadSchema::No);
         apply_post_migration_changes(group, changes, {}, DidRereadSchema::No);
     }
     }
 
 
     set_schema_version(group, target_schema_version);
     set_schema_version(group, target_schema_version);
-    set_schema_columns(group, target_schema);
+    set_schema_keys(group, target_schema);
 }
 }
 
 
 Schema ObjectStore::schema_from_group(Group const& group) {
 Schema ObjectStore::schema_from_group(Group const& group) {
     std::vector<ObjectSchema> schema;
     std::vector<ObjectSchema> schema;
     schema.reserve(group.size());
     schema.reserve(group.size());
-    for (size_t i = 0; i < group.size(); i++) {
-        auto object_type = object_type_for_table_name(group.get_table_name(i));
+    for (auto key : group.get_table_keys()) {
+        auto object_type = object_type_for_table_name(group.get_table_name(key));
         if (object_type.size()) {
         if (object_type.size()) {
-            schema.emplace_back(group, object_type, i);
+            schema.emplace_back(group, object_type, key);
         }
         }
     }
     }
     return schema;
     return schema;
 }
 }
 
 
-util::Optional<Property> ObjectStore::property_for_column_index(ConstTableRef& table, size_t column_index)
+util::Optional<Property> ObjectStore::property_for_column_index(ConstTableRef& table, ColKey column_key)
 {
 {
-    StringData column_name = table->get_column_name(column_index);
-
-#if REALM_ENABLE_SYNC
-    // The object ID column is an implementation detail, and is omitted from the schema.
-    // FIXME: Consider filtering out all column names starting with `!`.
-    if (column_name == sync::object_id_column_name)
-        return util::none;
-#endif
-
-    if (table->get_column_type(column_index) == type_Table) {
-        auto subdesc = table->get_subdescriptor(column_index);
-        if (subdesc->get_column_count() != 1 || subdesc->get_column_name(0) != ObjectStore::ArrayColumnName)
-            return util::none;
-    }
+    StringData column_name = table->get_column_name(column_key);
 
 
     Property property;
     Property property;
     property.name = column_name;
     property.name = column_name;
-    property.type = ObjectSchema::from_core_type(*table->get_descriptor(), column_index);
-    property.is_indexed = table->has_search_index(column_index);
-    property.table_column = column_index;
+    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) {
     if (property.type == PropertyType::Object) {
         // set link type for objects and arrays
         // set link type for objects and arrays
-        ConstTableRef linkTable = table->get_link_target(column_index);
+        ConstTableRef linkTable = table->get_link_target(column_key);
         property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
         property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
     }
     }
     return property;
     return property;
 }
 }
 
 
-void ObjectStore::set_schema_columns(Group const& group, Schema& schema)
+void ObjectStore::set_schema_keys(Group const& group, Schema& schema)
 {
 {
     for (auto& object_schema : schema) {
     for (auto& object_schema : schema) {
         auto table = table_for_object_schema(group, object_schema);
         auto table = table_for_object_schema(group, object_schema);
         if (!table) {
         if (!table) {
             continue;
             continue;
         }
         }
+        object_schema.table_key = table->get_key();
         for (auto& property : object_schema.persisted_properties) {
         for (auto& property : object_schema.persisted_properties) {
-            property.table_column = table->get_column_index(property.name);
+            property.column_key = table->get_column_key(property.name);
         }
         }
     }
     }
 }
 }
 
 
-void ObjectStore::delete_data_for_object(Group& group, StringData object_type) {
+void ObjectStore::delete_data_for_object(Group& group, StringData object_type)
+{
     if (TableRef table = table_for_object_type(group, object_type)) {
     if (TableRef table = table_for_object_type(group, object_type)) {
-        group.remove_table(table->get_index_in_group());
         ObjectStore::set_primary_key_for_object(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 (size_t i = 0; i < group.size(); i++) {
-        ConstTableRef table = group.get_table(i);
+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());
         auto object_type = object_type_for_table_name(table->get_name());
         if (object_type.size() == 0 || object_type.begins_with("__")) {
         if (object_type.size() == 0 || object_type.begins_with("__")) {
             continue;
             continue;
@@ -938,7 +831,7 @@ void ObjectStore::rename_property(Group& group, Schema& target_schema, StringDat
                                             object_type, old_name, new_name));
                                             object_type, old_name, new_name));
     }
     }
 
 
-    ObjectSchema table_object_schema(group, object_type);
+    ObjectSchema table_object_schema(group, object_type, table->get_key());
     Property *old_property = table_object_schema.property_for_name(old_name);
     Property *old_property = table_object_schema.property_for_name(old_name);
     if (!old_property) {
     if (!old_property) {
         throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name));
         throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name));
@@ -950,7 +843,7 @@ void ObjectStore::rename_property(Group& group, Schema& target_schema, StringDat
         // renaming to an intermediate property in a multi-version migration.
         // renaming to an intermediate property in a multi-version migration.
         // This is safe because the migration will fail schema validation unless
         // This is safe because the migration will fail schema validation unless
         // this property is renamed again to a valid name before the end.
         // this property is renamed again to a valid name before the end.
-        table->rename_column(old_property->table_column, new_name);
+        table->rename_column(old_property->column_key, new_name);
         return;
         return;
     }
     }
 
 
@@ -964,23 +857,18 @@ void ObjectStore::rename_property(Group& group, Schema& target_schema, StringDat
                                             object_type, old_name, new_name));
                                             object_type, old_name, new_name));
     }
     }
 
 
-    size_t column_to_remove = new_property->table_column;
-    table->rename_column(old_property->table_column, new_name);
-    table->remove_column(column_to_remove);
+    table->remove_column(new_property->column_key);
+    table->rename_column(old_property->column_key, new_name);
 
 
-    // update table_column for each property since it may have shifted
-    for (auto& current_prop : target_object_schema->persisted_properties) {
-        if (current_prop.table_column == column_to_remove)
-            current_prop.table_column = old_property->table_column;
-        else if (current_prop.table_column > column_to_remove)
-            --current_prop.table_column;
+    if (auto prop = target_object_schema->property_for_name(new_name)) {
+        prop->column_key = old_property->column_key;
     }
     }
 
 
     // update nullability for column
     // update nullability for column
     if (is_nullable(new_property->type) && !is_nullable(old_property->type)) {
     if (is_nullable(new_property->type) && !is_nullable(old_property->type)) {
         auto prop = *new_property;
         auto prop = *new_property;
-        prop.table_column = old_property->table_column;
-        make_property_optional(group, *table, prop);
+        prop.column_key = old_property->column_key;
+        make_property_optional(*table, prop);
     }
     }
 }
 }
 
 
@@ -990,12 +878,6 @@ InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_versio
 {
 {
 }
 }
 
 
-DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, std::string property)
-: logic_error(util::format("Primary key property '%1.%2' has duplicate values after migration.", object_type, property))
-, m_object_type(object_type), m_property(property)
-{
-}
-
 SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
 SchemaValidationException::SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors)
 : std::logic_error([&] {
 : std::logic_error([&] {
     std::string message = "Schema validation failed due to the following errors:";
     std::string message = "Schema validation failed due to the following errors:";

+ 7 - 15
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.hpp

@@ -27,8 +27,11 @@
 #include <functional>
 #include <functional>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
+#include <limits>
+
 namespace realm {
 namespace realm {
 class Group;
 class Group;
+class Transaction;
 class Schema;
 class Schema;
 class SchemaChange;
 class SchemaChange;
 class StringData;
 class StringData;
@@ -82,7 +85,7 @@ public:
     // passed in target schema is updated with the correct column mapping
     // passed in target schema is updated with the correct column mapping
     // optionally runs migration function if schema is out of date
     // optionally runs migration function if schema is out of date
     // NOTE: must be performed within a write transaction
     // NOTE: must be performed within a write transaction
-    static void apply_schema_changes(Group& group, uint64_t schema_version,
+    static void apply_schema_changes(Transaction& group, uint64_t schema_version,
                                      Schema& target_schema, uint64_t target_schema_version,
                                      Schema& target_schema, uint64_t target_schema_version,
                                      SchemaMode mode, std::vector<SchemaChange> const& changes,
                                      SchemaMode mode, std::vector<SchemaChange> const& changes,
                                      util::Optional<std::string> sync_user_id,
                                      util::Optional<std::string> sync_user_id,
@@ -99,9 +102,9 @@ public:
 
 
     // get the property for a existing column in the given table. return none if the column is reserved internally.
     // 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.
     // NOTE: is_primary won't be set for the returned property.
-    static util::Optional<Property> property_for_column_index(ConstTableRef& table, size_t column_index);
+    static util::Optional<Property> property_for_column_index(ConstTableRef& table, ColKey column_key);
 
 
-    static void set_schema_columns(Group const& group, Schema& schema);
+    static void set_schema_keys(Group const& group, Schema& schema);
 
 
     // deletes the table for the given type
     // deletes the table for the given type
     static void delete_data_for_object(Group& group, StringData object_type);
     static void delete_data_for_object(Group& group, StringData object_type);
@@ -123,7 +126,7 @@ public:
     static StringData object_type_for_table_name(StringData table_name);
     static StringData object_type_for_table_name(StringData table_name);
 
 
     // creates the private role for the given user if it does not exist
     // creates the private role for the given user if it does not exist
-    static void ensure_private_role_exists_for_user(Group& group, StringData sync_user_id);
+    static void ensure_private_role_exists_for_user(Transaction& group, StringData sync_user_id);
 
 
 private:
 private:
     friend class ObjectSchema;
     friend class ObjectSchema;
@@ -138,17 +141,6 @@ private:
     uint64_t m_old_version, m_new_version;
     uint64_t m_old_version, m_new_version;
 };
 };
 
 
-class DuplicatePrimaryKeyValueException : public std::logic_error {
-public:
-    DuplicatePrimaryKeyValueException(std::string object_type, std::string property);
-
-    std::string const& object_type() const { return m_object_type; }
-    std::string const& property() const { return m_property; }
-private:
-    std::string m_object_type;
-    std::string m_property;
-};
-
 // Schema validation exceptions
 // Schema validation exceptions
 struct ObjectSchemaValidationException : public std::logic_error {
 struct ObjectSchemaValidationException : public std::logic_error {
     ObjectSchemaValidationException(std::string message) : logic_error(std::move(message)) {}
     ObjectSchemaValidationException(std::string message) : logic_error(std::move(message)) {}

+ 17 - 16
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/property.hpp

@@ -22,6 +22,8 @@
 #include "util/tagged_bool.hpp"
 #include "util/tagged_bool.hpp"
 
 
 #include <realm/util/features.h>
 #include <realm/util/features.h>
+// FIXME: keys.hpp is currently pretty heavyweight
+#include <realm/keys.hpp>
 
 
 #include <string>
 #include <string>
 
 
@@ -29,13 +31,11 @@ namespace realm {
 namespace util {
 namespace util {
     template<typename> class Optional;
     template<typename> class Optional;
 }
 }
-class StringData;
 class BinaryData;
 class BinaryData;
-class Timestamp;
+class Obj;
+class StringData;
 class Table;
 class Table;
-
-template<typename> class BasicRowExpr;
-using RowExpr = BasicRowExpr<Table>;
+class Timestamp;
 
 
 enum class PropertyType : unsigned char {
 enum class PropertyType : unsigned char {
     Int    = 0,
     Int    = 0,
@@ -86,24 +86,25 @@ struct Property {
     IsPrimary is_primary = false;
     IsPrimary is_primary = false;
     IsIndexed is_indexed = false;
     IsIndexed is_indexed = false;
 
 
-    size_t table_column = -1;
+    ColKey column_key;
 
 
     Property() = default;
     Property() = default;
 
 
-    Property(std::string name, PropertyType type, IsPrimary primary = false, IsIndexed indexed = false, std::string public_name = "");
+    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,
     Property(std::string name, PropertyType type, std::string object_type,
              std::string link_origin_property_name = "", std::string public_name = "");
              std::string link_origin_property_name = "", std::string public_name = "");
 
 
     Property(Property const&) = default;
     Property(Property const&) = default;
-    Property(Property&&) = default;
+    Property(Property&&) noexcept = default;
     Property& operator=(Property const&) = default;
     Property& operator=(Property const&) = default;
-    Property& operator=(Property&&) = default;
+    Property& operator=(Property&&) noexcept = default;
 
 
     bool requires_index() const { return is_primary || is_indexed; }
     bool requires_index() const { return is_primary || is_indexed; }
 
 
-    bool type_is_indexable() const;
-    bool type_is_nullable() const;
+    bool type_is_indexable() const noexcept;
+    bool type_is_nullable() const noexcept;
 
 
     std::string type_string() const;
     std::string type_string() const;
 };
 };
@@ -172,7 +173,7 @@ inline constexpr bool is_nullable(PropertyType a)
     return to_underlying(a & PropertyType::Nullable) == to_underlying(PropertyType::Nullable);
     return to_underlying(a & PropertyType::Nullable) == to_underlying(PropertyType::Nullable);
 }
 }
 
 
-template<typename Fn>
+template<typename ObjType=Obj, typename Fn>
 static auto switch_on_type(PropertyType type, Fn&& fn)
 static auto switch_on_type(PropertyType type, Fn&& fn)
 {
 {
     using PT = PropertyType;
     using PT = PropertyType;
@@ -185,7 +186,7 @@ static auto switch_on_type(PropertyType type, Fn&& fn)
         case PT::String: return fn((StringData*)0);
         case PT::String: return fn((StringData*)0);
         case PT::Data:   return fn((BinaryData*)0);
         case PT::Data:   return fn((BinaryData*)0);
         case PT::Date:   return fn((Timestamp*)0);
         case PT::Date:   return fn((Timestamp*)0);
-        case PT::Object: return fn((RowExpr*)0);
+        case PT::Object: return fn((ObjType*)0);
         default: REALM_COMPILER_HINT_UNREACHABLE();
         default: REALM_COMPILER_HINT_UNREACHABLE();
     }
     }
 }
 }
@@ -235,7 +236,7 @@ inline Property::Property(std::string name, PropertyType type,
 {
 {
 }
 }
 
 
-inline bool Property::type_is_indexable() const
+inline bool Property::type_is_indexable() const noexcept
 {
 {
     return type == PropertyType::Int
     return type == PropertyType::Int
         || type == PropertyType::Bool
         || type == PropertyType::Bool
@@ -243,7 +244,7 @@ inline bool Property::type_is_indexable() const
         || type == PropertyType::String;
         || type == PropertyType::String;
 }
 }
 
 
-inline bool Property::type_is_nullable() const
+inline bool Property::type_is_nullable() const noexcept
 {
 {
     return !(is_array(type) && type == PropertyType::Object) && type != PropertyType::LinkingObjects;
     return !(is_array(type) && type == PropertyType::Object) && type != PropertyType::LinkingObjects;
 }
 }
@@ -269,7 +270,7 @@ inline std::string Property::type_string() const
 
 
 inline bool operator==(Property const& lft, Property const& rgt)
 inline bool operator==(Property const& lft, Property const& rgt)
 {
 {
-    // note: not checking table_column
+    // note: not checking column_key
     // ordered roughly by the cost of the check
     // ordered roughly by the cost of the check
     return to_underlying(lft.type) == to_underlying(rgt.type)
     return to_underlying(lft.type) == to_underlying(rgt.type)
         && lft.is_primary == rgt.is_primary
         && lft.is_primary == rgt.is_primary

File diff suppressed because it is too large
+ 456 - 211
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp


+ 120 - 90
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp

@@ -26,6 +26,8 @@
 #include "object_schema.hpp"
 #include "object_schema.hpp"
 #include "property.hpp"
 #include "property.hpp"
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
+#include "util/checked_mutex.hpp"
+#include "util/copyable_atomic.hpp"
 
 
 #include <realm/table_view.hpp>
 #include <realm/table_view.hpp>
 #include <realm/util/optional.hpp>
 #include <realm/util/optional.hpp>
@@ -35,7 +37,7 @@ class Mixed;
 class ObjectSchema;
 class ObjectSchema;
 
 
 namespace _impl {
 namespace _impl {
-    class ResultsNotifier;
+    class ResultsNotifierBase;
 }
 }
 
 
 class Results {
 class Results {
@@ -44,10 +46,12 @@ public:
     // or a wrapper around a query and a sort order which creates and updates
     // or a wrapper around a query and a sort order which creates and updates
     // the tableview as needed
     // the tableview as needed
     Results();
     Results();
-    Results(std::shared_ptr<Realm> r, Table& table);
+    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, Query q, DescriptorOrdering o = {});
     Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o = {});
     Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o = {});
-    Results(std::shared_ptr<Realm> r, LinkViewRef lv, util::Optional<Query> q = {}, SortDescriptor s = {});
+    Results(std::shared_ptr<Realm> r, std::shared_ptr<LnkLst> list, util::Optional<Query> q = {}, SortDescriptor s = {});
     ~Results();
     ~Results();
 
 
     // Results is copyable and moveable
     // Results is copyable and moveable
@@ -60,100 +64,112 @@ public:
     std::shared_ptr<Realm> get_realm() const { return m_realm; }
     std::shared_ptr<Realm> get_realm() const { return m_realm; }
 
 
     // Object schema describing the vendored object type
     // Object schema describing the vendored object type
-    const ObjectSchema &get_object_schema() const;
+    const ObjectSchema &get_object_schema() const REQUIRES(!m_mutex);
 
 
     // Get a query which will match the same rows as is contained in this Results
     // 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
     // Returned query will not be valid if the current mode is Empty
-    Query get_query() const;
+    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.
     // Get the list of sort and distinct operations applied for this Results.
     DescriptorOrdering const& get_descriptor_ordering() const noexcept { return m_descriptor_ordering; }
     DescriptorOrdering const& get_descriptor_ordering() const noexcept { return m_descriptor_ordering; }
 
 
     // Get a tableview containing the same rows as this Results
     // Get a tableview containing the same rows as this Results
-    TableView get_tableview();
+    TableView get_tableview() REQUIRES(!m_mutex);
 
 
     // Get the object type which will be returned by get()
     // Get the object type which will be returned by get()
     StringData get_object_type() const noexcept;
     StringData get_object_type() const noexcept;
 
 
-    PropertyType get_type() const;
-
-    // Get the LinkView this Results is derived from, if any
-    LinkViewRef get_linkview() const { return m_link_view; }
+    PropertyType get_type() const REQUIRES(!m_mutex);
 
 
     // Get the size of this results
     // Get the size of this results
     // Can be either O(1) or O(N) depending on the state of things
     // Can be either O(1) or O(N) depending on the state of things
-    size_t size();
+    size_t size() REQUIRES(!m_mutex);
 
 
     // Get the row accessor for the given index
     // Get the row accessor for the given index
     // Throws OutOfBoundsIndexException if index >= size()
     // Throws OutOfBoundsIndexException if index >= size()
-    template<typename T = RowExpr>
-    T get(size_t index);
+    template<typename T = Obj>
+    T get(size_t index) REQUIRES(!m_mutex);
 
 
     // Get the boxed row accessor for the given index
     // Get the boxed row accessor for the given index
     // Throws OutOfBoundsIndexException if index >= size()
     // Throws OutOfBoundsIndexException if index >= size()
     template<typename Context>
     template<typename Context>
-    auto get(Context&, size_t index);
+    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
     // Get a row accessor for the first/last row, or none if the results are empty
     // More efficient than calling size()+get()
     // More efficient than calling size()+get()
-    template<typename T = RowExpr>
-    util::Optional<T> first();
-    template<typename T = RowExpr>
-    util::Optional<T> last();
+    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
     // Get the index of the first row matching the query in this table
-    size_t index_of(Query&& q);
+    size_t index_of(Query&& q) REQUIRES(!m_mutex);
 
 
     // Get the first index of the given value in this results, or not_found
     // Get the first index of the given value in this results, or not_found
     // Throws DetachedAccessorException if row is not attached
     // Throws DetachedAccessorException if row is not attached
     // Throws IncorrectTableException if row belongs to a different table
     // Throws IncorrectTableException if row belongs to a different table
     template<typename T>
     template<typename T>
-    size_t index_of(T const& value);
+    size_t index_of(T const& value) REQUIRES(!m_mutex);
 
 
     // Delete all of the rows in this Results from the Realm
     // Delete all of the rows in this Results from the Realm
     // size() will always be zero afterwards
     // size() will always be zero afterwards
     // Throws InvalidTransactionException if not in a write transaction
     // Throws InvalidTransactionException if not in a write transaction
-    void clear();
+    void clear() REQUIRES(!m_mutex);
 
 
     // Create a new Results by further filtering or sorting this Results
     // Create a new Results by further filtering or sorting this Results
-    Results filter(Query&& q) const;
-    Results sort(SortDescriptor&& sort) const;
-    Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const;
+    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
     // Create a new Results by removing duplicates
-    Results distinct(DistinctDescriptor&& uniqueness) const;
-    Results distinct(std::vector<std::string> const& keypaths) const;
+    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
     // Create a new Results with only the first `max_count` entries
-    Results limit(size_t max_count) const;
+    Results limit(size_t max_count) const REQUIRES(!m_mutex);
 
 
     // Create a new Results by adding sort and distinct combinations
     // Create a new Results by adding sort and distinct combinations
-    Results apply_ordering(DescriptorOrdering&& ordering);
+    Results apply_ordering(DescriptorOrdering&& ordering) REQUIRES(!m_mutex);
 
 
     // Return a snapshot of this Results that never updates to reflect changes in the underlying data.
     // Return a snapshot of this Results that never updates to reflect changes in the underlying data.
-    Results snapshot() const &;
-    Results snapshot() &&;
+    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
     // Get the min/max/average/sum of the given column
     // All but sum() returns none when there are zero matching rows
     // All but sum() returns none when there are zero matching rows
     // sum() returns 0, except for when it returns none
     // sum() returns 0, except for when it returns none
     // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
     // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
     // Throws OutOfBoundsIndexException for an out-of-bounds column
     // Throws OutOfBoundsIndexException for an out-of-bounds column
-    util::Optional<Mixed> max(size_t column=0);
-    util::Optional<Mixed> min(size_t column=0);
-    util::Optional<double> average(size_t column=0);
-    util::Optional<Mixed> sum(size_t column=0);
+    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 {
     enum class Mode {
         Empty, // Backed by nothing (for missing tables)
         Empty, // Backed by nothing (for missing tables)
         Table, // Backed directly by a Table
         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
         Query, // Backed by a query that has not yet been turned into a TableView
-        LinkView,  // Backed directly by a LinkView
+        LinkList,  // Backed directly by a LinkList
         TableView, // Backed by a TableView created from a Query
         TableView, // Backed by a TableView created from a Query
     };
     };
     // Get the currrent mode of the Results
     // Get the currrent mode of the Results
     // Ideally this would not be public but it's needed for some KVO stuff
     // Ideally this would not be public but it's needed for some KVO stuff
-    Mode get_mode() const { return m_mode; }
+    Mode get_mode() const noexcept REQUIRES(!m_mutex);
 
 
     // Is this Results associated with a Realm that has not been invalidated?
     // Is this Results associated with a Realm that has not been invalidated?
     bool is_valid() const;
     bool is_valid() const;
@@ -186,16 +202,17 @@ public:
 
 
     // The requested aggregate operation is not supported for the column type
     // The requested aggregate operation is not supported for the column type
     struct UnsupportedColumnTypeException : public std::logic_error {
     struct UnsupportedColumnTypeException : public std::logic_error {
-        size_t column_index;
+        ColKey column_key;
         StringData column_name;
         StringData column_name;
         PropertyType property_type;
         PropertyType property_type;
 
 
-        UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation);
+        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
     // The property request does not exist in the schema
     struct InvalidPropertyException : public std::logic_error {
     struct InvalidPropertyException : public std::logic_error {
-        InvalidPropertyException(const std::string& object_type, const std::string& property_name);
+        InvalidPropertyException(StringData object_type, StringData property_name);
         const std::string object_type;
         const std::string object_type;
         const std::string property_name;
         const std::string property_name;
 	};
 	};
@@ -208,91 +225,104 @@ public:
     // Create an async query from this Results
     // Create an async query from this Results
     // The query will be run on a background thread and delivered to the callback,
     // 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
     // and then rerun after each commit (if needed) and redelivered if it changed
-    template<typename Func>
-    NotificationToken async(Func&& target);
     NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
     NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
 
 
-    bool wants_background_updates() const { return m_wants_background_updates; }
-
     // Returns whether the rows are guaranteed to be in table order.
     // Returns whether the rows are guaranteed to be in table order.
     bool is_in_table_order() const;
     bool is_in_table_order() const;
 
 
-    // Helper type to let ResultsNotifier update the tableview without giving access
-    // to any other privates or letting anyone else do so
-    class Internal {
-        friend class _impl::ResultsNotifier;
-        static void set_table_view(Results& results, TableView&& tv);
-    };
-
-    template<typename Context> auto first(Context&);
-    template<typename Context> auto last(Context&);
+    template<typename Context> auto first(Context&) REQUIRES(!m_mutex);
+    template<typename Context> auto last(Context&) REQUIRES(!m_mutex);
 
 
     template<typename Context, typename T>
     template<typename Context, typename T>
-    size_t index_of(Context&, T value);
+    size_t index_of(Context&, T value) REQUIRES(!m_mutex);
 
 
     // Batch updates all items in this collection with the provided value
     // Batch updates all items in this collection with the provided value
     // Must be called inside a transaction
     // Must be called inside a transaction
     // Throws an exception if the value does not match the type for given prop_name
     // Throws an exception if the value does not match the type for given prop_name
     template<typename ValueType, typename ContextType>
     template<typename ValueType, typename ContextType>
-    void set_property_value(ContextType& ctx, StringData prop_name, ValueType value);
+    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()
     // 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
     // may cost similar time compared with creating the tableview. Use this function to
     // avoid running the query twice for size() and other accessors.
     // avoid running the query twice for size() and other accessors.
-    void evaluate_query_if_needed(bool wants_notifications = true);
+    void evaluate_query_if_needed(bool wants_notifications = true) REQUIRES(!m_mutex);
 
 
-private:
     enum class UpdatePolicy {
     enum class UpdatePolicy {
-        Auto,  // Update automatically to reflect changes in the underlying data.
-        Never, // Never update.
+        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;
     std::shared_ptr<Realm> m_realm;
-    mutable const ObjectSchema *m_object_schema = nullptr;
-    Query m_query;
-    TableView m_table_view;
-    LinkViewRef m_link_view;
-    TableRef m_table;
+    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;
     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::ResultsNotifier> m_notifier;
+    _impl::CollectionNotifier::Handle<_impl::ResultsNotifierBase> m_notifier;
 
 
-    Mode m_mode = Mode::Empty;
+    Mode m_mode GUARDED_BY(m_mutex) = Mode::Empty;
     UpdatePolicy m_update_policy = UpdatePolicy::Auto;
     UpdatePolicy m_update_policy = UpdatePolicy::Auto;
-    bool m_has_used_table_view = false;
-    bool m_wants_background_updates = true;
 
 
-    bool update_linkview();
+    bool update_linklist() REQUIRES(m_mutex);
 
 
     void validate_read() const;
     void validate_read() const;
     void validate_write() 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>;
     using ForCallback = util::TaggedBool<class ForCallback>;
     void prepare_async(ForCallback);
     void prepare_async(ForCallback);
 
 
-    template<typename T>
-    util::Optional<T> try_get(size_t);
+    ColKey key(StringData) const;
 
 
-    template<typename Int, typename Float, typename Double, typename Timestamp>
-    util::Optional<Mixed> aggregate(size_t column,
-                                    const char* name,
-                                    Int agg_int, Float agg_float,
-                                    Double agg_double, Timestamp agg_timestamp);
-    void prepare_for_aggregate(size_t column, const char* name);
+    template<typename T>
+    util::Optional<T> try_get(size_t) REQUIRES(m_mutex);
 
 
-    void set_table_view(TableView&& tv);
+    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>
     template<typename Fn>
-    auto dispatch(Fn&&) const;
-};
+    auto dispatch(Fn&&) const REQUIRES(!m_mutex);
 
 
-template<typename Func>
-NotificationToken Results::async(Func&& target)
-{
-    return this->add_notification_callback([target = std::forward<Func>(target)](CollectionChangeSet const&, std::exception_ptr e) {
-        target(e);
-    });
-}
+    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>
 template<typename Fn>
 auto Results::dispatch(Fn&& fn) const
 auto Results::dispatch(Fn&& fn) const
@@ -312,7 +342,7 @@ auto Results::first(Context& ctx)
     // GCC 4.9 complains about `ctx` not being defined within the lambda without this goofy capture
     // GCC 4.9 complains about `ctx` not being defined within the lambda without this goofy capture
     return dispatch([this, ctx = &ctx](auto t) {
     return dispatch([this, ctx = &ctx](auto t) {
         auto value = this->first<std::decay_t<decltype(*t)>>();
         auto value = this->first<std::decay_t<decltype(*t)>>();
-        return value ? static_cast<decltype(ctx->no_value())>(ctx->box(*value)) : ctx->no_value();
+        return value ? static_cast<decltype(ctx->no_value())>(ctx->box(std::move(*value))) : ctx->no_value();
     });
     });
 }
 }
 
 
@@ -321,7 +351,7 @@ auto Results::last(Context& ctx)
 {
 {
     return dispatch([&](auto t) {
     return dispatch([&](auto t) {
         auto value = this->last<std::decay_t<decltype(*t)>>();
         auto value = this->last<std::decay_t<decltype(*t)>>();
-        return value ? static_cast<decltype(ctx.no_value())>(ctx.box(*value)) : ctx.no_value();
+        return value ? static_cast<decltype(ctx.no_value())>(ctx.box(std::move(*value))) : ctx.no_value();
     });
     });
 }
 }
 
 
@@ -334,7 +364,7 @@ size_t Results::index_of(Context& ctx, T value)
 }
 }
 
 
 template <typename ValueType, typename ContextType>
 template <typename ValueType, typename ContextType>
-void Results::set_property_value(ContextType& ctx, StringData prop_name, ValueType value)
+void Results::set_property_value(ContextType& ctx, StringData prop_name, ValueType value) NO_THREAD_SAFETY_ANALYSIS
 {
 {
     // Check invariants for calling this method
     // Check invariants for calling this method
     validate_write();
     validate_write();
@@ -344,7 +374,7 @@ void Results::set_property_value(ContextType& ctx, StringData prop_name, ValueTy
         throw InvalidPropertyException(object_schema.name, prop_name);
         throw InvalidPropertyException(object_schema.name, prop_name);
     }
     }
     if (prop->is_primary && !m_realm->is_in_migration()) {
     if (prop->is_primary && !m_realm->is_in_migration()) {
-        throw ModifyPrimaryKeyException(m_object_schema->name, prop->name);
+        throw ModifyPrimaryKeyException(object_schema.name, prop->name);
     }
     }
 
 
     // Update all objects in this ResultSets. Use snapshot to avoid correctness problems if the
     // Update all objects in this ResultSets. Use snapshot to avoid correctness problems if the

+ 16 - 33
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.cpp

@@ -28,29 +28,30 @@
 using namespace realm;
 using namespace realm;
 
 
 namespace realm {
 namespace realm {
-bool operator==(Schema const& a, Schema const& b)
+bool operator==(Schema const& a, Schema const& b) noexcept
 {
 {
     return static_cast<Schema::base const&>(a) == static_cast<Schema::base const&>(b);
     return static_cast<Schema::base const&>(a) == static_cast<Schema::base const&>(b);
 }
 }
 }
 }
 
 
-Schema::Schema() = default;
+Schema::Schema() noexcept = default;
 Schema::~Schema() = default;
 Schema::~Schema() = default;
 Schema::Schema(Schema const&) = default;
 Schema::Schema(Schema const&) = default;
-Schema::Schema(Schema &&) = default;
+Schema::Schema(Schema &&) noexcept = default;
 Schema& Schema::operator=(Schema const&) = default;
 Schema& Schema::operator=(Schema const&) = default;
-Schema& Schema::operator=(Schema&&) = default;
+Schema& Schema::operator=(Schema&&) noexcept = default;
 
 
 Schema::Schema(std::initializer_list<ObjectSchema> types) : Schema(base(types)) { }
 Schema::Schema(std::initializer_list<ObjectSchema> types) : Schema(base(types)) { }
 
 
-Schema::Schema(base types) : base(std::move(types))
+Schema::Schema(base types) noexcept
+: base(std::move(types))
 {
 {
     std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) {
     std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) {
         return lft.name < rgt.name;
         return lft.name < rgt.name;
     });
     });
 }
 }
 
 
-Schema::iterator Schema::find(StringData name)
+Schema::iterator Schema::find(StringData name) noexcept
 {
 {
     auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) {
     auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) {
         return lft.name < rgt;
         return lft.name < rgt;
@@ -61,7 +62,7 @@ Schema::iterator Schema::find(StringData name)
     return it;
     return it;
 }
 }
 
 
-Schema::const_iterator Schema::find(StringData name) const
+Schema::const_iterator Schema::find(StringData name) const noexcept
 {
 {
     return const_cast<Schema *>(this)->find(name);
     return const_cast<Schema *>(this)->find(name);
 }
 }
@@ -101,19 +102,6 @@ void Schema::validate() const
     }
     }
 }
 }
 
 
-namespace {
-struct IsNotRemoveProperty {
-    bool operator()(SchemaChange sc) const { return sc.visit(*this); }
-    bool operator()(schema_change::RemoveProperty) const { return false; }
-    template<typename T> bool operator()(T) const { return true; }
-};
-struct GetRemovedColumn {
-    size_t operator()(SchemaChange sc) const { return sc.visit(*this); }
-    size_t operator()(schema_change::RemoveProperty p) const { return p.property->table_column; }
-    template<typename T> size_t operator()(T) const { REALM_COMPILER_HINT_UNREACHABLE(); }
-};
-}
-
 static void compare(ObjectSchema const& existing_schema,
 static void compare(ObjectSchema const& existing_schema,
                     ObjectSchema const& target_schema,
                     ObjectSchema const& target_schema,
                     std::vector<SchemaChange>& changes)
                     std::vector<SchemaChange>& changes)
@@ -151,25 +139,19 @@ static void compare(ObjectSchema const& existing_schema,
         }
         }
     }
     }
 
 
-    if (existing_schema.primary_key != target_schema.primary_key) {
-        changes.emplace_back(schema_change::ChangePrimaryKey{&existing_schema, target_schema.primary_key_property()});
-    }
-
     for (auto& target_prop : target_schema.persisted_properties) {
     for (auto& target_prop : target_schema.persisted_properties) {
         if (!existing_schema.property_for_name(target_prop.name)) {
         if (!existing_schema.property_for_name(target_prop.name)) {
             changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop});
             changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop});
         }
         }
     }
     }
 
 
-    // Move all RemovePropertys to the end and sort in descending order of
-    // column index, as removing a column will shift all columns after that one
-    auto it = std::partition(begin(changes), end(changes), IsNotRemoveProperty{});
-    std::sort(it, end(changes),
-              [](auto a, auto b) { return GetRemovedColumn()(a) > GetRemovedColumn()(b); });
+    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>
 template<typename T, typename U, typename Func>
-void Schema::zip_matching(T&& a, U&& b, Func&& func)
+void Schema::zip_matching(T&& a, U&& b, Func&& func) noexcept
 {
 {
     size_t i = 0, j = 0;
     size_t i = 0, j = 0;
     while (i < a.size() && j < b.size()) {
     while (i < a.size() && j < b.size()) {
@@ -224,23 +206,24 @@ std::vector<SchemaChange> Schema::compare(Schema const& target_schema, bool incl
     return changes;
     return changes;
 }
 }
 
 
-void Schema::copy_table_columns_from(realm::Schema const& other)
+void Schema::copy_keys_from(realm::Schema const& other) noexcept
 {
 {
     zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) {
     zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) {
         if (!existing || !other)
         if (!existing || !other)
             return;
             return;
 
 
+        existing->table_key = other->table_key;
         for (auto& current_prop : other->persisted_properties) {
         for (auto& current_prop : other->persisted_properties) {
             auto target_prop = existing->property_for_name(current_prop.name);
             auto target_prop = existing->property_for_name(current_prop.name);
             if (target_prop) {
             if (target_prop) {
-                target_prop->table_column = current_prop.table_column;
+                target_prop->column_key = current_prop.column_key;
             }
             }
         }
         }
     });
     });
 }
 }
 
 
 namespace realm {
 namespace realm {
-bool operator==(SchemaChange const& lft, SchemaChange const& rgt)
+bool operator==(SchemaChange const& lft, SchemaChange const& rgt) noexcept
 {
 {
     if (lft.m_kind != rgt.m_kind)
     if (lft.m_kind != rgt.m_kind)
         return false;
         return false;

+ 11 - 11
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/schema.hpp

@@ -34,20 +34,20 @@ class Schema : private std::vector<ObjectSchema> {
 private:
 private:
     using base = std::vector<ObjectSchema>;
     using base = std::vector<ObjectSchema>;
 public:
 public:
-    Schema();
+    Schema() noexcept;
     ~Schema();
     ~Schema();
     // Create a schema from a vector of ObjectSchema
     // Create a schema from a vector of ObjectSchema
-    Schema(base types);
+    Schema(base types) noexcept;
     Schema(std::initializer_list<ObjectSchema> types);
     Schema(std::initializer_list<ObjectSchema> types);
 
 
     Schema(Schema const&);
     Schema(Schema const&);
-    Schema(Schema &&);
+    Schema(Schema&&) noexcept;
     Schema& operator=(Schema const&);
     Schema& operator=(Schema const&);
-    Schema& operator=(Schema&&);
+    Schema& operator=(Schema&&) noexcept;
 
 
     // find an ObjectSchema by name
     // find an ObjectSchema by name
-    iterator find(StringData name);
-    const_iterator find(StringData name) const;
+    iterator find(StringData name) noexcept;
+    const_iterator find(StringData name) const noexcept;
 
 
     // find an ObjectSchema with the same name as the passed in one
     // find an ObjectSchema with the same name as the passed in one
     iterator find(ObjectSchema const& object) noexcept;
     iterator find(ObjectSchema const& object) noexcept;
@@ -60,10 +60,10 @@ public:
     // Get the changes which must be applied to this schema to produce the passed-in schema
     // 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;
     std::vector<SchemaChange> compare(Schema const&, bool include_removals=false) const;
 
 
-    void copy_table_columns_from(Schema const&);
+    void copy_keys_from(Schema const&) noexcept;
 
 
-    friend bool operator==(Schema const&, Schema const&);
-    friend bool operator!=(Schema const& a, Schema const& b) { return !(a == b); }
+    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::iterator;
     using base::const_iterator;
     using base::const_iterator;
@@ -74,7 +74,7 @@ public:
 
 
 private:
 private:
     template<typename T, typename U, typename Func>
     template<typename T, typename U, typename Func>
-    static void zip_matching(T&& a, U&& b, Func&& func);
+    static void zip_matching(T&& a, U&& b, Func&& func) noexcept;
 };
 };
 
 
 namespace schema_change {
 namespace schema_change {
@@ -162,7 +162,7 @@ public:
         REALM_COMPILER_HINT_UNREACHABLE();
         REALM_COMPILER_HINT_UNREACHABLE();
     }
     }
 
 
-    friend bool operator==(SchemaChange const& lft, SchemaChange const& rgt);
+    friend bool operator==(SchemaChange const& lft, SchemaChange const& rgt) noexcept;
 private:
 private:
     enum class Kind {
     enum class Kind {
 #define REALM_SCHEMA_CHANGE_TYPE(name) name,
 #define REALM_SCHEMA_CHANGE_TYPE(name) name,

File diff suppressed because it is too large
+ 249 - 364
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp


+ 80 - 84
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp

@@ -19,11 +19,12 @@
 #ifndef REALM_REALM_HPP
 #ifndef REALM_REALM_HPP
 #define REALM_REALM_HPP
 #define REALM_REALM_HPP
 
 
-#include "execution_context_id.hpp"
 #include "schema.hpp"
 #include "schema.hpp"
 
 
 #include <realm/util/optional.hpp>
 #include <realm/util/optional.hpp>
 #include <realm/binary_data.hpp>
 #include <realm/binary_data.hpp>
+#include <realm/db.hpp>
+#include <realm/version_id.hpp>
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
 #include <realm/sync/client.hpp>
 #include <realm/sync/client.hpp>
@@ -35,23 +36,23 @@ namespace realm {
 class AsyncOpenTask;
 class AsyncOpenTask;
 class AuditInterface;
 class AuditInterface;
 class BindingContext;
 class BindingContext;
+class DB;
 class Group;
 class Group;
+class Obj;
 class Realm;
 class Realm;
 class Replication;
 class Replication;
-class SharedGroup;
 class StringData;
 class StringData;
 class Table;
 class Table;
+class ThreadSafeReference;
+class Transaction;
 struct SyncConfig;
 struct SyncConfig;
-class ThreadSafeReferenceBase;
-template <typename T> class ThreadSafeReference;
-struct VersionID;
-template<typename Table> class BasicRow;
-typedef BasicRow<Table> Row;
-template<typename> class BasicRowExpr;
-using RowExpr = BasicRowExpr<Table>;
 typedef std::shared_ptr<Realm> SharedRealm;
 typedef std::shared_ptr<Realm> SharedRealm;
 typedef std::weak_ptr<Realm> WeakRealm;
 typedef std::weak_ptr<Realm> WeakRealm;
 
 
+namespace util {
+class Scheduler;
+}
+
 namespace _impl {
 namespace _impl {
     class AnyHandover;
     class AnyHandover;
     class CollectionNotifier;
     class CollectionNotifier;
@@ -227,11 +228,6 @@ public:
         // The following are intended for internal/testing purposes and
         // The following are intended for internal/testing purposes and
         // should not be publicly exposed in binding APIs
         // should not be publicly exposed in binding APIs
 
 
-        // If false, always return a new Realm instance, and don't return
-        // that Realm instance for other requests for a cached Realm. Useful
-        // for dynamic Realms and for tests that need multiple instances on
-        // one thread
-        bool cache = true;
         // Throw an exception rather than automatically upgrading the file
         // Throw an exception rather than automatically upgrading the file
         // format. Used by the browser to warn the user that it'll modify
         // format. Used by the browser to warn the user that it'll modify
         // the file.
         // the file.
@@ -242,9 +238,9 @@ public:
         // speeds up tests that don't need notifications.
         // speeds up tests that don't need notifications.
         bool automatic_change_notifications = true;
         bool automatic_change_notifications = true;
 
 
-        // The identifier of the abstract execution context in which this Realm will be used.
-        // If unset, the current thread's identifier will be used to identify the execution context.
-        util::Optional<AbstractExecutionContextID> execution_context;
+        // 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.
         /// A data structure storing data used to configure the Realm for sync support.
         std::shared_ptr<SyncConfig> sync_config;
         std::shared_ptr<SyncConfig> sync_config;
@@ -255,16 +251,18 @@ public:
 
 
         // A factory function which produces an audit implementation.
         // A factory function which produces an audit implementation.
         std::function<std::shared_ptr<AuditInterface>()> audit_factory;
         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();
     };
     };
 
 
-    // Get a cached Realm or create a new one if no cached copies exists
-    // Caching is done by path - mismatches for in_memory, schema mode or
-    // encryption key will raise an exception.
+    // Returns a thread-confined live Realm for the given configuration
     static SharedRealm get_shared_realm(Config config);
     static SharedRealm get_shared_realm(Config config);
 
 
-    // Get a Realm for the given execution context (or current thread if `none`)
-    // from the thread safe reference. May return a cached Realm or create a new one.
-    static SharedRealm get_shared_realm(ThreadSafeReference<Realm>, util::Optional<AbstractExecutionContextID> = util::none);
+    // 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
 #if REALM_ENABLE_SYNC
     // Open a synchronized Realm and make sure it is fully up to date before
     // Open a synchronized Realm and make sure it is fully up to date before
@@ -275,6 +273,8 @@ public:
     // start until you call `AsyncOpenTask::start(callback)`
     // start until you call `AsyncOpenTask::start(callback)`
     static std::shared_ptr<AsyncOpenTask> get_synchronized_realm(Config config);
     static std::shared_ptr<AsyncOpenTask> get_synchronized_realm(Config config);
 #endif
 #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.
     // Updates a Realm to a given schema, using the Realm's pre-set schema mode.
     void update_schema(Schema schema, uint64_t version=0,
     void update_schema(Schema schema, uint64_t version=0,
@@ -304,14 +304,36 @@ public:
     void cancel_transaction();
     void cancel_transaction();
     bool is_in_transaction() const noexcept;
     bool is_in_transaction() const noexcept;
 
 
-    bool is_in_read_transaction() const { return !!m_group; }
+    // 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;
     VersionID read_transaction_version() const;
     Group& read_group();
     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 is_in_migration() const noexcept { return m_in_migration; }
 
 
     bool refresh();
     bool refresh();
-    void set_auto_refresh(bool auto_refresh) { m_auto_refresh = auto_refresh; }
+    void set_auto_refresh(bool auto_refresh);
     bool auto_refresh() const { return m_auto_refresh; }
     bool auto_refresh() const { return m_auto_refresh; }
     void notify();
     void notify();
 
 
@@ -326,13 +348,14 @@ public:
     void verify_thread() const;
     void verify_thread() const;
     void verify_in_write() const;
     void verify_in_write() const;
     void verify_open() const;
     void verify_open() const;
+    bool verify_notifications_available(bool throw_on_error = true) const;
 
 
     bool can_deliver_notifications() const noexcept;
     bool can_deliver_notifications() const noexcept;
+    std::shared_ptr<util::Scheduler> scheduler() const noexcept { return m_scheduler; }
 
 
-    // Close this Realm and remove it from the cache. Continuing to use a
-    // Realm after closing it will throw ClosedRealmException
+    // Close this Realm. Continuing to use a Realm after closing it will throw ClosedRealmException
     void close();
     void close();
-    bool is_closed() const { return !m_read_only_group && !m_shared_group; }
+    bool is_closed() const { return !m_group && !m_coordinator; }
 
 
     // returns the file format version upgraded from if an upgrade took place
     // returns the file format version upgraded from if an upgrade took place
     util::Optional<int> file_format_upgraded_from_version() const;
     util::Optional<int> file_format_upgraded_from_version() const;
@@ -343,27 +366,21 @@ public:
     Realm& operator=(Realm&&) = delete;
     Realm& operator=(Realm&&) = delete;
     ~Realm();
     ~Realm();
 
 
-    // Construct a thread safe reference, pinning the version in the process.
-    template <typename T>
-    ThreadSafeReference<T> obtain_thread_safe_reference(T const& value);
-
-    // Advances the read transaction to the latest version, resolving the thread safe reference and unpinning the
-    // version in the process.
-    template <typename T>
-    T resolve_thread_safe_reference(ThreadSafeReference<T> reference);
-
     ComputedPrivileges get_privileges();
     ComputedPrivileges get_privileges();
     ComputedPrivileges get_privileges(StringData object_type);
     ComputedPrivileges get_privileges(StringData object_type);
-    ComputedPrivileges get_privileges(RowExpr row);
+    ComputedPrivileges get_privileges(ConstObj const& obj);
 
 
     AuditInterface* audit_context() const noexcept;
     AuditInterface* audit_context() const noexcept;
 
 
-    static SharedRealm make_shared_realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator = nullptr) {
-        struct make_shared_enabler : public Realm {
-            make_shared_enabler(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator)
-            : Realm(std::move(config), std::move(coordinator)) { }
-        };
-        return std::make_shared<make_shared_enabler>(std::move(config), std::move(coordinator));
+    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
     // Expose some internal functionality to other parts of the ObjectStore
@@ -372,41 +389,35 @@ public:
         friend class _impl::CollectionNotifier;
         friend class _impl::CollectionNotifier;
         friend class _impl::PartialSyncHelper;
         friend class _impl::PartialSyncHelper;
         friend class _impl::RealmCoordinator;
         friend class _impl::RealmCoordinator;
-        friend class ThreadSafeReferenceBase;
         friend class GlobalNotifier;
         friend class GlobalNotifier;
         friend class TestHelper;
         friend class TestHelper;
+        friend class ThreadSafeReference;
 
 
-        // ResultsNotifier and ListNotifier need access to the SharedGroup
-        // to be able to call the handover functions, which are not very wrappable
-        static const std::unique_ptr<SharedGroup>& get_shared_group(Realm& realm) { return realm.m_shared_group; }
+        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
         // CollectionNotifier needs to be able to access the owning
         // coordinator to wake up the worker thread when a callback is
         // coordinator to wake up the worker thread when a callback is
         // added, and coordinators need to be able to get themselves from a Realm
         // 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 _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);
         static void begin_read(Realm&, VersionID);
     };
     };
 
 
-    static void open_with_config(const Config& config,
-                                 std::unique_ptr<Replication>& history,
-                                 std::unique_ptr<SharedGroup>& shared_group,
-                                 std::unique_ptr<Group>& read_only_group,
-                                 Realm* realm);
-
 private:
 private:
-    // `enable_shared_from_this` is unsafe with public constructors; use `make_shared_realm` instead
-    Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator);
+    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;
     Config m_config;
-    AnyExecutionContextID m_execution_context;
+    util::Optional<VersionID> m_frozen_version;
+    std::shared_ptr<util::Scheduler> m_scheduler;
     bool m_auto_refresh = true;
     bool m_auto_refresh = true;
 
 
-    std::unique_ptr<Replication> m_history;
-    std::unique_ptr<SharedGroup> m_shared_group;
-    std::unique_ptr<Group> m_read_only_group;
-
-    Group *m_group = nullptr;
+    std::shared_ptr<Group> m_group;
 
 
     uint64_t m_schema_version;
     uint64_t m_schema_version;
     Schema m_schema;
     Schema m_schema;
@@ -417,13 +428,6 @@ private:
     // that's actually fully working
     // that's actually fully working
     bool m_dynamic_schema = true;
     bool m_dynamic_schema = true;
 
 
-    std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
-    std::unique_ptr<sync::TableInfoCache> m_table_info_cache;
-    std::unique_ptr<sync::PermissionsCache> m_permissions_cache;
-
-    // File format versions populated when a file format upgrade takes place during realm opening
-    int upgrade_initial_version = 0, upgrade_final_version = 0;
-
     // True while sending the notifications caused by advancing the read
     // True while sending the notifications caused by advancing the read
     // transaction version, to avoid recursive notifications where possible
     // transaction version, to avoid recursive notifications where possible
     bool m_is_sending_notifications = false;
     bool m_is_sending_notifications = false;
@@ -434,6 +438,7 @@ private:
     bool m_in_migration = false;
     bool m_in_migration = false;
 
 
     void begin_read(VersionID);
     void begin_read(VersionID);
+    bool do_refresh();
 
 
     void set_schema(Schema const& reference, Schema schema);
     void set_schema(Schema const& reference, Schema schema);
     bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
     bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
@@ -452,13 +457,15 @@ private:
     bool init_permission_cache();
     bool init_permission_cache();
     void invalidate_permission_cache();
     void invalidate_permission_cache();
 
 
+    Transaction& transaction();
+    Transaction& transaction() const;
+    std::shared_ptr<Transaction> transaction_ref();
+
 public:
 public:
     std::unique_ptr<BindingContext> m_binding_context;
     std::unique_ptr<BindingContext> m_binding_context;
 
 
-    // FIXME: This is currently needed by the adapter to get access to its changeset cooker
-    Replication* history() { return m_history.get(); }
-
-    friend class _impl::RealmFriend;
+    // `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 {
 class RealmFileException : public std::runtime_error {
@@ -481,9 +488,6 @@ public:
         IncompatibleLockFile,
         IncompatibleLockFile,
         /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
         /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
         FormatUpgradeRequired,
         FormatUpgradeRequired,
-        /** Thrown if the local copy of a synced Realm file was created using an incompatible version of Realm.
-         The specified path is where the local file was moved for recovery. */
-        IncompatibleSyncedRealm,
     };
     };
     RealmFileException(Kind kind, std::string path, std::string message, std::string underlying)
     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)) {}
     : std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {}
@@ -531,14 +535,6 @@ class InvalidEncryptionKeyException : public std::logic_error {
 public:
 public:
     InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
     InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
 };
 };
-
-// FIXME Those are exposed for Java async queries, mainly because of handover related methods.
-class _impl::RealmFriend {
-public:
-    static SharedGroup& get_shared_group(Realm& realm);
-    static Group& read_group_to(Realm& realm, VersionID version);
-};
-
 } // namespace realm
 } // namespace realm
 
 
 #endif /* defined(REALM_REALM_HPP) */
 #endif /* defined(REALM_REALM_HPP) */

+ 208 - 78
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.cpp

@@ -18,106 +18,236 @@
 
 
 #include "thread_safe_reference.hpp"
 #include "thread_safe_reference.hpp"
 
 
-#include "impl/realm_coordinator.hpp"
 #include "list.hpp"
 #include "list.hpp"
 #include "object.hpp"
 #include "object.hpp"
 #include "object_schema.hpp"
 #include "object_schema.hpp"
 #include "results.hpp"
 #include "results.hpp"
+#include "shared_realm.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_created_in_write_transaction(realm.is_in_transaction())
+    {
+    }
+
+    void refresh_target_realm(Realm&);
 
 
-#include <realm/util/scope_exit.hpp>
+protected:
+    const TransactionRef m_transaction;
 
 
-using namespace realm;
+private:
+    const VersionID m_target_version;
+    const bool m_created_in_write_transaction;
+};
 
 
-ThreadSafeReferenceBase::ThreadSafeReferenceBase(SharedRealm source_realm) : m_source_realm(std::move(source_realm))
+void ThreadSafeReference::Payload::refresh_target_realm(Realm& realm)
 {
 {
-    m_source_realm->verify_thread();
-    if (m_source_realm->is_in_transaction()) {
-        throw InvalidTransactionException("Cannot obtain thread safe reference during a write transaction.");
+    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());
     }
     }
-
-    try {
-        m_version_id = get_source_shared_group().pin_version();
-    } catch (...) {
-        invalidate();
-        throw;
+    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();
     }
     }
 }
 }
 
 
-ThreadSafeReferenceBase::~ThreadSafeReferenceBase()
-{
-    if (!is_invalidated())
-        invalidate();
-}
+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())
+    {
+    }
 
 
-template <typename V, typename T>
-V ThreadSafeReferenceBase::invalidate_after_import(Realm& destination_realm, T construct_with_shared_group) {
-    destination_realm.verify_thread();
-    REALM_ASSERT_DEBUG(!m_source_realm->is_in_transaction());
-    REALM_ASSERT_DEBUG(!is_invalidated());
+    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);
+    }
 
 
-    SharedGroup& destination_shared_group = *Realm::Internal::get_shared_group(destination_realm);
-    auto unpin_version = util::make_scope_exit([&]() noexcept { invalidate(); });
+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)
+    {
+    }
 
 
-    return construct_with_shared_group(destination_shared_group);
-}
+    Object import_into(std::shared_ptr<Realm> const& r)
+    {
+        return Object(r, m_object_schema_name, m_key);
+    }
 
 
-SharedGroup& ThreadSafeReferenceBase::get_source_shared_group() const {
-    return *Realm::Internal::get_shared_group(*m_source_realm);
-}
+private:
+    ObjKey m_key;
+    std::string m_object_schema_name;
+};
+
+template<typename T>
+struct ListType {
+    using type = Lst<std::remove_reference_t<T>>;
+};
+
+// The code path which would instantiate List<Obj> isn't reachable, but still
+// produces errors about the type not being instantiable so we instead map it
+// to an arbitrary valid type
+template<>
+struct ListType<Obj&> {
+    using type = Lst<int64_t>;
+};
+
+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);
+        }
+    }
 
 
-bool ThreadSafeReferenceBase::has_same_config(Realm& realm) const {
-    return &Realm::Internal::get_coordinator(*m_source_realm) == &Realm::Internal::get_coordinator(realm);
-}
+    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<typename ListType<decltype(*t)>::type>();
+                });
+            }
+            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)
+    {
+    }
 
 
-void ThreadSafeReferenceBase::invalidate() {
-    REALM_ASSERT_DEBUG(m_source_realm);
-    SharedRealm thread_local_realm = Realm::Internal::get_coordinator(*m_source_realm).get_realm();
-    Realm::Internal::get_shared_group(*thread_local_realm)->unpin_version(m_version_id);
-    m_source_realm = nullptr;
+    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));
 }
 }
 
 
-ThreadSafeReference<List>::ThreadSafeReference(List const& list)
-: ThreadSafeReferenceBase(list.get_realm())
-, m_link_view(get_source_shared_group().export_linkview_for_handover(list.m_link_view))
-, m_table(get_source_shared_group().export_table_for_handover(list.m_table))
-{ }
-
-List ThreadSafeReference<List>::import_into_realm(SharedRealm realm) && {
-    return invalidate_after_import<List>(*realm, [&](SharedGroup& shared_group) {
-        if (auto link_view = shared_group.import_linkview_from_handover(std::move(m_link_view)))
-            return List(std::move(realm), std::move(link_view));
-        return List(std::move(realm), shared_group.import_table_from_handover(std::move(m_table)));
-    });
+template<>
+ThreadSafeReference::ThreadSafeReference(std::shared_ptr<Realm> const& value)
+{
+    m_payload.reset(new PayloadImpl<std::shared_ptr<Realm>>(value));
 }
 }
 
 
-ThreadSafeReference<Object>::ThreadSafeReference(Object const& object)
-: ThreadSafeReferenceBase(object.realm())
-, m_row(get_source_shared_group().export_for_handover(Row(object.row())))
-, m_object_schema_name(object.get_object_schema().name) { }
-
-Object ThreadSafeReference<Object>::import_into_realm(SharedRealm realm) && {
-    return invalidate_after_import<Object>(*realm, [&](SharedGroup& shared_group) {
-        Row row = *shared_group.import_from_handover(std::move(m_row));
-        auto object_schema = realm->schema().find(m_object_schema_name);
-        REALM_ASSERT_DEBUG(object_schema != realm->schema().end());
-        return Object(std::move(realm), *object_schema, row);
-    });
+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 {};
+    }
 }
 }
 
 
-ThreadSafeReference<Results>::ThreadSafeReference(Results const& results)
-: ThreadSafeReferenceBase(results.get_realm())
-, m_query(get_source_shared_group().export_for_handover(results.get_query(), ConstSourcePayload::Copy))
-, m_ordering_patch([&]() {
-    DescriptorOrdering::HandoverPatch ordering_patch;
-    DescriptorOrdering::generate_patch(results.get_descriptor_ordering(), ordering_patch);
-    return ordering_patch;
-}()){ }
-
-Results ThreadSafeReference<Results>::import_into_realm(SharedRealm realm) && {
-    return invalidate_after_import<Results>(*realm, [&](SharedGroup& shared_group) {
-        Query query = *shared_group.import_from_handover(std::move(m_query));
-        Table& table = *query.get_table();
-        DescriptorOrdering descriptors = DescriptorOrdering::create_from_and_consume_patch(m_ordering_patch, table);
-        return Results(std::move(realm), std::move(query), std::move(descriptors));
-    });
+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

+ 21 - 96
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/thread_safe_reference.hpp

@@ -16,116 +16,41 @@
 //
 //
 ////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////
 
 
-#ifndef REALM_THREAD_SAFE_REFERENCE_HPP
-#define REALM_THREAD_SAFE_REFERENCE_HPP
+#ifndef REALM_OS_THREAD_SAFE_REFERENCE_HPP
+#define REALM_OS_THREAD_SAFE_REFERENCE_HPP
 
 
-#include <realm/group_shared.hpp>
+#include <memory>
 
 
 namespace realm {
 namespace realm {
-class LinkView;
 class List;
 class List;
 class Object;
 class Object;
-class Query;
 class Realm;
 class Realm;
 class Results;
 class Results;
-class TableView;
-template<typename T> class BasicRow;
-typedef BasicRow<Table> Row;
-namespace _impl { class RealmCoordinator; }
 
 
-// Opaque type representing an object for handover
-class ThreadSafeReferenceBase {
+// Opaque type-ereased wrapper for a Realm object which can be imported into another Realm
+class ThreadSafeReference {
 public:
 public:
-    ThreadSafeReferenceBase(const ThreadSafeReferenceBase&) = delete;
-    ThreadSafeReferenceBase& operator=(const ThreadSafeReferenceBase&) = delete;
-    ThreadSafeReferenceBase(ThreadSafeReferenceBase&&) = default;
-    ThreadSafeReferenceBase& operator=(ThreadSafeReferenceBase&&) = default;
-    ThreadSafeReferenceBase();
-    virtual ~ThreadSafeReferenceBase();
+    ThreadSafeReference() noexcept;
+    ~ThreadSafeReference();
+    ThreadSafeReference(const ThreadSafeReference&) = delete;
+    ThreadSafeReference& operator=(const ThreadSafeReference&) = delete;
+    ThreadSafeReference(ThreadSafeReference&&) noexcept;
+    ThreadSafeReference& operator=(ThreadSafeReference&&) noexcept;
 
 
-    bool is_invalidated() const { return m_source_realm == nullptr; };
+    template<typename T>
+    ThreadSafeReference(T const& value);
 
 
-protected:
-    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
-    ThreadSafeReferenceBase(std::shared_ptr<Realm> source_realm);
+    // Import the object into the destination Realm
+    template<typename T>
+    T resolve(std::shared_ptr<Realm> const&);
 
 
-    SharedGroup& get_source_shared_group() const;
-
-    template <typename V, typename T>
-    V invalidate_after_import(Realm& destination_realm, T construct_with_shared_group);
+    explicit operator bool() const noexcept { return !!m_payload; }
 
 
 private:
 private:
-    friend Realm;
-
-    VersionID m_version_id;
-    std::shared_ptr<Realm> m_source_realm; // Strong reference keeps alive so version stays pinned! Don't touch!!
-
-    bool has_same_config(Realm& realm) const;
-    void invalidate();
-};
-
-template <typename T>
-class ThreadSafeReference;
-
-template<>
-class ThreadSafeReference<List>: public ThreadSafeReferenceBase {
-    friend class Realm;
-
-    std::unique_ptr<SharedGroup::Handover<LinkView>> m_link_view;
-    std::unique_ptr<SharedGroup::Handover<Table>> m_table;
-
-    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
-    ThreadSafeReference(List const& value);
-
-    // Precondition: Realm and handover are on same version.
-    List import_into_realm(std::shared_ptr<Realm> realm) &&;
-};
-
-template<>
-class ThreadSafeReference<Object>: public ThreadSafeReferenceBase {
-    friend class Realm;
-
-    std::unique_ptr<SharedGroup::Handover<Row>> m_row;
-    std::string m_object_schema_name;
-
-    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
-    ThreadSafeReference(Object const& value);
-
-    // Precondition: Realm and handover are on same version.
-    Object import_into_realm(std::shared_ptr<Realm> realm) &&;
-};
-
-template<>
-class ThreadSafeReference<Results>: public ThreadSafeReferenceBase {
-    friend class Realm;
-
-    std::unique_ptr<SharedGroup::Handover<Query>> m_query;
-    DescriptorOrdering::HandoverPatch m_ordering_patch;
-
-    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
-    ThreadSafeReference(Results const& value);
-
-    // Precondition: Realm and handover are on same version.
-    Results import_into_realm(std::shared_ptr<Realm> realm) &&;
-};
-
-template<>
-class ThreadSafeReference<Realm> {
-    friend class Realm;
-    friend class _impl::RealmCoordinator;
-
-    std::shared_ptr<Realm> m_realm;
-
-    ThreadSafeReference(std::shared_ptr<Realm>);
-    std::shared_ptr<Realm> resolve() &&;
-public:
-
-    ThreadSafeReference() = default;
-    ThreadSafeReference(ThreadSafeReference const&) = delete;
-    ThreadSafeReference(ThreadSafeReference &&) = default;
-    ThreadSafeReference& operator=(ThreadSafeReference const&) = delete;
-    ThreadSafeReference& operator=(ThreadSafeReference&&) = default;
+    class Payload;
+    template<typename> class PayloadImpl;
+    std::unique_ptr<Payload> m_payload;
 };
 };
 }
 }
 
 
-#endif /* REALM_THREAD_SAFE_REFERENCE_HPP */
+#endif /* REALM_OS_THREAD_SAFE_REFERENCE_HPP */

+ 7 - 7
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/util/tagged_bool.hpp

@@ -36,20 +36,20 @@ namespace util {
 template <typename Tag>
 template <typename Tag>
 struct TaggedBool {
 struct TaggedBool {
     // Allow explicit construction from anything convertible to bool
     // Allow explicit construction from anything convertible to bool
-    constexpr explicit TaggedBool(bool v) : m_value(v) { }
+    constexpr explicit TaggedBool(bool v) noexcept : m_value(v) { }
 
 
     // Allow implicit construction from *just* bool and not things convertible
     // Allow implicit construction from *just* bool and not things convertible
     // to bool (such as other types of tagged bools)
     // to bool (such as other types of tagged bools)
     template <typename Bool, typename = typename std::enable_if<std::is_same<Bool, bool>::value>::type>
     template <typename Bool, typename = typename std::enable_if<std::is_same<Bool, bool>::value>::type>
-    constexpr TaggedBool(Bool v) : m_value(v) {}
+    constexpr TaggedBool(Bool v) noexcept : m_value(v) {}
 
 
-    constexpr TaggedBool(TaggedBool const& v) : m_value(v.m_value) {}
+    constexpr TaggedBool(TaggedBool const& v) noexcept : m_value(v.m_value) {}
 
 
-    constexpr operator bool() const { return m_value; }
-    constexpr TaggedBool operator!() const { return TaggedBool{!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) { return l.m_value == r.m_value; }
-    friend constexpr bool operator!=(TaggedBool l, TaggedBool r) { return l.m_value != r.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:
 private:
     bool m_value;
     bool m_value;

+ 7 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt

@@ -9,6 +9,7 @@ set(HEADERS
 
 
 set(SOURCES
 set(SOURCES
     collection_change_indices.cpp
     collection_change_indices.cpp
+    frozen_objects.cpp
     index_set.cpp
     index_set.cpp
     list.cpp
     list.cpp
     main.cpp
     main.cpp
@@ -49,6 +50,12 @@ if(REALM_ENABLE_SYNC)
     )
     )
 endif()
 endif()
 
 
+if(REALM_ENABLE_SERVER)
+    list(APPEND SOURCES
+        sync/global_notifier.cpp
+    )
+endif()
+
 add_executable(tests ${SOURCES} ${HEADERS})
 add_executable(tests ${SOURCES} ${HEADERS})
 target_compile_definitions(tests PRIVATE ${PLATFORM_DEFINES})
 target_compile_definitions(tests PRIVATE ${PLATFORM_DEFINES})
 
 

+ 26 - 298
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/collection_change_indices.cpp

@@ -59,16 +59,6 @@ TEST_CASE("collection_change: insert()") {
         c.insert(4);
         c.insert(4);
         REQUIRE_MOVES(c, {10, 6}, {10, 2}, {3, 11});
         REQUIRE_MOVES(c, {10, 6}, {10, 2}, {3, 11});
     }
     }
-
-    SECTION("shifts destination of previous row moves after the insertion point") {
-        c.move_over(10, 30);
-        c.move_over(15, 29);
-        c.move_over(16, 28);
-        c.move_over(20, 27);
-        c.insert(11, 3);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {27, 23}, {28, 19}, {29, 18}, {30, 10});
-    }
 }
 }
 
 
 TEST_CASE("collection_change: modify()") {
 TEST_CASE("collection_change: modify()") {
@@ -99,22 +89,17 @@ TEST_CASE("collection_change: modify()") {
     SECTION("marks the appropriate column as modified when applicable") {
     SECTION("marks the appropriate column as modified when applicable") {
         c.modify(5, 2);
         c.modify(5, 2);
         REQUIRE_INDICES(c.modifications, 5);
         REQUIRE_INDICES(c.modifications, 5);
-        REQUIRE(c.columns.size() > 2);
-        REQUIRE(c.columns[0].empty());
-        REQUIRE(c.columns[1].empty());
+        REQUIRE(c.columns.size() == 1);
         REQUIRE_INDICES(c.columns[2], 5);
         REQUIRE_INDICES(c.columns[2], 5);
 
 
         c.modify(4, 2);
         c.modify(4, 2);
         REQUIRE_INDICES(c.modifications, 4, 5);
         REQUIRE_INDICES(c.modifications, 4, 5);
-        REQUIRE(c.columns.size() > 2);
-        REQUIRE(c.columns[0].empty());
-        REQUIRE(c.columns[1].empty());
+        REQUIRE(c.columns.size() == 1);
         REQUIRE_INDICES(c.columns[2], 4, 5);
         REQUIRE_INDICES(c.columns[2], 4, 5);
 
 
         c.modify(3, 1);
         c.modify(3, 1);
         REQUIRE_INDICES(c.modifications, 3, 4, 5);
         REQUIRE_INDICES(c.modifications, 3, 4, 5);
-        REQUIRE(c.columns.size() > 2);
-        REQUIRE(c.columns[0].empty());
+        REQUIRE(c.columns.size() == 2);
         REQUIRE_INDICES(c.columns[1], 3);
         REQUIRE_INDICES(c.columns[1], 3);
         REQUIRE_INDICES(c.columns[2], 4, 5);
         REQUIRE_INDICES(c.columns[2], 4, 5);
     }
     }
@@ -176,111 +161,6 @@ TEST_CASE("collection_change: erase()") {
     }
     }
 }
 }
 
 
-TEST_CASE("collection_change: move_over()") {
-    _impl::CollectionChangeBuilder c;
-
-    SECTION("is just erase when row == last_row") {
-        c.move_over(10, 10);
-        c.parse_complete();
-
-        REQUIRE_INDICES(c.deletions, 10);
-        REQUIRE(c.insertions.empty());
-        REQUIRE(c.moves.empty());
-    }
-
-    SECTION("is just erase when row + 1 == last_row") {
-        c.move_over(0, 6);
-        c.move_over(4, 5);
-        c.move_over(0, 4);
-        c.move_over(2, 3);
-        c.parse_complete();
-        c.clean_up_stale_moves();
-
-        REQUIRE_INDICES(c.deletions, 0, 2, 4, 5, 6);
-        REQUIRE_INDICES(c.insertions, 0);
-        REQUIRE_MOVES(c, {5, 0});
-    }
-
-    SECTION("marks the old last row as moved") {
-        c.move_over(5, 8);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {8, 5});
-    }
-
-    SECTION("does not mark the old last row as moved if it was newly inserted") {
-        c.insert(8);
-        c.move_over(5, 8);
-        c.parse_complete();
-        REQUIRE(c.moves.empty());
-    }
-
-    SECTION("removes previous modifications for the removed row") {
-        c.modify(5, 0);
-        c.move_over(5, 8);
-        c.parse_complete();
-        REQUIRE(c.modifications.empty());
-        REQUIRE(c.columns[0].empty());
-    }
-
-    SECTION("updates previous insertions for the old last row") {
-        c.insert(5);
-        c.move_over(3, 5);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 3);
-    }
-
-    SECTION("updates previous modifications for the old last row") {
-        c.modify(5, 0);
-        c.move_over(3, 5);
-        c.parse_complete();
-        REQUIRE_INDICES(c.modifications, 3);
-        REQUIRE_COLUMN_INDICES(c.columns, 0, 3);
-    }
-
-    SECTION("removes moves to the target") {
-        c.move_over(5, 10);
-        c.move_over(5, 8);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {8, 5});
-    }
-
-    SECTION("updates moves to the source") {
-        c.move_over(8, 10);
-        c.move_over(5, 8);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {10, 5});
-    }
-
-    SECTION("removes moves to the row when row == last_row") {
-        c.move_over(0, 1);
-        c.move_over(0, 0);
-        c.parse_complete();
-
-        REQUIRE_INDICES(c.deletions, 0, 1);
-        REQUIRE(c.insertions.empty());
-        REQUIRE(c.moves.empty());
-    }
-
-    SECTION("is not shifted by previous calls to move_over()") {
-        c.move_over(5, 10);
-        c.move_over(6, 9);
-        c.parse_complete();
-        REQUIRE_INDICES(c.deletions, 5, 6, 9, 10);
-        REQUIRE_INDICES(c.insertions, 5, 6);
-        REQUIRE_MOVES(c, {9, 6}, {10, 5});
-    }
-
-    SECTION("marks the moved-over row as deleted when chaining moves") {
-        c.move_over(5, 10);
-        c.move_over(0, 5);
-        c.parse_complete();
-
-        REQUIRE_INDICES(c.deletions, 0, 5, 10);
-        REQUIRE_INDICES(c.insertions, 0);
-        REQUIRE_MOVES(c, {10, 0});
-    }
-}
-
 TEST_CASE("collection_change: clear()") {
 TEST_CASE("collection_change: clear()") {
     _impl::CollectionChangeBuilder c;
     _impl::CollectionChangeBuilder c;
 
 
@@ -311,15 +191,6 @@ TEST_CASE("collection_change: clear()") {
         c.clear(5);
         c.clear(5);
         REQUIRE_INDICES(c.deletions, 0, 1, 2, 3, 4, 5, 6, 7);
         REQUIRE_INDICES(c.deletions, 0, 1, 2, 3, 4, 5, 6, 7);
     }
     }
-
-    SECTION("sets deletions SIZE_T_MAX if that if the given previous size") {
-        c.insertions = {1, 2, 3};
-        c.clear(std::numeric_limits<size_t>::max());
-        REQUIRE(!c.deletions.empty());
-        REQUIRE(++c.deletions.begin() == c.deletions.end());
-        REQUIRE(c.deletions.begin()->first == 0);
-        REQUIRE(c.deletions.begin()->second == std::numeric_limits<size_t>::max());
-    }
 }
 }
 
 
 TEST_CASE("collection_change: move()") {
 TEST_CASE("collection_change: move()") {
@@ -393,220 +264,80 @@ TEST_CASE("collection_change: move()") {
     }
     }
 }
 }
 
 
-TEST_CASE("collection_change: swap()") {
-    _impl::CollectionChangeBuilder c;
-
-    SECTION("marks both as inserted and deleted") {
-        c.swap(6, 10);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 6, 10);
-        REQUIRE_INDICES(c.deletions, 6, 10);
-    }
-
-    SECTION("does not mark new insertions as deleted") {
-        c.insert(10);
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 5, 10);
-        REQUIRE_INDICES(c.deletions, 5);
-    }
-
-    SECTION("adds a pair of moves to represent the swap") {
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {5, 10}, {10, 5});
-    }
-
-    SECTION("collapses away when swapped twice") {
-        c.swap(5, 10);
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE(c.empty());
-    }
-
-    SECTION("updates previous moves to one of the swapped rows") {
-        c.move_over(5, 8);
-        c.swap(3, 5);
-        c.parse_complete();
-        REQUIRE_MOVES(c, {3, 5}, {8, 3});
-    }
-
-    SECTION("does not report a move for newly inserted rows") {
-        c.insert(5);
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 5, 10);
-        REQUIRE_MOVES(c, {9, 5});
-
-        c = {};
-        c.insert(10);
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 5, 10);
-        REQUIRE_MOVES(c, {5, 10});
-
-        c = {};
-        c.insert(5);
-        c.insert(10);
-        c.swap(5, 10);
-        c.parse_complete();
-        REQUIRE_INDICES(c.insertions, 5, 10);
-        REQUIRE(c.moves.empty());
-    }
-
-    SECTION("updates previous modifications") {
-        c.modify(6, 0);
-
-        c.swap(6, 10);
-        REQUIRE_INDICES(c.modifications, 10);
-        REQUIRE_COLUMN_INDICES(c.columns, 0, 10);
-
-        c.swap(10, 6);
-        REQUIRE_INDICES(c.modifications, 6);
-        REQUIRE_COLUMN_INDICES(c.columns, 0, 6);
-    }
-}
-
-TEST_CASE("collection_change: subsume()") {
-    _impl::CollectionChangeBuilder c;
-
-    auto subsume = [&](size_t ndx1, size_t ndx2) {
-        c.insert(ndx2);
-        c.insert(ndx2 + 1);
-        c.subsume(ndx1, ndx2);
-        c.move_over(ndx1, ndx2 + 1);
-        c.parse_complete();
-    };
-
-    SECTION("adds a move from the old to the new row") {
-        subsume(5, 10);
-        REQUIRE_MOVES(c, {5, 10});
-    }
-
-    SECTION("updates moves to the old row") {
-        c.move_over(5, 10);
-        c.insert(10);
-        c.insert(11);
-        c.insert(12);
-        c.subsume(5, 11);
-        c.move_over(5, 12);
-        c.parse_complete();
-
-        REQUIRE_MOVES(c, {10, 11});
-        REQUIRE_INDICES(c.insertions, 5, 10, 11);
-    }
-
-    SECTION("collapses away to nothing when the subsuming row is the last one") {
-        c.insert(10);
-        c.subsume(5, 10);
-        c.move_over(5, 10);
-        c.parse_complete();
-        REQUIRE(c.empty());
-    }
-
-    SECTION("marks the new as modified if the old was") {
-        subsume(5, 10);
-        REQUIRE(c.modifications.empty());
-
-        c.modify(3, 0);
-        subsume(3, 15);
-        REQUIRE_INDICES(c.modifications, 15);
-        REQUIRE_COLUMN_INDICES(c.columns, 0, 15);
-    }
-
-    SECTION("leaves the new row marked as modified if it already was") {
-        c.insert(6);
-        c.insert(7);
-        c.modify(6, 0);
-
-        c.subsume(5, 6);
-        c.move_over(5, 7);
-        REQUIRE_INDICES(c.modifications, 6);
-        REQUIRE_COLUMN_INDICES(c.columns, 0, 6);
-    }
-}
-
-TEST_CASE("collection_change: calculate() unsorted") {
+TEST_CASE("collection_change: calculate() table order") {
     _impl::CollectionChangeBuilder c;
     _impl::CollectionChangeBuilder c;
 
 
     auto all_modified = [](size_t) { return true; };
     auto all_modified = [](size_t) { return true; };
     auto none_modified = [](size_t) { return false; };
     auto none_modified = [](size_t) { return false; };
-    const auto npos = size_t(-1);
-    IndexSet empty;
+    bool in_table_order = true;
 
 
     SECTION("returns an empty set when input and output are identical") {
     SECTION("returns an empty set when input and output are identical") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, in_table_order);
         REQUIRE(c.empty());
         REQUIRE(c.empty());
     }
     }
 
 
     SECTION("marks all as inserted when prev is empty") {
     SECTION("marks all as inserted when prev is empty") {
-        c = _impl::CollectionChangeBuilder::calculate({}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.insertions, 0, 1, 2);
         REQUIRE_INDICES(c.insertions, 0, 1, 2);
     }
     }
 
 
     SECTION("marks all as deleted when new is empty") {
     SECTION("marks all as deleted when new is empty") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 0, 1, 2);
         REQUIRE_INDICES(c.deletions, 0, 1, 2);
     }
     }
 
 
     SECTION("marks npos rows in prev as deleted") {
     SECTION("marks npos rows in prev as deleted") {
-        c = _impl::CollectionChangeBuilder::calculate({npos, 1, 2, 3, npos}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({-1, 1, 2, 3, -1}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 0, 4);
         REQUIRE_INDICES(c.deletions, 0, 4);
     }
     }
 
 
     SECTION("marks modified rows which do not move as modified") {
     SECTION("marks modified rows which do not move as modified") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.modifications, 0, 1, 2);
         REQUIRE_INDICES(c.modifications, 0, 1, 2);
     }
     }
 
 
     SECTION("does not mark unmodified rows as modified") {
     SECTION("does not mark unmodified rows as modified") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified, in_table_order);
         REQUIRE(c.modifications.empty());
         REQUIRE(c.modifications.empty());
     }
     }
 
 
     SECTION("marks newly added rows as insertions") {
     SECTION("marks newly added rows as insertions") {
-        c = _impl::CollectionChangeBuilder::calculate({2, 3}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({2, 3}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.insertions, 0);
         REQUIRE_INDICES(c.insertions, 0);
 
 
-        c = _impl::CollectionChangeBuilder::calculate({1, 3}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 3}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.insertions, 1);
         REQUIRE_INDICES(c.insertions, 1);
 
 
-        c = _impl::CollectionChangeBuilder::calculate({1, 2}, {1, 2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2}, {1, 2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.insertions, 2);
         REQUIRE_INDICES(c.insertions, 2);
     }
     }
 
 
     SECTION("marks removed rows as deleted") {
     SECTION("marks removed rows as deleted") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 2);
         REQUIRE_INDICES(c.deletions, 2);
 
 
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 1);
         REQUIRE_INDICES(c.deletions, 1);
 
 
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {2, 3}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {2, 3}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 0);
         REQUIRE_INDICES(c.deletions, 0);
     }
     }
 
 
     SECTION("marks rows as both inserted and deleted") {
     SECTION("marks rows as both inserted and deleted") {
-        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 4}, all_modified, empty);
+        c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 3, 4}, all_modified, in_table_order);
         REQUIRE_INDICES(c.deletions, 1);
         REQUIRE_INDICES(c.deletions, 1);
         REQUIRE_INDICES(c.insertions, 2);
         REQUIRE_INDICES(c.insertions, 2);
         REQUIRE(c.moves.empty());
         REQUIRE(c.moves.empty());
     }
     }
 
 
-    IndexSet all = {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 10};
-    SECTION("marks rows as modified even if they moved") {
-        c = _impl::CollectionChangeBuilder::calculate({5, 3}, {3, 5}, all_modified, all);
-        REQUIRE_MOVES(c, {1, 0});
-        REQUIRE_INDICES(c.modifications, 0, 1);
-    }
-
     SECTION("does not mark rows as modified if they are new") {
     SECTION("does not mark rows as modified if they are new") {
-        c = _impl::CollectionChangeBuilder::calculate({3}, {3, 5}, all_modified, all);
+        c = _impl::CollectionChangeBuilder::calculate({3}, {3, 5}, all_modified, in_table_order);
         REQUIRE_INDICES(c.modifications, 0);
         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()") {
     SECTION("reports moves which can be produced by move_last_over()") {
-        auto calc = [&](std::vector<size_t> values) {
+        auto calc = [&](std::vector<int64_t> values) {
             return _impl::CollectionChangeBuilder::calculate(values, {1, 2, 3}, none_modified, all);
             return _impl::CollectionChangeBuilder::calculate(values, {1, 2, 3}, none_modified, all);
         };
         };
 
 
@@ -624,6 +355,7 @@ TEST_CASE("collection_change: calculate() unsorted") {
         c = _impl::CollectionChangeBuilder::calculate({3, 1, 2}, {1, 2, 3}, none_modified, IndexSet{3});
         c = _impl::CollectionChangeBuilder::calculate({3, 1, 2}, {1, 2, 3}, none_modified, IndexSet{3});
         REQUIRE_MOVES(c, {0, 2});
         REQUIRE_MOVES(c, {0, 2});
     }
     }
+#endif
 }
 }
 
 
 TEST_CASE("collection_change: calculate() sorted") {
 TEST_CASE("collection_change: calculate() sorted") {
@@ -631,7 +363,7 @@ TEST_CASE("collection_change: calculate() sorted") {
 
 
     auto all_modified = [](size_t) { return true; };
     auto all_modified = [](size_t) { return true; };
     auto none_modified = [](size_t) { return false; };
     auto none_modified = [](size_t) { return false; };
-    const auto npos = size_t(-1);
+    size_t npos = -1;
 
 
     SECTION("returns an empty set when input and output are identical") {
     SECTION("returns an empty set when input and output are identical") {
         c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified);
         c = _impl::CollectionChangeBuilder::calculate({1, 2, 3}, {1, 2, 3}, none_modified);
@@ -940,7 +672,7 @@ TEST_CASE("collection_change: merge()") {
 
 
     SECTION("is a no-op if the new set is empty") {
     SECTION("is a no-op if the new set is empty") {
         c = {{1, 2, 3}, {4, 5}, {6, 7}, {{8, 9}}};
         c = {{1, 2, 3}, {4, 5}, {6, 7}, {{8, 9}}};
-        c.columns = {{6}, {7}};
+        c.columns = {{0, {6}}, {1, {7}}};
         c.merge({});
         c.merge({});
         REQUIRE_INDICES(c.deletions, 1, 2, 3, 8);
         REQUIRE_INDICES(c.deletions, 1, 2, 3, 8);
         REQUIRE_INDICES(c.insertions, 4, 5, 9);
         REQUIRE_INDICES(c.insertions, 4, 5, 9);
@@ -1194,7 +926,7 @@ TEST_CASE("collection_change: merge()") {
     SECTION("column-level modifications") {
     SECTION("column-level modifications") {
         _impl::CollectionChangeBuilder c2;
         _impl::CollectionChangeBuilder c2;
         c = {{}, {}, {1, 2, 3}, {}};
         c = {{}, {}, {1, 2, 3}, {}};
-        c.columns = {{1}, {2, 3}};
+        c.columns = {{0, {1}}, {1, {2, 3}}};
 
 
         SECTION("preserved on no-op merges") {
         SECTION("preserved on no-op merges") {
             c.merge({});
             c.merge({});
@@ -1208,7 +940,7 @@ TEST_CASE("collection_change: merge()") {
 
 
         SECTION("merged with other column-level modifications") {
         SECTION("merged with other column-level modifications") {
             c2.modifications = {0, 4};
             c2.modifications = {0, 4};
-            c2.columns = {{1, 2}, {}, {4}};
+            c2.columns = {{0, {1, 2}}, {2, {4}}};
             c.merge(std::move(c2));
             c.merge(std::move(c2));
 
 
             REQUIRE_COLUMN_INDICES(c.columns, 0, 1, 2);
             REQUIRE_COLUMN_INDICES(c.columns, 0, 1, 2);
@@ -1229,9 +961,5 @@ TEST_CASE("collection_change: merge()") {
             REQUIRE_COLUMN_INDICES(c.columns, 0, 1);
             REQUIRE_COLUMN_INDICES(c.columns, 0, 1);
             REQUIRE_COLUMN_INDICES(c.columns, 1, 2, 4);
             REQUIRE_COLUMN_INDICES(c.columns, 1, 2, 4);
         }
         }
-
-        SECTION("updated by moves") {
-
-        }
     }
     }
 }
 }

+ 151 - 183
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/list.cpp

@@ -32,9 +32,8 @@
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "impl/object_accessor_impl.hpp"
 #include "impl/object_accessor_impl.hpp"
 
 
-#include <realm/group_shared.hpp>
-#include <realm/link_view.hpp>
 #include <realm/version.hpp>
 #include <realm/version.hpp>
+#include <realm/db.hpp>
 
 
 #include <cstdint>
 #include <cstdint>
 
 
@@ -43,7 +42,6 @@ using namespace realm;
 TEST_CASE("list") {
 TEST_CASE("list") {
     InMemoryTestFile config;
     InMemoryTestFile config;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
-    config.cache = false;
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
     r->update_schema({
     r->update_schema({
         {"origin", {
         {"origin", {
@@ -61,45 +59,50 @@ TEST_CASE("list") {
         }},
         }},
     });
     });
 
 
-    auto& coordinator = *_impl::RealmCoordinator::get_existing_coordinator(config.path);
+    auto& coordinator = *_impl::RealmCoordinator::get_coordinator(config.path);
 
 
     auto origin = r->read_group().get_table("class_origin");
     auto origin = r->read_group().get_table("class_origin");
     auto target = r->read_group().get_table("class_target");
     auto target = r->read_group().get_table("class_target");
     auto other_origin = r->read_group().get_table("class_other_origin");
     auto other_origin = r->read_group().get_table("class_other_origin");
     auto other_target = r->read_group().get_table("class_other_target");
     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();
     r->begin_transaction();
 
 
-    target->add_empty_row(10);
+    std::vector<ObjKey> target_keys;
+    target->create_objects(10, target_keys);
     for (int i = 0; i < 10; ++i)
     for (int i = 0; i < 10; ++i)
-        target->set_int(0, i, i);
+        target->get_object(target_keys[i]).set_all(i);
 
 
-    origin->add_empty_row(2);
-    origin->set_int_unique(0, 0, 1);
-    origin->set_int_unique(0, 1, 2);
-    LinkViewRef lv = origin->get_linklist(1, 0);
+    Obj obj = origin->create_object();
+    auto lv = obj.get_linklist_ptr(col_link);
     for (int i = 0; i < 10; ++i)
     for (int i = 0; i < 10; ++i)
-        lv->add(i);
-    LinkViewRef lv2 = origin->get_linklist(1, 1);
+        lv->add(target_keys[i]);
+    auto lv2 = origin->create_object().get_linklist_ptr(col_link);
     for (int i = 0; i < 10; ++i)
     for (int i = 0; i < 10; ++i)
-        lv2->add(i);
+        lv2->add(target_keys[i]);
 
 
-    other_origin->add_empty_row();
-    other_target->add_empty_row(10);
+    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)
     for (int i = 0; i < 10; ++i)
-        other_target->set_int(0, i, i);
-    LinkViewRef other_lv = other_origin->get_linklist(0, 0);
+        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)
     for (int i = 0; i < 10; ++i)
-        other_lv->add(i);
+        other_lv->add(other_target_keys[i]);
 
 
     r->commit_transaction();
     r->commit_transaction();
 
 
     auto r2 = coordinator.get_realm();
     auto r2 = coordinator.get_realm();
-    auto r2_lv = r2->read_group().get_table("class_origin")->get_linklist(1, 0);
+    auto r2_lv = r2->read_group().get_table("class_origin")->get_object(0).get_linklist_ptr(col_link);
 
 
     SECTION("add_notification_block()") {
     SECTION("add_notification_block()") {
         CollectionChangeSet change;
         CollectionChangeSet change;
-        List lst(r, lv);
+        List lst(r, obj, col_link);
 
 
         auto write = [&](auto&& f) {
         auto write = [&](auto&& f) {
             r->begin_transaction();
             r->begin_transaction();
@@ -129,23 +132,23 @@ TEST_CASE("list") {
 
 
         SECTION("modifying the list sends a change notifications") {
         SECTION("modifying the list sends a change notifications") {
             auto token = require_change();
             auto token = require_change();
-            write([&] { lst.remove(5); });
+            write([&] { if (lv2->size() > 5) lst.remove(5); });
             REQUIRE_INDICES(change.deletions, 5);
             REQUIRE_INDICES(change.deletions, 5);
         }
         }
 
 
         SECTION("modifying a different list doesn't send a change notification") {
         SECTION("modifying a different list doesn't send a change notification") {
             auto token = require_no_change();
             auto token = require_no_change();
-            write([&] { lv2->remove(5); });
+            write([&] { if (lv2->size() > 5) lv2->remove(5); });
         }
         }
 
 
         SECTION("deleting the list sends a change notification") {
         SECTION("deleting the list sends a change notification") {
             auto token = require_change();
             auto token = require_change();
-            write([&] { origin->move_last_over(0); });
+            write([&] { obj.remove(); });
             REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
             REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
 
 
             // Should not resend delete all notification after another commit
             // Should not resend delete all notification after another commit
             change = {};
             change = {};
-            write([&] { target->add_empty_row(); });
+            write([&] { target->create_object(); });
             REQUIRE(change.empty());
             REQUIRE(change.empty());
         }
         }
 
 
@@ -154,27 +157,28 @@ TEST_CASE("list") {
                 change = c;
                 change = c;
             });
             });
             advance_and_notify(*r);
             advance_and_notify(*r);
-            write([&] { origin->move_last_over(0); });
+            write([&] { origin->begin()->remove(); });
             REQUIRE_INDICES(change.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
             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") {
         SECTION("modifying one of the target rows sends a change notification") {
             auto token = require_change();
             auto token = require_change();
-            write([&] { lst.get(5).set_int(0, 6); });
+            write([&] { lst.get(5).set(col_value, 6); });
             REQUIRE_INDICES(change.modifications, 5);
             REQUIRE_INDICES(change.modifications, 5);
         }
         }
 
 
         SECTION("deleting a target row sends a change notification") {
         SECTION("deleting a target row sends a change notification") {
             auto token = require_change();
             auto token = require_change();
-            write([&] { target->move_last_over(5); });
+            write([&] { target->remove_object(target_keys[5]); });
             REQUIRE_INDICES(change.deletions, 5);
             REQUIRE_INDICES(change.deletions, 5);
         }
         }
 
 
         SECTION("adding a row and then modifying the target row does not mark the row as modified") {
         SECTION("adding a row and then modifying the target row does not mark the row as modified") {
             auto token = require_change();
             auto token = require_change();
             write([&] {
             write([&] {
-                lst.add(5);
-                target->set_int(0, 5, 10);
+                Obj obj = target->get_object(target_keys[5]);
+                lst.add(obj);
+                obj.set(col_value, 10);
             });
             });
             REQUIRE_INDICES(change.insertions, 10);
             REQUIRE_INDICES(change.insertions, 10);
             REQUIRE_INDICES(change.modifications, 5);
             REQUIRE_INDICES(change.modifications, 5);
@@ -183,7 +187,7 @@ TEST_CASE("list") {
         SECTION("modifying and then moving a row reports move/insert but not modification") {
         SECTION("modifying and then moving a row reports move/insert but not modification") {
             auto token = require_change();
             auto token = require_change();
             write([&] {
             write([&] {
-                target->set_int(0, 5, 10);
+                target->get_object(target_keys[5]).set(col_value, 10);
                 lst.move(5, 8);
                 lst.move(5, 8);
             });
             });
             REQUIRE_INDICES(change.insertions, 8);
             REQUIRE_INDICES(change.insertions, 8);
@@ -194,21 +198,21 @@ TEST_CASE("list") {
 
 
         SECTION("modifying a row which appears multiple times in a list marks them all as modified") {
         SECTION("modifying a row which appears multiple times in a list marks them all as modified") {
             r->begin_transaction();
             r->begin_transaction();
-            lst.add(5);
+            lst.add(target_keys[5]);
             r->commit_transaction();
             r->commit_transaction();
 
 
             auto token = require_change();
             auto token = require_change();
-            write([&] { target->set_int(0, 5, 10); });
+            write([&] { target->get_object(target_keys[5]).set(col_value, 10); });
             REQUIRE_INDICES(change.modifications, 5, 10);
             REQUIRE_INDICES(change.modifications, 5, 10);
         }
         }
 
 
         SECTION("deleting a row which appears multiple times in a list marks them all as modified") {
         SECTION("deleting a row which appears multiple times in a list marks them all as modified") {
             r->begin_transaction();
             r->begin_transaction();
-            lst.add(5);
+            lst.add(target_keys[5]);
             r->commit_transaction();
             r->commit_transaction();
 
 
             auto token = require_change();
             auto token = require_change();
-            write([&] { target->move_last_over(5); });
+            write([&] { target->remove_object(target_keys[5]); });
             REQUIRE_INDICES(change.deletions, 5, 10);
             REQUIRE_INDICES(change.deletions, 5, 10);
         }
         }
 
 
@@ -221,30 +225,32 @@ TEST_CASE("list") {
         SECTION("moving a target row does not send a change notification") {
         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
             // Remove a row from the LV so that we have one to delete that's not in the list
             r->begin_transaction();
             r->begin_transaction();
-            lv->remove(2);
+            if (lv->size() > 2)
+                lv->remove(2);
             r->commit_transaction();
             r->commit_transaction();
 
 
             auto token = require_no_change();
             auto token = require_no_change();
-            write([&] { target->move_last_over(2); });
+            write([&] { target->remove_object(target_keys[2]); });
         }
         }
 
 
-        SECTION("multiple LinkViws for the same LinkList can get notifications") {
+        SECTION("multiple LinkViews for the same LinkList can get notifications") {
             r->begin_transaction();
             r->begin_transaction();
             target->clear();
             target->clear();
-            target->add_empty_row(5);
+            std::vector<ObjKey> keys;
+            target->create_objects(5, keys);
             r->commit_transaction();
             r->commit_transaction();
 
 
             auto get_list = [&] {
             auto get_list = [&] {
                 auto r = Realm::get_shared_realm(config);
                 auto r = Realm::get_shared_realm(config);
-                auto lv = r->read_group().get_table("class_origin")->get_linklist(1, 0);
-                return List(r, lv);
+                auto obj = r->read_group().get_table("class_origin")->get_object(0);
+                return List(r, obj, col_link);
             };
             };
             auto change_list = [&] {
             auto change_list = [&] {
                 r->begin_transaction();
                 r->begin_transaction();
                 if (lv->size()) {
                 if (lv->size()) {
-                    target->set_int(0, lv->size() - 1, lv->size());
+                    target->get_object(lv->size() - 1).set(col_value, int64_t(lv->size()));
                 }
                 }
-                lv->add(lv->size());
+                lv->add(keys[lv->size()]);
                 r->commit_transaction();
                 r->commit_transaction();
             };
             };
 
 
@@ -291,7 +297,7 @@ TEST_CASE("list") {
             auto token2 = require_change();
             auto token2 = require_change();
 
 
             r->begin_transaction();
             r->begin_transaction();
-            lv->add(0);
+            lv->add(target_keys[0]);
             token.suppress_next();
             token.suppress_next();
             r->commit_transaction();
             r->commit_transaction();
 
 
@@ -302,14 +308,14 @@ TEST_CASE("list") {
         SECTION("multiple Lists for the same LinkView can be skipped individually") {
         SECTION("multiple Lists for the same LinkView can be skipped individually") {
             auto token = require_no_change();
             auto token = require_no_change();
 
 
-            List list2(r, lv);
+            List list2(r, obj, col_link);
             auto token2 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
             auto token2 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
                 change = c;
                 change = c;
             });
             });
             advance_and_notify(*r);
             advance_and_notify(*r);
 
 
             r->begin_transaction();
             r->begin_transaction();
-            lv->add(0);
+            lv->add(target_keys[0]);
             token.suppress_next();
             token.suppress_next();
             r->commit_transaction();
             r->commit_transaction();
 
 
@@ -329,7 +335,7 @@ TEST_CASE("list") {
 
 
             // should now produce a notification
             // should now produce a notification
             r->begin_transaction();
             r->begin_transaction();
-            lv->add(0);
+            lv->add(target_keys[0]);
             r->commit_transaction();
             r->commit_transaction();
             advance_and_notify(*r);
             advance_and_notify(*r);
             REQUIRE_INDICES(change.insertions, 10);
             REQUIRE_INDICES(change.insertions, 10);
@@ -337,11 +343,11 @@ TEST_CASE("list") {
 
 
         SECTION("modifying a different table does not send a change notification") {
         SECTION("modifying a different table does not send a change notification") {
             auto token = require_no_change();
             auto token = require_no_change();
-            write([&] { other_lv->add(0); });
+            write([&] { other_lv->add(other_target_keys[0]); });
         }
         }
 
 
         SECTION("changes are reported correctly for multiple tables") {
         SECTION("changes are reported correctly for multiple tables") {
-            List list2(r, other_lv);
+            List list2(r, *other_lv);
             CollectionChangeSet other_changes;
             CollectionChangeSet other_changes;
             auto token1 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
             auto token1 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
                 other_changes = std::move(c);
                 other_changes = std::move(c);
@@ -349,28 +355,29 @@ TEST_CASE("list") {
             auto token2 = require_change();
             auto token2 = require_change();
 
 
             write([&] {
             write([&] {
-                lv->add(1);
+                lv->add(target_keys[1]);
 
 
-                other_origin->insert_empty_row(0);
-                other_lv->insert(1, 0);
+                other_origin->create_object();
+                if (other_lv->size() > 0)
+                    other_lv->insert(1, other_target_keys[0]);
 
 
-                lv->add(2);
+                lv->add(target_keys[2]);
             });
             });
             REQUIRE_INDICES(change.insertions, 10, 11);
             REQUIRE_INDICES(change.insertions, 10, 11);
             REQUIRE_INDICES(other_changes.insertions, 1);
             REQUIRE_INDICES(other_changes.insertions, 1);
 
 
             write([&] {
             write([&] {
-                lv->add(3);
-                other_origin->move_last_over(1);
-                lv->add(4);
+                lv->add(target_keys[3]);
+                other_obj.remove();
+                lv->add(target_keys[4]);
             });
             });
             REQUIRE_INDICES(change.insertions, 12, 13);
             REQUIRE_INDICES(change.insertions, 12, 13);
             REQUIRE_INDICES(other_changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
             REQUIRE_INDICES(other_changes.deletions, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
 
 
             write([&] {
             write([&] {
-                lv->add(5);
+                lv->add(target_keys[5]);
                 other_origin->clear();
                 other_origin->clear();
-                lv->add(6);
+                lv->add(target_keys[6]);
             });
             });
             REQUIRE_INDICES(change.insertions, 14, 15);
             REQUIRE_INDICES(change.insertions, 14, 15);
         }
         }
@@ -385,19 +392,19 @@ TEST_CASE("list") {
             });
             });
 
 
             r2->begin_transaction();
             r2->begin_transaction();
-            r2->read_group().get_table("class_target")->set_int(0, 0, 10);
-            r2->read_group().get_table("class_other_target")->set_int(0, 1, 10);
+            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();
             r2->commit_transaction();
 
 
-            List list2(r2, r2->read_group().get_table("class_other_origin")->get_linklist(0, 0));
+            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) {
             auto token2 = list2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
                 changes2 = std::move(c);
                 changes2 = std::move(c);
             });
             });
 
 
             auto r3 = coordinator.get_realm();
             auto r3 = coordinator.get_realm();
             r3->begin_transaction();
             r3->begin_transaction();
-            r3->read_group().get_table("class_target")->set_int(0, 2, 10);
-            r3->read_group().get_table("class_other_target")->set_int(0, 3, 10);
+            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();
             r3->commit_transaction();
 
 
             advance_and_notify(*r);
             advance_and_notify(*r);
@@ -411,8 +418,8 @@ TEST_CASE("list") {
             auto token = require_change();
             auto token = require_change();
 
 
             r2->begin_transaction();
             r2->begin_transaction();
-            r2_lv->get(5).set_int(0, 10);
-            r2_lv->get(1).set_int(0, 10);
+            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(5, 8);
             r2_lv->move(1, 2);
             r2_lv->move(1, 2);
             r2->commit_transaction();
             r2->commit_transaction();
@@ -420,7 +427,8 @@ TEST_CASE("list") {
             coordinator.on_change();
             coordinator.on_change();
 
 
             r2->begin_transaction();
             r2->begin_transaction();
-            r2_lv->move(8, 5);
+            if (r2_lv->size() > 8)
+                r2_lv->move(8, 5);
             r2->commit_transaction();
             r2->commit_transaction();
             advance_and_notify(*r);
             advance_and_notify(*r);
 
 
@@ -430,46 +438,6 @@ TEST_CASE("list") {
             REQUIRE_MOVES(change, {1, 2});
             REQUIRE_MOVES(change, {1, 2});
         }
         }
 
 
-        SECTION("moving the list's containing row does not break notifications") {
-            auto token = require_change();
-
-            // insert rows before it
-            write([&] {
-                origin->insert_empty_row(0, 2);
-                lv->add(1);
-            });
-            REQUIRE_INDICES(change.insertions, 10);
-            REQUIRE(lst.size() == 11);
-            REQUIRE(lst.get(10).get_index() == 1);
-
-            // swap the row containing it with another row
-            write([&] {
-                origin->swap_rows(2, 3);
-                lv->add(2);
-            });
-            REQUIRE_INDICES(change.insertions, 11);
-            REQUIRE(lst.size() == 12);
-            REQUIRE(lst.get(11).get_index() == 2);
-
-            // swap it back to verify both of the rows in the swap are handled
-            write([&] {
-                origin->swap_rows(2, 3);
-                lv->add(3);
-            });
-            REQUIRE_INDICES(change.insertions, 12);
-            REQUIRE(lst.size() == 13);
-            REQUIRE(lst.get(12).get_index() == 3);
-
-            // delete a row so that it's moved (as it's at the end)
-            write([&] {
-                origin->move_last_over(0);
-                lv->add(4);
-            });
-            REQUIRE_INDICES(change.insertions, 13);
-            REQUIRE(lst.size() == 14);
-            REQUIRE(lst.get(13).get_index() == 4);
-        }
-
         SECTION("changes are sent in initial notification") {
         SECTION("changes are sent in initial notification") {
             auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
             auto token = lst.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
                 change = c;
                 change = c;
@@ -530,8 +498,8 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("sorted add_notification_block()") {
     SECTION("sorted add_notification_block()") {
-        List lst(r, lv);
-        Results results = lst.sort({*target, {{0}}, {false}});
+        List lst(r, *lv);
+        Results results = lst.sort({{{col_value}}, {false}});
 
 
         int notification_calls = 0;
         int notification_calls = 0;
         CollectionChangeSet change;
         CollectionChangeSet change;
@@ -550,12 +518,11 @@ TEST_CASE("list") {
 
 
             advance_and_notify(*r);
             advance_and_notify(*r);
         };
         };
-
         SECTION("add duplicates") {
         SECTION("add duplicates") {
             write([&] {
             write([&] {
-                lst.add(5);
-                lst.add(5);
-                lst.add(5);
+                lst.add(target_keys[5]);
+                lst.add(target_keys[5]);
+                lst.add(target_keys[5]);
             });
             });
             REQUIRE(notification_calls == 2);
             REQUIRE(notification_calls == 2);
             REQUIRE_INDICES(change.insertions, 5, 6, 7);
             REQUIRE_INDICES(change.insertions, 5, 6, 7);
@@ -563,7 +530,7 @@ TEST_CASE("list") {
 
 
         SECTION("change order by modifying target") {
         SECTION("change order by modifying target") {
             write([&] {
             write([&] {
-                lst.get(5).set_int(0, 15);
+                lst.get(5).set(col_value, 15);
             });
             });
             REQUIRE(notification_calls == 2);
             REQUIRE(notification_calls == 2);
             REQUIRE_INDICES(change.deletions, 4);
             REQUIRE_INDICES(change.deletions, 4);
@@ -586,8 +553,8 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("filtered add_notification_block()") {
     SECTION("filtered add_notification_block()") {
-        List lst(r, lv);
-        Results results = lst.filter(target->where().less(0, 9));
+        List lst(r, *lv);
+        Results results = lst.filter(target->where().less(col_value, 9));
 
 
         int notification_calls = 0;
         int notification_calls = 0;
         CollectionChangeSet change;
         CollectionChangeSet change;
@@ -606,12 +573,11 @@ TEST_CASE("list") {
 
 
             advance_and_notify(*r);
             advance_and_notify(*r);
         };
         };
-
         SECTION("add duplicates") {
         SECTION("add duplicates") {
             write([&] {
             write([&] {
-                lst.add(5);
-                lst.add(5);
-                lst.add(5);
+                lst.add(target_keys[5]);
+                lst.add(target_keys[5]);
+                lst.add(target_keys[5]);
             });
             });
             REQUIRE(notification_calls == 2);
             REQUIRE(notification_calls == 2);
             REQUIRE_INDICES(change.insertions, 9, 10, 11);
             REQUIRE_INDICES(change.insertions, 9, 10, 11);
@@ -652,50 +618,50 @@ TEST_CASE("list") {
 
 
     SECTION("sort()") {
     SECTION("sort()") {
         auto objectschema = &*r->schema().find("target");
         auto objectschema = &*r->schema().find("target");
-        List list(r, lv);
-        auto results = list.sort({*target, {{0}}, {false}});
+        List list(r, *lv);
+        auto results = list.sort({{{col_value}}, {false}});
 
 
         REQUIRE(&results.get_object_schema() == objectschema);
         REQUIRE(&results.get_object_schema() == objectschema);
-        REQUIRE(results.get_mode() == Results::Mode::LinkView);
+        REQUIRE(results.get_mode() == Results::Mode::LinkList);
         REQUIRE(results.size() == 10);
         REQUIRE(results.size() == 10);
 
 
         // Aggregates don't inherently have to convert to TableView, but do
         // Aggregates don't inherently have to convert to TableView, but do
         // because aggregates aren't implemented for LinkView
         // because aggregates aren't implemented for LinkView
-        REQUIRE(results.sum(0) == 45);
+        REQUIRE(results.sum(col_value) == 45);
         REQUIRE(results.get_mode() == Results::Mode::TableView);
         REQUIRE(results.get_mode() == Results::Mode::TableView);
 
 
         // Reset to LinkView mode to test implicit conversion to TableView on get()
         // Reset to LinkView mode to test implicit conversion to TableView on get()
-        results = list.sort({*target, {{0}}, {false}});
+        results = list.sort({{{col_value}}, {false}});
         for (size_t i = 0; i < 10; ++i)
         for (size_t i = 0; i < 10; ++i)
-            REQUIRE(results.get(i).get_index() == 9 - 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_THROWS_WITH(results.get(10), "Requested index 10 greater than max 9");
         REQUIRE(results.get_mode() == Results::Mode::TableView);
         REQUIRE(results.get_mode() == Results::Mode::TableView);
 
 
         // Zero sort columns should leave it in LinkView mode
         // Zero sort columns should leave it in LinkView mode
-        results = list.sort({*target, {}, {}});
+        results = list.sort(SortDescriptor());
         for (size_t i = 0; i < 10; ++i)
         for (size_t i = 0; i < 10; ++i)
-            REQUIRE(results.get(i).get_index() == i);
+            REQUIRE(results.get(i).get_key() == target_keys[i]);
         REQUIRE_THROWS_WITH(results.get(10), "Requested index 10 greater than max 9");
         REQUIRE_THROWS_WITH(results.get(10), "Requested index 10 greater than max 9");
-        REQUIRE(results.get_mode() == Results::Mode::LinkView);
+        REQUIRE(results.get_mode() == Results::Mode::LinkList);
     }
     }
 
 
     SECTION("filter()") {
     SECTION("filter()") {
         auto objectschema = &*r->schema().find("target");
         auto objectschema = &*r->schema().find("target");
-        List list(r, lv);
-        auto results = list.filter(target->where().greater(0, 5));
+        List list(r, *lv);
+        auto results = list.filter(target->where().greater(col_value, 5));
 
 
         REQUIRE(&results.get_object_schema() == objectschema);
         REQUIRE(&results.get_object_schema() == objectschema);
         REQUIRE(results.get_mode() == Results::Mode::Query);
         REQUIRE(results.get_mode() == Results::Mode::Query);
         REQUIRE(results.size() == 4);
         REQUIRE(results.size() == 4);
 
 
         for (size_t i = 0; i < 4; ++i) {
         for (size_t i = 0; i < 4; ++i) {
-            REQUIRE(results.get(i).get_index() == i + 6);
+            REQUIRE(results.get(i).get_key().value == i + 6);
         }
         }
     }
     }
 
 
     SECTION("snapshot()") {
     SECTION("snapshot()") {
         auto objectschema = &*r->schema().find("target");
         auto objectschema = &*r->schema().find("target");
-        List list(r, lv);
+        List list(r, *lv);
 
 
         auto snapshot = list.snapshot();
         auto snapshot = list.snapshot();
         REQUIRE(&snapshot.get_object_schema() == objectschema);
         REQUIRE(&snapshot.get_object_schema() == objectschema);
@@ -708,30 +674,30 @@ TEST_CASE("list") {
         }
         }
         REQUIRE(snapshot.size() == 10);
         REQUIRE(snapshot.size() == 10);
         for (size_t i = 0; i < snapshot.size(); ++i) {
         for (size_t i = 0; i < snapshot.size(); ++i) {
-            REQUIRE(snapshot.get(i).is_attached());
+            REQUIRE(snapshot.get(i).is_valid());
         }
         }
         for (size_t i = 0; i < 5; ++i) {
         for (size_t i = 0; i < 5; ++i) {
-            target->move_last_over(i);
+            target->remove_object(target_keys[i]);
         }
         }
         REQUIRE(snapshot.size() == 10);
         REQUIRE(snapshot.size() == 10);
         for (size_t i = 0; i < 5; ++i) {
         for (size_t i = 0; i < 5; ++i) {
-            REQUIRE(!snapshot.get(i).is_attached());
+            REQUIRE(!snapshot.get(i).is_valid());
         }
         }
         for (size_t i = 5; i < 10; ++i) {
         for (size_t i = 5; i < 10; ++i) {
-            REQUIRE(snapshot.get(i).is_attached());
+            REQUIRE(snapshot.get(i).is_valid());
         }
         }
-        list.add(0);
+        list.add(target_keys[5]);
         REQUIRE(snapshot.size() == 10);
         REQUIRE(snapshot.size() == 10);
     }
     }
 
 
     SECTION("get_object_schema()") {
     SECTION("get_object_schema()") {
-        List list(r, lv);
+        List list(r, *lv);
         auto objectschema = &*r->schema().find("target");
         auto objectschema = &*r->schema().find("target");
         REQUIRE(&list.get_object_schema() == objectschema);
         REQUIRE(&list.get_object_schema() == objectschema);
     }
     }
 
 
     SECTION("delete_at()") {
     SECTION("delete_at()") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
         auto initial_view_size = lv->size();
         auto initial_view_size = lv->size();
         auto initial_target_size = target->size();
         auto initial_target_size = target->size();
@@ -742,7 +708,7 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("delete_all()") {
     SECTION("delete_all()") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
         list.delete_all();
         list.delete_all();
         REQUIRE(lv->size() == 0);
         REQUIRE(lv->size() == 0);
@@ -751,7 +717,7 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("as_results().clear()") {
     SECTION("as_results().clear()") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
         list.as_results().clear();
         list.as_results().clear();
         REQUIRE(lv->size() == 0);
         REQUIRE(lv->size() == 0);
@@ -760,7 +726,7 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("snapshot().clear()") {
     SECTION("snapshot().clear()") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
         auto snapshot = list.snapshot();
         auto snapshot = list.snapshot();
         snapshot.clear();
         snapshot.clear();
@@ -772,153 +738,155 @@ TEST_CASE("list") {
     }
     }
 
 
     SECTION("add(RowExpr)") {
     SECTION("add(RowExpr)") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
         SECTION("adds rows from the correct table") {
         SECTION("adds rows from the correct table") {
-            list.add(target->get(5));
+            list.add(target_keys[5]);
             REQUIRE(list.size() == 11);
             REQUIRE(list.size() == 11);
-            REQUIRE(list.get(10).get_index() == 5);
+            REQUIRE(list.get(10).get_key() == target_keys[5]);
         }
         }
 
 
         SECTION("throws for rows from the wrong table") {
         SECTION("throws for rows from the wrong table") {
-            REQUIRE_THROWS(list.add(origin->get(0)));
+            REQUIRE_THROWS(list.add(obj));
         }
         }
         r->cancel_transaction();
         r->cancel_transaction();
     }
     }
 
 
     SECTION("insert(RowExpr)") {
     SECTION("insert(RowExpr)") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
 
 
         SECTION("insert rows from the correct table") {
         SECTION("insert rows from the correct table") {
-            list.insert(0, target->get(5));
+            list.insert(0, target_keys[5]);
             REQUIRE(list.size() == 11);
             REQUIRE(list.size() == 11);
-            REQUIRE(list.get(0).get_index() == 5);
+            REQUIRE(list.get(0).get_key() == target_keys[5]);
         }
         }
 
 
         SECTION("throws for rows from the wrong table") {
         SECTION("throws for rows from the wrong table") {
-            REQUIRE_THROWS(list.insert(0, origin->get(0)));
+            REQUIRE_THROWS(list.insert(0, obj));
         }
         }
 
 
         SECTION("throws for out of bounds insertions") {
         SECTION("throws for out of bounds insertions") {
-            REQUIRE_THROWS(list.insert(11, target->get(5)));
-            REQUIRE_NOTHROW(list.insert(10, target->get(5)));
+            REQUIRE_THROWS(list.insert(11, target_keys[5]));
+            REQUIRE_NOTHROW(list.insert(10, target_keys[5]));
         }
         }
         r->cancel_transaction();
         r->cancel_transaction();
     }
     }
 
 
     SECTION("set(RowExpr)") {
     SECTION("set(RowExpr)") {
-        List list(r, lv);
+        List list(r, *lv);
         r->begin_transaction();
         r->begin_transaction();
 
 
         SECTION("assigns for rows from the correct table") {
         SECTION("assigns for rows from the correct table") {
-            list.set(0, target->get(5));
+            list.set(0, target_keys[5]);
             REQUIRE(list.size() == 10);
             REQUIRE(list.size() == 10);
-            REQUIRE(list.get(0).get_index() == 5);
+            REQUIRE(list.get(0).get_key() == target_keys[5]);
         }
         }
 
 
         SECTION("throws for rows from the wrong table") {
         SECTION("throws for rows from the wrong table") {
-            REQUIRE_THROWS(list.set(0, origin->get(0)));
+            REQUIRE_THROWS(list.set(0, obj));
         }
         }
 
 
         SECTION("throws for out of bounds sets") {
         SECTION("throws for out of bounds sets") {
-            REQUIRE_THROWS(list.set(10, target->get(5)));
+            REQUIRE_THROWS(list.set(10, target_keys[5]));
         }
         }
         r->cancel_transaction();
         r->cancel_transaction();
     }
     }
 
 
     SECTION("find(RowExpr)") {
     SECTION("find(RowExpr)") {
-        List list(r, lv);
+        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") {
         SECTION("returns index in list for values in the list") {
-            REQUIRE(list.find(target->get(5)) == 5);
+            REQUIRE(list.find(obj5) == 5);
         }
         }
 
 
         SECTION("returns index in list and not index in table") {
         SECTION("returns index in list and not index in table") {
             r->begin_transaction();
             r->begin_transaction();
             list.remove(1);
             list.remove(1);
-            REQUIRE(list.find(target->get(5)) == 4);
-            REQUIRE(list.as_results().index_of(target->get(5)) == 4);
+            REQUIRE(list.find(obj5) == 4);
+            REQUIRE(list.as_results().index_of(obj5) == 4);
             r->cancel_transaction();
             r->cancel_transaction();
         }
         }
 
 
         SECTION("returns npos for values not in the list") {
         SECTION("returns npos for values not in the list") {
             r->begin_transaction();
             r->begin_transaction();
             list.remove(1);
             list.remove(1);
-            REQUIRE(list.find(target->get(1)) == npos);
-            REQUIRE(list.as_results().index_of(target->get(1)) == npos);
+            REQUIRE(list.find(obj1) == npos);
+            REQUIRE(list.as_results().index_of(obj1) == npos);
             r->cancel_transaction();
             r->cancel_transaction();
         }
         }
 
 
         SECTION("throws for row in wrong table") {
         SECTION("throws for row in wrong table") {
-            REQUIRE_THROWS(list.find(origin->get(0)));
-            REQUIRE_THROWS(list.as_results().index_of(origin->get(0)));
+            REQUIRE_THROWS(list.find(obj));
+            REQUIRE_THROWS(list.as_results().index_of(obj));
         }
         }
     }
     }
 
 
     SECTION("find(Query)") {
     SECTION("find(Query)") {
-        List list(r, lv);
+        List list(r, *lv);
 
 
         SECTION("returns index in list for values in the list") {
         SECTION("returns index in list for values in the list") {
-            REQUIRE(list.find(std::move(target->where().equal(0, 5))) == 5);
+            REQUIRE(list.find(std::move(target->where().equal(col_value, 5))) == 5);
         }
         }
 
 
         SECTION("returns index in list and not index in table") {
         SECTION("returns index in list and not index in table") {
             r->begin_transaction();
             r->begin_transaction();
             list.remove(1);
             list.remove(1);
-            REQUIRE(list.find(std::move(target->where().equal(0, 5))) == 4);
+            REQUIRE(list.find(std::move(target->where().equal(col_value, 5))) == 4);
             r->cancel_transaction();
             r->cancel_transaction();
         }
         }
 
 
         SECTION("returns npos for values not in the list") {
         SECTION("returns npos for values not in the list") {
-            REQUIRE(list.find(std::move(target->where().equal(0, 11))) == npos);
+            REQUIRE(list.find(std::move(target->where().equal(col_value, 11))) == npos);
         }
         }
     }
     }
 
 
     SECTION("add(Context)") {
     SECTION("add(Context)") {
-        List list(r, lv);
+        List list(r, *lv);
         CppContext ctx(r, &list.get_object_schema());
         CppContext ctx(r, &list.get_object_schema());
         r->begin_transaction();
         r->begin_transaction();
 
 
         SECTION("adds boxed RowExpr") {
         SECTION("adds boxed RowExpr") {
-            list.add(ctx, util::Any(target->get(5)));
+            list.add(ctx, util::Any(target->get_object(target_keys[5])));
             REQUIRE(list.size() == 11);
             REQUIRE(list.size() == 11);
-            REQUIRE(list.get(10).get_index() == 5);
+            REQUIRE(list.get(10).get_key().value == 5);
         }
         }
 
 
         SECTION("adds boxed realm::Object") {
         SECTION("adds boxed realm::Object") {
-            realm::Object obj(r, list.get_object_schema(), target->get(5));
+            realm::Object obj(r, list.get_object_schema(), target->get_object(target_keys[5]));
             list.add(ctx, util::Any(obj));
             list.add(ctx, util::Any(obj));
             REQUIRE(list.size() == 11);
             REQUIRE(list.size() == 11);
-            REQUIRE(list.get(10).get_index() == 5);
+            REQUIRE(list.get(10).get_key() == target_keys[5]);
         }
         }
 
 
         SECTION("creates new object for dictionary") {
         SECTION("creates new object for dictionary") {
             list.add(ctx, util::Any(AnyDict{{"value", INT64_C(20)}}));
             list.add(ctx, util::Any(AnyDict{{"value", INT64_C(20)}}));
             REQUIRE(list.size() == 11);
             REQUIRE(list.size() == 11);
             REQUIRE(target->size() == 11);
             REQUIRE(target->size() == 11);
-            REQUIRE(list.get(10).get_int(0) == 20);
+            REQUIRE(list.get(10).get<Int>(col_value) == 20);
         }
         }
 
 
         SECTION("throws for object in wrong table") {
         SECTION("throws for object in wrong table") {
-            REQUIRE_THROWS(list.add(ctx, util::Any(origin->get(0))));
-            realm::Object obj(r, *r->schema().find("origin"), origin->get(0));
-            REQUIRE_THROWS(list.add(ctx, util::Any(obj)));
+            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();
         r->cancel_transaction();
     }
     }
 
 
     SECTION("find(Context)") {
     SECTION("find(Context)") {
-        List list(r, lv);
+        List list(r, *lv);
         CppContext ctx(r, &list.get_object_schema());
         CppContext ctx(r, &list.get_object_schema());
 
 
         SECTION("returns index in list for boxed RowExpr") {
         SECTION("returns index in list for boxed RowExpr") {
-            REQUIRE(list.find(ctx, util::Any(target->get(5))) == 5);
+            REQUIRE(list.find(ctx, util::Any(target->get_object(target_keys[5]))) == 5);
         }
         }
 
 
         SECTION("returns index in list for boxed Object") {
         SECTION("returns index in list for boxed Object") {
-            realm::Object obj(r, *r->schema().find("origin"), target->get(5));
+            realm::Object obj(r, *r->schema().find("origin"), target->get_object(target_keys[5]));
             REQUIRE(list.find(ctx, util::Any(obj)) == 5);
             REQUIRE(list.find(ctx, util::Any(obj)) == 5);
         }
         }
 
 
@@ -928,17 +896,17 @@ TEST_CASE("list") {
         }
         }
 
 
         SECTION("throws for object in wrong table") {
         SECTION("throws for object in wrong table") {
-            REQUIRE_THROWS(list.find(ctx, util::Any(origin->get(0))));
+            REQUIRE_THROWS(list.find(ctx, util::Any(obj)));
         }
         }
     }
     }
 
 
     SECTION("get(Context)") {
     SECTION("get(Context)") {
-        List list(r, lv);
+        List list(r, *lv);
         CppContext ctx(r, &list.get_object_schema());
         CppContext ctx(r, &list.get_object_schema());
 
 
         Object obj;
         Object obj;
         REQUIRE_NOTHROW(obj = any_cast<Object&&>(list.get(ctx, 1)));
         REQUIRE_NOTHROW(obj = any_cast<Object&&>(list.get(ctx, 1)));
         REQUIRE(obj.is_valid());
         REQUIRE(obj.is_valid());
-        REQUIRE(obj.row().get_index() == 1);
+        REQUIRE(obj.obj().get_key() == target_keys[1]);
     }
     }
 }
 }

+ 4 - 4
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/main.cpp

@@ -16,9 +16,6 @@
 //
 //
 ////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////
 
 
-// FIXME: the nextafter define below can be removed once we upgrade to an
-// Android ndk version which has support for std::nextafter (absent in ndk r10e)
-#define CATCH_CONFIG_GLOBAL_NEXTAFTER
 #define CATCH_CONFIG_RUNNER
 #define CATCH_CONFIG_RUNNER
 #include "catch2/catch.hpp"
 #include "catch2/catch.hpp"
 
 
@@ -42,7 +39,10 @@ int main(int argc, char** argv) {
     SetCurrentDirectoryA(path);
     SetCurrentDirectoryA(path);
 #else
 #else
     char executable[PATH_MAX];
     char executable[PATH_MAX];
-    realpath(argv[0], executable);
+    if (realpath(argv[0], executable) == NULL) {
+        fprintf(stderr, "Failed to resolve path to exectuable.\n");
+        return 1;
+    }
     const char* directory = dirname(executable);
     const char* directory = dirname(executable);
     chdir(directory);
     chdir(directory);
 #endif
 #endif

+ 120 - 141
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/migrations.cpp

@@ -27,7 +27,6 @@
 
 
 #include "impl/object_accessor_impl.hpp"
 #include "impl/object_accessor_impl.hpp"
 
 
-#include <realm/descriptor.hpp>
 #include <realm/group.hpp>
 #include <realm/group.hpp>
 #include <realm/table.hpp>
 #include <realm/table.hpp>
 #include <realm/util/scope_exit.hpp>
 #include <realm/util/scope_exit.hpp>
@@ -38,11 +37,11 @@
 
 
 using namespace realm;
 using namespace realm;
 
 
-#define VERIFY_SCHEMA(r) verify_schema((r), __LINE__)
+#define VERIFY_SCHEMA(r, m) verify_schema((r), __LINE__, m)
 
 
 #define REQUIRE_UPDATE_SUCCEEDS(r, s, version) do { \
 #define REQUIRE_UPDATE_SUCCEEDS(r, s, version) do { \
     REQUIRE_NOTHROW((r).update_schema(s, version)); \
     REQUIRE_NOTHROW((r).update_schema(s, version)); \
-    VERIFY_SCHEMA(r); \
+    VERIFY_SCHEMA(r, false); \
     REQUIRE((r).schema() == s); \
     REQUIRE((r).schema() == s); \
 } while (0)
 } while (0)
 
 
@@ -59,21 +58,28 @@ using namespace realm;
 } while (0)
 } while (0)
 
 
 namespace {
 namespace {
-void verify_schema(Realm& r, int line)
+void verify_schema(Realm& r, int line, bool in_migration)
 {
 {
     CAPTURE(line);
     CAPTURE(line);
     for (auto&& object_schema : r.schema()) {
     for (auto&& object_schema : r.schema()) {
-        auto table = ObjectStore::table_for_object_type(r.read_group(), object_schema.name);
+        auto table = r.read_group().get_table(object_schema.table_key);
         REQUIRE(table);
         REQUIRE(table);
+        REQUIRE(std::string(table->get_name()) == ObjectStore::table_name_for_object_type(object_schema.name));
         CAPTURE(object_schema.name);
         CAPTURE(object_schema.name);
-        std::string primary_key = ObjectStore::get_primary_key_for_object(r.read_group(), object_schema.name);
-        REQUIRE(primary_key == object_schema.primary_key);
+        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) {
         for (auto&& prop : object_schema.persisted_properties) {
-            size_t col = table->get_column_index(prop.name);
+            auto col = table->get_column_key(prop.name);
             CAPTURE(prop.name);
             CAPTURE(prop.name);
-            REQUIRE(col != npos);
-            REQUIRE(col == prop.table_column);
-            REQUIRE(to_underlying(ObjectSchema::from_core_type(*table->get_descriptor(), col)) ==
+            REQUIRE(col);
+            REQUIRE(col == prop.column_key);
+            REQUIRE(to_underlying(ObjectSchema::from_core_type(*table, col)) ==
                     to_underlying(prop.type));
                     to_underlying(prop.type));
             REQUIRE(table->has_search_index(col) == prop.requires_index());
             REQUIRE(table->has_search_index(col) == prop.requires_index());
             REQUIRE(bool(prop.is_primary) == (prop.name == primary_key));
             REQUIRE(bool(prop.is_primary) == (prop.name == primary_key));
@@ -158,6 +164,11 @@ Schema set_primary_key(Schema schema, StringData object_name, StringData new_pri
     object_schema.primary_key = new_primary_property;
     object_schema.primary_key = new_primary_property;
     return schema;
     return schema;
 }
 }
+auto create_objects(Table& table, size_t count) {
+    std::vector<ObjKey> keys;
+    table.create_objects(count, keys);
+    return keys;
+};
 } // anonymous namespace
 } // anonymous namespace
 
 
 TEST_CASE("migration: Automatic") {
 TEST_CASE("migration: Automatic") {
@@ -283,7 +294,7 @@ TEST_CASE("migration: Automatic") {
             REQUIRE((*realm).schema() == schema1);
             REQUIRE((*realm).schema() == schema1);
             REQUIRE_NOTHROW((*realm).update_schema(schema2, 1,
             REQUIRE_NOTHROW((*realm).update_schema(schema2, 1,
                             [](SharedRealm, SharedRealm, Schema&) { /* empty but present migration handler */ }));
                             [](SharedRealm, SharedRealm, Schema&) { /* empty but present migration handler */ }));
-            VERIFY_SCHEMA(*realm);
+            VERIFY_SCHEMA(*realm, false);
             REQUIRE((*realm).schema() == schema2);
             REQUIRE((*realm).schema() == schema2);
         }
         }
 
 
@@ -433,7 +444,7 @@ TEST_CASE("migration: Automatic") {
             realm->update_schema(schema, 1);
             realm->update_schema(schema, 1);
             REQUIRE_THROWS(realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
             REQUIRE_THROWS(realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
                 auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
                 auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-                table->add_empty_row(2);
+                create_objects(*table, 2);
             }));
             }));
         }
         }
 
 
@@ -446,8 +457,10 @@ TEST_CASE("migration: Automatic") {
             auto realm = Realm::get_shared_realm(config);
             auto realm = Realm::get_shared_realm(config);
             realm->update_schema(schema, 1);
             realm->update_schema(schema, 1);
 
 
+            realm->begin_transaction();
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-            table->add_empty_row(2);
+            create_objects(*table, 2);
+            realm->commit_transaction();
 
 
             schema = set_primary_key(schema, "object", "value");
             schema = set_primary_key(schema, "object", "value");
             REQUIRE_THROWS(realm->update_schema(schema, 2, nullptr));
             REQUIRE_THROWS(realm->update_schema(schema, 2, nullptr));
@@ -466,7 +479,7 @@ TEST_CASE("migration: Automatic") {
 
 
             REQUIRE_THROWS(realm->update_schema(schema2, 2, [](SharedRealm, SharedRealm realm, Schema&) {
             REQUIRE_THROWS(realm->update_schema(schema2, 2, [](SharedRealm, SharedRealm realm, Schema&) {
                 auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
                 auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-                table->add_empty_row();
+                table->create_object();
                 throw 5;
                 throw 5;
             }));
             }));
 
 
@@ -489,7 +502,7 @@ TEST_CASE("migration: Automatic") {
 
 
             realm->begin_transaction();
             realm->begin_transaction();
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-            table->add_empty_row(10);
+            create_objects(*table, 10);
             realm->commit_transaction();
             realm->commit_transaction();
 
 
             schema = set_type(schema, "object", "value", PropertyType::Float);
             schema = set_type(schema, "object", "value", PropertyType::Float);
@@ -508,14 +521,16 @@ TEST_CASE("migration: Automatic") {
 
 
             realm->begin_transaction();
             realm->begin_transaction();
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-            table->add_empty_row(10);
-            for (size_t i = 0; i < 10; ++i)
-                table->set_int(0, i, i);
+            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->commit_transaction();
 
 
             realm->update_schema(set_optional(schema, "object", "value", true), 2);
             realm->update_schema(set_optional(schema, "object", "value", true), 2);
+            key = table->get_column_key("value");
             for (int i = 0; i < 10; ++i)
             for (int i = 0; i < 10; ++i)
-                REQUIRE(table->get_int(0, i) == i);
+                REQUIRE(table->get_object(i).get<util::Optional<int64_t>>(key) == i);
         }
         }
 
 
         SECTION("values for nullable properties are discarded when converitng to required") {
         SECTION("values for nullable properties are discarded when converitng to required") {
@@ -529,14 +544,16 @@ TEST_CASE("migration: Automatic") {
 
 
             realm->begin_transaction();
             realm->begin_transaction();
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-            table->add_empty_row(10);
-            for (size_t i = 0; i < 10; ++i)
-                table->set_int(0, i, i);
+            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->commit_transaction();
 
 
             realm->update_schema(set_optional(schema, "object", "value", false), 2);
             realm->update_schema(set_optional(schema, "object", "value", false), 2);
+            key = table->get_column_key("value");
             for (size_t i = 0; i < 10; ++i)
             for (size_t i = 0; i < 10; ++i)
-                REQUIRE(table->get_int(0, i) == 0);
+                REQUIRE(table->get_object(i).get<int64_t>(key) == 0);
         }
         }
 
 
         SECTION("deleting table removed from the schema deletes it") {
         SECTION("deleting table removed from the schema deletes it") {
@@ -564,7 +581,7 @@ TEST_CASE("migration: Automatic") {
             realm->update_schema(schema, 1);
             realm->update_schema(schema, 1);
 
 
             realm->begin_transaction();
             realm->begin_transaction();
-            ObjectStore::table_for_object_type(realm->read_group(), "object")->add_empty_row();
+            ObjectStore::table_for_object_type(realm->read_group(), "object")->create_object();
             realm->commit_transaction();
             realm->commit_transaction();
 
 
             realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
             realm->update_schema(schema, 2, [](SharedRealm, SharedRealm realm, Schema&) {
@@ -588,32 +605,6 @@ TEST_CASE("migration: Automatic") {
                 ObjectStore::delete_data_for_object(realm->read_group(), "foo");
                 ObjectStore::delete_data_for_object(realm->read_group(), "foo");
             }));
             }));
         }
         }
-
-        SECTION("subtables columns are not modified by unrelated changes") {
-            config.in_memory = false;
-            Schema schema = {
-                {"object", {
-                    {"value", PropertyType::Int},
-                }},
-            };
-
-            {
-                auto realm = Realm::get_shared_realm(config);
-                realm->update_schema(schema, 1);
-                realm->begin_transaction();
-                get_table(realm, "object")->add_column(type_Table, "subtable");
-                realm->commit_transaction();
-            }
-            // close and reopen the Realm to ensure it rereads the schema from
-            // the group
-
-            auto realm = Realm::get_shared_realm(config);
-            realm->update_schema(add_property(schema, "object", {"value 2", PropertyType::Int}), 2);
-
-            auto& table = *get_table(realm, "object");
-            REQUIRE(table.get_column_type(1) == type_Table);
-            REQUIRE(table.get_column_count() == 3);
-        }
     }
     }
 
 
     SECTION("schema correctness during migration") {
     SECTION("schema correctness during migration") {
@@ -642,11 +633,11 @@ TEST_CASE("migration: Automatic") {
         REQUIRE(old_realm->schema() == schema); \
         REQUIRE(old_realm->schema() == schema); \
         REQUIRE(new_realm->schema_version() == 1); \
         REQUIRE(new_realm->schema_version() == 1); \
         REQUIRE(new_realm->schema() == new_schema); \
         REQUIRE(new_realm->schema() == new_schema); \
-        VERIFY_SCHEMA(*old_realm); \
-        VERIFY_SCHEMA(*new_realm); \
+        VERIFY_SCHEMA(*old_realm, true); \
+        VERIFY_SCHEMA(*new_realm, true); \
     }); \
     }); \
     REQUIRE(realm->schema() == new_schema); \
     REQUIRE(realm->schema() == new_schema); \
-    VERIFY_SCHEMA(*realm); \
+    VERIFY_SCHEMA(*realm, false); \
 } while (false)
 } while (false)
 
 
         SECTION("add new table") {
         SECTION("add new table") {
@@ -875,16 +866,16 @@ TEST_CASE("migration: Automatic") {
                 REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(ctx, "date")) == 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 linked_obj(new_realm, "link target", 0);
-                Object new_obj(new_realm, "link target", get_table(new_realm, "link target")->add_empty_row());
+                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"));
                 auto linking = any_cast<Results>(linked_obj.get_property_value<util::Any>(ctx, "origin"));
                 REQUIRE(linking.size() == 1);
                 REQUIRE(linking.size() == 1);
 
 
-                REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object")).row().get_index()
-                        == linked_obj.row().get_index());
+                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));
                 obj.set_property_value(ctx, "object", util::Any(new_obj));
-                REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object")).row().get_index()
-                        == new_obj.row().get_index());
+                REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(ctx, "object")).obj().get_key()
+                        == new_obj.obj().get_key());
 
 
                 REQUIRE(linking.size() == 0);
                 REQUIRE(linking.size() == 0);
             });
             });
@@ -920,6 +911,8 @@ TEST_CASE("migration: Automatic") {
 
 
         SECTION("change primary key property type") {
         SECTION("change primary key property type") {
             schema = set_type(schema, "all types", "pk", PropertyType::String);
             schema = set_type(schema, "all types", "pk", PropertyType::String);
+            // FIXME: changing the primary key of a type with binary columns currently crashes in core
+            schema = remove_property(schema, "all types", "data");
             realm->update_schema(schema, 2, [](auto, auto new_realm, auto&) {
             realm->update_schema(schema, 2, [](auto, auto new_realm, auto&) {
                 Object obj(new_realm, "all types", 0);
                 Object obj(new_realm, "all types", 0);
 
 
@@ -1075,8 +1068,7 @@ TEST_CASE("migration: Automatic") {
             realm->update_schema(old_schema, 1);
             realm->update_schema(old_schema, 1);
             realm->begin_transaction();
             realm->begin_transaction();
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
             auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-            table->add_empty_row();
-            table->set_int(0, 0, 10);
+            table->create_object().set_all(10);
             realm->commit_transaction();
             realm->commit_transaction();
         };
         };
 
 
@@ -1084,8 +1076,13 @@ TEST_CASE("migration: Automatic") {
     init(old_schema); \
     init(old_schema); \
     REQUIRE_NOTHROW(realm->update_schema(new_schema, 2, apply_renames({__VA_ARGS__}))); \
     REQUIRE_NOTHROW(realm->update_schema(new_schema, 2, apply_renames({__VA_ARGS__}))); \
     REQUIRE(realm->schema() == new_schema); \
     REQUIRE(realm->schema() == new_schema); \
-    VERIFY_SCHEMA(*realm); \
-    REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->get_int(0, 0) == 10); \
+    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)
 } while (false)
 
 
         SECTION("basic valid rename") {
         SECTION("basic valid rename") {
@@ -1166,12 +1163,6 @@ TEST_CASE("migration: Immutable") {
             };
             };
             REQUIRE_NOTHROW(realm->update_schema(schema));
             REQUIRE_NOTHROW(realm->update_schema(schema));
             REQUIRE(realm->schema() == schema);
             REQUIRE(realm->schema() == schema);
-
-            for (auto& object_schema : realm->schema()) {
-                for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
-                    REQUIRE(i == object_schema.persisted_properties[i].table_column);
-                }
-            }
         }
         }
 
 
         SECTION("extra tables") {
         SECTION("extra tables") {
@@ -1210,11 +1201,11 @@ TEST_CASE("migration: Immutable") {
 
 
             auto object_schema = realm->schema().find("object");
             auto object_schema = realm->schema().find("object");
             REQUIRE(object_schema->persisted_properties.size() == 1);
             REQUIRE(object_schema->persisted_properties.size() == 1);
-            REQUIRE(object_schema->persisted_properties[0].table_column == 0);
+            REQUIRE(object_schema->persisted_properties[0].column_key);
 
 
             object_schema = realm->schema().find("second object");
             object_schema = realm->schema().find("second object");
             REQUIRE(object_schema->persisted_properties.size() == 1);
             REQUIRE(object_schema->persisted_properties.size() == 1);
-            REQUIRE(object_schema->persisted_properties[0].table_column == size_t(-1));
+            REQUIRE(!object_schema->persisted_properties[0].column_key);
         }
         }
 
 
         SECTION("extra columns in table") {
         SECTION("extra columns in table") {
@@ -1289,12 +1280,6 @@ TEST_CASE("migration: ReadOnly") {
             };
             };
             REQUIRE_NOTHROW(realm->update_schema(schema));
             REQUIRE_NOTHROW(realm->update_schema(schema));
             REQUIRE(realm->schema() == schema);
             REQUIRE(realm->schema() == schema);
-
-            for (auto& object_schema : realm->schema()) {
-                for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
-                    REQUIRE(i == object_schema.persisted_properties[i].table_column);
-                }
-            }
         }
         }
 
 
         SECTION("extra tables") {
         SECTION("extra tables") {
@@ -1421,7 +1406,7 @@ TEST_CASE("migration: ResetFile") {
         realm->update_schema(schema);
         realm->update_schema(schema);
         REQUIRE(ino == get_fileid());
         REQUIRE(ino == get_fileid());
         realm->begin_transaction();
         realm->begin_transaction();
-        ObjectStore::table_for_object_type(realm->read_group(), "object")->add_empty_row();
+        ObjectStore::table_for_object_type(realm->read_group(), "object")->create_object();
         realm->commit_transaction();
         realm->commit_transaction();
     }
     }
     auto realm = Realm::get_shared_realm(config);
     auto realm = Realm::get_shared_realm(config);
@@ -1481,7 +1466,6 @@ TEST_CASE("migration: Additive") {
 
 
     TestFile config;
     TestFile config;
     config.schema_mode = SchemaMode::Additive;
     config.schema_mode = SchemaMode::Additive;
-    config.cache = false;
     config.schema = schema;
     config.schema = schema;
     auto realm = Realm::get_shared_realm(config);
     auto realm = Realm::get_shared_realm(config);
     realm->update_schema(schema);
     realm->update_schema(schema);
@@ -1502,26 +1486,28 @@ TEST_CASE("migration: Additive") {
 
 
     SECTION("indexes are updated when schema version is bumped") {
     SECTION("indexes are updated when schema version is bumped") {
         auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
         auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-        REQUIRE(table->has_search_index(0));
-        REQUIRE(!table->has_search_index(1));
+        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_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value", false), 1));
-        REQUIRE(!table->has_search_index(0));
+        REQUIRE(!table->has_search_index(col_keys[0]));
 
 
         REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true), 2));
         REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true), 2));
-        REQUIRE(table->has_search_index(1));
+        REQUIRE(table->has_search_index(col_keys[1]));
     }
     }
 
 
     SECTION("indexes are not updated when schema version is not bumped") {
     SECTION("indexes are not updated when schema version is not bumped") {
         auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
         auto table = ObjectStore::table_for_object_type(realm->read_group(), "object");
-        REQUIRE(table->has_search_index(0));
-        REQUIRE(!table->has_search_index(1));
+        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_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value", false)));
-        REQUIRE(table->has_search_index(0));
+        REQUIRE(table->has_search_index(col_keys[0]));
 
 
         REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true)));
         REQUIRE_NOTHROW(realm->update_schema(set_indexed(schema, "object", "value 2", true)));
-        REQUIRE(!table->has_search_index(1));
+        REQUIRE(!table->has_search_index(col_keys[1]));
     }
     }
 
 
     SECTION("can remove properties from existing tables, but column is not removed") {
     SECTION("can remove properties from existing tables, but column is not removed") {
@@ -1530,7 +1516,9 @@ TEST_CASE("migration: Additive") {
         REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->get_column_count() == 2);
         REQUIRE(ObjectStore::table_for_object_type(realm->read_group(), "object")->get_column_count() == 2);
         auto const& properties = realm->schema().find("object")->persisted_properties;
         auto const& properties = realm->schema().find("object")->persisted_properties;
         REQUIRE(properties.size() == 1);
         REQUIRE(properties.size() == 1);
-        REQUIRE(properties[0].table_column == 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") {
     SECTION("cannot change existing property types") {
@@ -1571,32 +1559,19 @@ TEST_CASE("migration: Additive") {
                                              [&](SharedRealm, SharedRealm, Schema&) { REQUIRE(false); }));
                                              [&](SharedRealm, SharedRealm, Schema&) { REQUIRE(false); }));
     }
     }
 
 
-    SECTION("add new columns at end from different SG") {
+    SECTION("add new columns from different SG") {
         auto realm2 = Realm::get_shared_realm(config);
         auto realm2 = Realm::get_shared_realm(config);
         auto& group = realm2->read_group();
         auto& group = realm2->read_group();
         realm2->begin_transaction();
         realm2->begin_transaction();
         auto table = ObjectStore::table_for_object_type(group, "object");
         auto table = ObjectStore::table_for_object_type(group, "object");
+        auto col_keys = table->get_column_keys();
         table->add_column(type_Int, "new column");
         table->add_column(type_Int, "new column");
         realm2->commit_transaction();
         realm2->commit_transaction();
 
 
         REQUIRE_NOTHROW(realm->refresh());
         REQUIRE_NOTHROW(realm->refresh());
         REQUIRE(realm->schema() == schema);
         REQUIRE(realm->schema() == schema);
-        REQUIRE(realm->schema().find("object")->persisted_properties[0].table_column == 0);
-        REQUIRE(realm->schema().find("object")->persisted_properties[1].table_column == 1);
-    }
-
-    SECTION("add new columns at beginning 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");
-        table->insert_column(0, type_Int, "new column");
-        realm2->commit_transaction();
-
-        REQUIRE_NOTHROW(realm->refresh());
-        REQUIRE(realm->schema() == schema);
-        REQUIRE(realm->schema().find("object")->persisted_properties[0].table_column == 1);
-        REQUIRE(realm->schema().find("object")->persisted_properties[1].table_column == 2);
+        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") {
     SECTION("opening new Realms uses the correct schema after an external change") {
@@ -1604,18 +1579,19 @@ TEST_CASE("migration: Additive") {
         auto& group = realm2->read_group();
         auto& group = realm2->read_group();
         realm2->begin_transaction();
         realm2->begin_transaction();
         auto table = ObjectStore::table_for_object_type(group, "object");
         auto table = ObjectStore::table_for_object_type(group, "object");
-        table->insert_column(0, type_Double, "newcol");
+        auto col_keys = table->get_column_keys();
+        table->add_column(type_Double, "newcol");
         realm2->commit_transaction();
         realm2->commit_transaction();
 
 
         REQUIRE_NOTHROW(realm->refresh());
         REQUIRE_NOTHROW(realm->refresh());
         REQUIRE(realm->schema() == schema);
         REQUIRE(realm->schema() == schema);
-        REQUIRE(realm->schema().find("object")->persisted_properties[0].table_column == 1);
-        REQUIRE(realm->schema().find("object")->persisted_properties[1].table_column == 2);
+        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
         // Gets the schema from the RealmCoordinator
         auto realm3 = Realm::get_shared_realm(config);
         auto realm3 = Realm::get_shared_realm(config);
-        REQUIRE(realm3->schema().find("object")->persisted_properties[0].table_column == 1);
-        REQUIRE(realm3->schema().find("object")->persisted_properties[1].table_column == 2);
+        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
         // Close and re-open the file entirely so that the coordinator is recreated
         realm.reset();
         realm.reset();
@@ -1624,8 +1600,8 @@ TEST_CASE("migration: Additive") {
 
 
         realm = Realm::get_shared_realm(config);
         realm = Realm::get_shared_realm(config);
         REQUIRE(realm->schema() == schema);
         REQUIRE(realm->schema() == schema);
-        REQUIRE(realm->schema().find("object")->persisted_properties[0].table_column == 1);
-        REQUIRE(realm->schema().find("object")->persisted_properties[1].table_column == 2);
+        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") {
     SECTION("can have different subsets of columns in different Realm instances") {
@@ -1659,13 +1635,14 @@ TEST_CASE("migration: Additive") {
         config2.schema = add_property(schema, "object",
         config2.schema = add_property(schema, "object",
                                       {"value 3", PropertyType::Int});
                                       {"value 3", PropertyType::Int});
         auto realm2 = Realm::get_shared_realm(config2);
         auto realm2 = Realm::get_shared_realm(config2);
+        auto& properties2 = realm2->schema().find("object")->persisted_properties;
 
 
         REQUIRE_NOTHROW(realm->update_schema(*config2.schema));
         REQUIRE_NOTHROW(realm->update_schema(*config2.schema));
         REQUIRE(realm->schema().find("object")->persisted_properties.size() == 3);
         REQUIRE(realm->schema().find("object")->persisted_properties.size() == 3);
         auto& properties = realm->schema().find("object")->persisted_properties;
         auto& properties = realm->schema().find("object")->persisted_properties;
-        REQUIRE(properties[0].table_column == 0);
-        REQUIRE(properties[1].table_column == 1);
-        REQUIRE(properties[2].table_column == 2);
+        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") {
     SECTION("increasing schema version without modifying schema properly leaves the schema untouched") {
@@ -1739,6 +1716,7 @@ TEST_CASE("migration: Manual") {
         }}
         }}
     };
     };
     realm->update_schema(schema);
     realm->update_schema(schema);
+    auto col_keys = realm->read_group().get_table("class_object")->get_column_keys();
 
 
 #define REQUIRE_MIGRATION(schema, migration) do { \
 #define REQUIRE_MIGRATION(schema, migration) do { \
     Schema new_schema = (schema); \
     Schema new_schema = (schema); \
@@ -1759,91 +1737,92 @@ TEST_CASE("migration: Manual") {
     }
     }
     SECTION("add property to table") {
     SECTION("add property to table") {
         REQUIRE_MIGRATION(add_property(schema, "object", {"new", PropertyType::Int}),
         REQUIRE_MIGRATION(add_property(schema, "object", {"new", PropertyType::Int}),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               get_table(realm, "object")->add_column(type_Int, "new");
                               get_table(realm, "object")->add_column(type_Int, "new");
                           });
                           });
     }
     }
     SECTION("remove property from table") {
     SECTION("remove property from table") {
         REQUIRE_MIGRATION(remove_property(schema, "object", "value"),
         REQUIRE_MIGRATION(remove_property(schema, "object", "value"),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
-                              get_table(realm, "object")->remove_column(1);
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
+                              get_table(realm, "object")->remove_column(col_keys[1]);
                           });
                           });
     }
     }
     SECTION("add primary key to table") {
     SECTION("add primary key to table") {
         REQUIRE_MIGRATION(set_primary_key(schema, "link origin", "not a pk"),
         REQUIRE_MIGRATION(set_primary_key(schema, "link origin", "not a pk"),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "link origin", "not a pk");
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "link origin", "not a pk");
-                              get_table(realm, "link origin")->add_search_index(0);
+                              auto table = get_table(realm, "link origin");
+                              table->add_search_index(table->get_column_key("not a pk"));
                           });
                           });
     }
     }
     SECTION("remove primary key from table") {
     SECTION("remove primary key from table") {
         REQUIRE_MIGRATION(set_primary_key(schema, "object", ""),
         REQUIRE_MIGRATION(set_primary_key(schema, "object", ""),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "");
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "");
-                              get_table(realm, "object")->remove_search_index(0);
+                              get_table(realm, "object")->remove_search_index(col_keys[0]);
                           });
                           });
     }
     }
     SECTION("change primary key") {
     SECTION("change primary key") {
         REQUIRE_MIGRATION(set_primary_key(schema, "object", "value"),
         REQUIRE_MIGRATION(set_primary_key(schema, "object", "value"),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "value");
                               ObjectStore::set_primary_key_for_object(realm->read_group(), "object", "value");
                               auto table = get_table(realm, "object");
                               auto table = get_table(realm, "object");
-                              table->remove_search_index(0);
-                              table->add_search_index(1);
+                              table->remove_search_index(col_keys[0]);
+                              table->add_search_index(col_keys[1]);
                           });
                           });
     }
     }
     SECTION("change property type") {
     SECTION("change property type") {
         REQUIRE_MIGRATION(set_type(schema, "object", "value", PropertyType::Date),
         REQUIRE_MIGRATION(set_type(schema, "object", "value", PropertyType::Date),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               auto table = get_table(realm, "object");
                               auto table = get_table(realm, "object");
-                              table->remove_column(1);
-                              size_t col = table->add_column(type_Timestamp, "value");
+                              table->remove_column(col_keys[1]);
+                              auto col = table->add_column(type_Timestamp, "value");
                               table->add_search_index(col);
                               table->add_search_index(col);
                           });
                           });
     }
     }
     SECTION("change link target") {
     SECTION("change link target") {
         REQUIRE_MIGRATION(set_target(schema, "link origin", "object", "link origin"),
         REQUIRE_MIGRATION(set_target(schema, "link origin", "object", "link origin"),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               auto table = get_table(realm, "link origin");
                               auto table = get_table(realm, "link origin");
-                              table->remove_column(1);
+                              table->remove_column(table->get_column_keys()[1]);
                               table->add_column_link(type_Link, "object", *table);
                               table->add_column_link(type_Link, "object", *table);
                           });
                           });
     }
     }
     SECTION("change linklist target") {
     SECTION("change linklist target") {
         REQUIRE_MIGRATION(set_target(schema, "link origin", "array", "link origin"),
         REQUIRE_MIGRATION(set_target(schema, "link origin", "array", "link origin"),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               auto table = get_table(realm, "link origin");
                               auto table = get_table(realm, "link origin");
-                              table->remove_column(2);
+                              table->remove_column(table->get_column_keys()[2]);
                               table->add_column_link(type_LinkList, "array", *table);
                               table->add_column_link(type_LinkList, "array", *table);
                           });
                           });
     }
     }
     SECTION("make property optional") {
     SECTION("make property optional") {
         REQUIRE_MIGRATION(set_optional(schema, "object", "value", true),
         REQUIRE_MIGRATION(set_optional(schema, "object", "value", true),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               auto table = get_table(realm, "object");
                               auto table = get_table(realm, "object");
-                              table->remove_column(1);
-                              size_t col = table->add_column(type_Int, "value", true);
+                              table->remove_column(col_keys[1]);
+                              auto col = table->add_column(type_Int, "value", true);
                               table->add_search_index(col);
                               table->add_search_index(col);
                           });
                           });
     }
     }
     SECTION("make property required") {
     SECTION("make property required") {
         REQUIRE_MIGRATION(set_optional(schema, "object", "optional", false),
         REQUIRE_MIGRATION(set_optional(schema, "object", "optional", false),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
                               auto table = get_table(realm, "object");
                               auto table = get_table(realm, "object");
-                              table->remove_column(2);
+                              table->remove_column(col_keys[2]);
                               table->add_column(type_Int, "optional", false);
                               table->add_column(type_Int, "optional", false);
                           });
                           });
     }
     }
     SECTION("add index") {
     SECTION("add index") {
         REQUIRE_MIGRATION(set_indexed(schema, "object", "optional", true),
         REQUIRE_MIGRATION(set_indexed(schema, "object", "optional", true),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
-                              get_table(realm, "object")->add_search_index(2);
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
+                              get_table(realm, "object")->add_search_index(col_keys[2]);
                           });
                           });
     }
     }
     SECTION("remove index") {
     SECTION("remove index") {
         REQUIRE_MIGRATION(set_indexed(schema, "object", "value", false),
         REQUIRE_MIGRATION(set_indexed(schema, "object", "value", false),
-                          [](SharedRealm, SharedRealm realm, Schema&) {
-                              get_table(realm, "object")->remove_search_index(1);
+                          [&](SharedRealm, SharedRealm realm, Schema&) {
+                              get_table(realm, "object")->remove_search_index(col_keys[1]);
                           });
                           });
     }
     }
     SECTION("reorder properties") {
     SECTION("reorder properties") {

+ 0 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/notifications-fuzzer/fuzzer.cpp

@@ -243,7 +243,6 @@ int main(int argc, char** argv) {
 
 
     Realm::Config config;
     Realm::Config config;
     config.path = "fuzzer.realm";
     config.path = "fuzzer.realm";
-    config.cache = false;
     config.in_memory = true;
     config.in_memory = true;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
 
 

+ 133 - 123
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp

@@ -31,7 +31,7 @@
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "impl/object_accessor_impl.hpp"
 #include "impl/object_accessor_impl.hpp"
 
 
-#include <realm/group_shared.hpp>
+#include <realm/group.hpp>
 #include <realm/util/any.hpp>
 #include <realm/util/any.hpp>
 
 
 #include <cstdint>
 #include <cstdint>
@@ -41,6 +41,11 @@ using namespace realm;
 namespace {
 namespace {
 using AnyDict = std::map<std::string, util::Any>;
 using AnyDict = std::map<std::string, util::Any>;
 using AnyVec = std::vector<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 {
 struct TestContext : CppContext {
@@ -76,7 +81,6 @@ TEST_CASE("object") {
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
-    config.cache = false;
     config.schema = Schema{
     config.schema = Schema{
         {"table", {
         {"table", {
             {"value 1", PropertyType::Int},
             {"value 1", PropertyType::Int},
@@ -151,22 +155,22 @@ TEST_CASE("object") {
     };
     };
     config.schema_version = 0;
     config.schema_version = 0;
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
-    auto& coordinator = *_impl::RealmCoordinator::get_existing_coordinator(config.path);
+    auto& coordinator = *_impl::RealmCoordinator::get_coordinator(config.path);
 
 
     SECTION("add_notification_callback()") {
     SECTION("add_notification_callback()") {
         auto table = r->read_group().get_table("class_table");
         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();
         r->begin_transaction();
-
-        table->add_empty_row(10);
         for (int i = 0; i < 10; ++i)
         for (int i = 0; i < 10; ++i)
-            table->set_int(0, i, i);
+            table->create_object(object_keys[i]).set_all(i);
         r->commit_transaction();
         r->commit_transaction();
 
 
         auto r2 = coordinator.get_realm();
         auto r2 = coordinator.get_realm();
 
 
         CollectionChangeSet change;
         CollectionChangeSet change;
-        Row row = table->get(0);
-        Object object(r, *r->schema().find("table"), row);
+        auto obj = *table->begin();
+        Object object(r, obj);
 
 
         auto write = [&](auto&& f) {
         auto write = [&](auto&& f) {
             r->begin_transaction();
             r->begin_transaction();
@@ -196,67 +200,65 @@ TEST_CASE("object") {
 
 
         SECTION("deleting the object sends a change notification") {
         SECTION("deleting the object sends a change notification") {
             auto token = require_change();
             auto token = require_change();
-            write([&] { row.move_last_over(); });
+            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);
             REQUIRE_INDICES(change.deletions, 0);
         }
         }
 
 
         SECTION("modifying the object sends a change notification") {
         SECTION("modifying the object sends a change notification") {
             auto token = require_change();
             auto token = require_change();
 
 
-            write([&] { row.set_int(0, 10); });
+            write([&] { obj.set(col_keys[0], 10); });
             REQUIRE_INDICES(change.modifications, 0);
             REQUIRE_INDICES(change.modifications, 0);
             REQUIRE(change.columns.size() == 1);
             REQUIRE(change.columns.size() == 1);
-            REQUIRE_INDICES(change.columns[0], 0);
+            REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
 
 
-            write([&] { row.set_int(1, 10); });
+            write([&] { obj.set(col_keys[1], 10); });
             REQUIRE_INDICES(change.modifications, 0);
             REQUIRE_INDICES(change.modifications, 0);
-            REQUIRE(change.columns.size() == 2);
-            REQUIRE(change.columns[0].empty());
-            REQUIRE_INDICES(change.columns[1], 0);
+            REQUIRE(change.columns.size() == 1);
+            REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
         }
         }
 
 
         SECTION("modifying a different object") {
         SECTION("modifying a different object") {
             auto token = require_no_change();
             auto token = require_no_change();
-            write([&] { table->get(1).set_int(0, 10); });
-        }
-
-        SECTION("moving the object") {
-            auto token = require_no_change();
-            write([&] { table->swap_rows(0, 5); });
-        }
-
-        SECTION("subsuming the object") {
-            auto token = require_change();
-            write([&] {
-                table->insert_empty_row(0);
-                table->merge_rows(row.get_index(), 0);
-                row.set_int(0, 10);
-            });
-            REQUIRE(change.columns.size() == 1);
-            REQUIRE_INDICES(change.columns[0], 0);
+            write([&] { table->get_object(1).set(col_keys[0], 10); });
         }
         }
 
 
         SECTION("multiple write transactions") {
         SECTION("multiple write transactions") {
             auto token = require_change();
             auto token = require_change();
 
 
-            auto r2row = r2->read_group().get_table("class_table")->get(0);
+            auto r2row = r2->read_group().get_table("class_table")->get_object(0);
             r2->begin_transaction();
             r2->begin_transaction();
-            r2row.set_int(0, 1);
+            r2row.set(col_keys[0], 1);
             r2->commit_transaction();
             r2->commit_transaction();
             r2->begin_transaction();
             r2->begin_transaction();
-            r2row.set_int(1, 2);
+            r2row.set(col_keys[1], 2);
             r2->commit_transaction();
             r2->commit_transaction();
 
 
             advance_and_notify(*r);
             advance_and_notify(*r);
             REQUIRE(change.columns.size() == 2);
             REQUIRE(change.columns.size() == 2);
-            REQUIRE_INDICES(change.columns[0], 0);
-            REQUIRE_INDICES(change.columns[1], 0);
+            REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
+            REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
         }
         }
 
 
         SECTION("skipping a notification") {
         SECTION("skipping a notification") {
             auto token = require_no_change();
             auto token = require_no_change();
             write([&] {
             write([&] {
-                row.set_int(0, 1);
+                obj.set(col_keys[0], 1);
                 token.suppress_next();
                 token.suppress_next();
             });
             });
         }
         }
@@ -271,9 +273,7 @@ TEST_CASE("object") {
             REQUIRE(change.empty());
             REQUIRE(change.empty());
 
 
             // should now produce a notification
             // should now produce a notification
-            write([&] {
-                row.set_int(0, 1);
-            });
+            write([&] { obj.set(col_keys[0], 1); });
             REQUIRE_INDICES(change.modifications, 0);
             REQUIRE_INDICES(change.modifications, 0);
         }
         }
 
 
@@ -284,13 +284,13 @@ TEST_CASE("object") {
                 });
                 });
             }
             }
             auto token = require_change();
             auto token = require_change();
-            write([&] { row.move_last_over(); });
+            write([&] { obj.remove(); });
             REQUIRE_INDICES(change.deletions, 0);
             REQUIRE_INDICES(change.deletions, 0);
         }
         }
 
 
         SECTION("observing deleted object throws") {
         SECTION("observing deleted object throws") {
             write([&] {
             write([&] {
-                row.move_last_over();
+                obj.remove();
             });
             });
             REQUIRE_THROWS(require_change());
             REQUIRE_THROWS(require_change());
         }
         }
@@ -334,44 +334,50 @@ TEST_CASE("object") {
             {"double array", AnyVec{3.3, 4.4}},
             {"double array", AnyVec{3.3, 4.4}},
             {"string array", AnyVec{"a"s, "b"s, "c"s}},
             {"string array", AnyVec{"a"s, "b"s, "c"s}},
             {"data array", AnyVec{"d"s, "e"s, "f"s}},
             {"data array", AnyVec{"d"s, "e"s, "f"s}},
-            {"date array", AnyVec{}},
+            {"date array", AnyVec{Timestamp(10, 20), Timestamp(30, 40)}},
             {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
             {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
         });
         });
 
 
-        auto row = obj.row();
-        REQUIRE(row.get_int(0) == 1);
-        REQUIRE(row.get_bool(1) == true);
-        REQUIRE(row.get_int(2) == 5);
-        REQUIRE(row.get_float(3) == 2.2f);
-        REQUIRE(row.get_double(4) == 3.3);
-        REQUIRE(row.get_string(5) == "hello");
-        REQUIRE(row.get_binary(6) == BinaryData("olleh", 5));
-        REQUIRE(row.get_timestamp(7) == Timestamp(10, 20));
-        REQUIRE(row.get_link(8) == 0);
-
-        auto link_target = r->read_group().get_table("class_link target")->get(0);
-        REQUIRE(link_target.get_int(0) == 10);
-
-        auto check_array = [&](size_t col, auto... values) {
-            auto table = row.get_subtable(col);
+        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;
             size_t i = 0;
-            for (auto& value : {values...}) {
+            for (const auto& value : vec) {
                 CAPTURE(i);
                 CAPTURE(i);
-                REQUIRE(i < row.get_subtable_size(col));
-                REQUIRE(value == table->get<typename std::decay<decltype(value)>::type>(0, i));
+                REQUIRE(i < list.size());
+                REQUIRE(value == list.get(i));
                 ++i;
                 ++i;
             }
             }
         };
         };
-        check_array(9, true, false);
-        check_array(10, INT64_C(5), INT64_C(6));
-        check_array(11, 1.1f, 2.2f);
-        check_array(12, 3.3, 4.4);
-        check_array(13, StringData("a"), StringData("b"), StringData("c"));
-        check_array(14, BinaryData("d", 1), BinaryData("e", 1), BinaryData("f", 1));
-
-        auto list = row.get_linklist(16);
+        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->size() == 1);
-        REQUIRE(list->get(0).get_int(0) == 20);
+        REQUIRE(list->get_object(0).get<Int>(array_target_table->get_column_key("value")) == 20);
     }
     }
 
 
     SECTION("create uses defaults for missing values") {
     SECTION("create uses defaults for missing values") {
@@ -400,24 +406,25 @@ TEST_CASE("object") {
             {"float", 6.6f},
             {"float", 6.6f},
         });
         });
 
 
-        auto row = obj.row();
-        REQUIRE(row.get_int(0) == 1);
-        REQUIRE(row.get_bool(1) == true);
-        REQUIRE(row.get_int(2) == 5);
-        REQUIRE(row.get_float(3) == 6.6f);
-        REQUIRE(row.get_double(4) == 3.3);
-        REQUIRE(row.get_string(5) == "hello");
-        REQUIRE(row.get_binary(6) == BinaryData("olleh", 5));
-        REQUIRE(row.get_timestamp(7) == Timestamp(10, 20));
-
-        REQUIRE(row.get_subtable(9)->size() == 2);
-        REQUIRE(row.get_subtable(10)->size() == 2);
-        REQUIRE(row.get_subtable(11)->size() == 2);
-        REQUIRE(row.get_subtable(12)->size() == 2);
-        REQUIRE(row.get_subtable(13)->size() == 3);
-        REQUIRE(row.get_subtable(14)->size() == 3);
-        REQUIRE(row.get_subtable(15)->size() == 0);
-        REQUIRE(row.get_linklist(16)->size() == 1);
+        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") {
     SECTION("create can use defaults for primary key") {
@@ -436,8 +443,8 @@ TEST_CASE("object") {
             {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
             {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
         });
         });
 
 
-        auto row = obj.row();
-        REQUIRE(row.get_int(0) == 10);
+        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") {
     SECTION("create does not complain about missing values for nullable fields") {
@@ -525,15 +532,16 @@ TEST_CASE("object") {
         REQUIRE(callback_called);
         REQUIRE(callback_called);
         REQUIRE_INDICES(change.modifications, 0);
         REQUIRE_INDICES(change.modifications, 0);
 
 
-        auto row = obj.row();
-        REQUIRE(row.get_int(0) == 1);
-        REQUIRE(row.get_bool(1) == true);
-        REQUIRE(row.get_int(2) == 6);
-        REQUIRE(row.get_float(3) == 2.2f);
-        REQUIRE(row.get_double(4) == 3.3);
-        REQUIRE(row.get_string(5) == "a");
-        REQUIRE(row.get_binary(6) == BinaryData("olleh", 5));
-        REQUIRE(row.get_timestamp(7) == Timestamp(10, 20));
+        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") {
     SECTION("create with update - only with diffs") {
@@ -567,7 +575,7 @@ TEST_CASE("object") {
 
 
         auto table = r->read_group().get_table("class_person");
         auto table = r->read_group().get_table("class_person");
         REQUIRE(table->size() == 5);
         REQUIRE(table->size() == 5);
-        Results result(r, *table);
+        Results result(r, table);
         auto token = result.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
         auto token = result.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
             change = c;
             change = c;
             callback_called = true;
             callback_called = true;
@@ -629,7 +637,7 @@ TEST_CASE("object") {
         });
         });
 
 
         auto obj_table = r->read_group().get_table("class_all types");
         auto obj_table = r->read_group().get_table("class_all types");
-        Results result(r, *obj_table);
+        Results result(r, obj_table);
         bool callback_called;
         bool callback_called;
         bool results_callback_called;
         bool results_callback_called;
         bool sub_callback_called;
         bool sub_callback_called;
@@ -706,7 +714,7 @@ TEST_CASE("object") {
         Object obj = create(dict);
         Object obj = create(dict);
 
 
         auto obj_table = r->read_group().get_table("class_all types");
         auto obj_table = r->read_group().get_table("class_all types");
-        Results result(r, *obj_table);
+        Results result(r, obj_table);
         auto token1 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
         auto token1 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
             callback_called = true;
             callback_called = true;
         });
         });
@@ -866,26 +874,28 @@ TEST_CASE("object") {
         };
         };
 
 
         auto obj = create(AnyDict{{"pk", d.null_value()}}, "nullable int pk");
         auto obj = create(AnyDict{{"pk", d.null_value()}}, "nullable int pk");
-        REQUIRE(obj.row().is_null(0));
+        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");
         obj = create(AnyDict{{"pk", d.null_value()}}, "nullable string pk");
-        REQUIRE(obj.row().is_null(0));
+        REQUIRE(obj.obj().is_null(col_pk_str));
 
 
         obj = create(AnyDict{{}}, "nullable int pk");
         obj = create(AnyDict{{}}, "nullable int pk");
-        REQUIRE(obj.row().get_int(0) == 10);
+        REQUIRE(obj.obj().get<util::Optional<Int>>(col_pk_int) == 10);
         obj = create(AnyDict{{}}, "nullable string pk");
         obj = create(AnyDict{{}}, "nullable string pk");
-        REQUIRE(obj.row().get_string(0) == "value");
+        REQUIRE(obj.obj().get<String>(col_pk_str) == "value");
     }
     }
 
 
     SECTION("getters and setters") {
     SECTION("getters and setters") {
         r->begin_transaction();
         r->begin_transaction();
 
 
-        auto& table = *r->read_group().get_table("class_all types");
-        table.add_empty_row();
-        Object obj(r, *r->schema().find("all types"), table[0]);
+        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.add_empty_row();
-        Object linkobj(r, *r->schema().find("link target"), link_table[0]);
+        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));
         obj.set_property_value(d, "bool", util::Any(true));
         REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
         REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
@@ -910,7 +920,7 @@ TEST_CASE("object") {
 
 
         REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "object").has_value());
         REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "object").has_value());
         obj.set_property_value(d, "object", util::Any(linkobj));
         obj.set_property_value(d, "object", util::Any(linkobj));
-        REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(d, "object")).row().get_index() == linkobj.row().get_index());
+        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"));
         auto linking = any_cast<Results>(linkobj.get_property_value<util::Any>(d, "origin"));
         REQUIRE(linking.size() == 1);
         REQUIRE(linking.size() == 1);
@@ -985,8 +995,8 @@ TEST_CASE("object") {
 
 
         r1->begin_transaction();
         r1->begin_transaction();
         r2->begin_transaction();
         r2->begin_transaction();
-        auto obj = Object::create(c1, r1, *r1->schema().find("pk after list"), util::Any(v1));
-        Object::create(c2, r2, *r2->schema().find("pk after list"), util::Any(v2));
+        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();
         r2->commit_transaction();
         r1->commit_transaction();
         r1->commit_transaction();
 
 
@@ -995,12 +1005,12 @@ TEST_CASE("object") {
             return r1->read_group().get_table("class_array target")->size() == 4;
             return r1->read_group().get_table("class_array target")->size() == 4;
         });
         });
 
 
-        // With stable IDs, sync creates the primary key column at index 0.
-        REQUIRE(obj.row().get_int(0) == 7); // pk
-        REQUIRE(obj.row().get_linklist(1)->size() == 2);
-        REQUIRE(obj.row().get_int(2) == 1); // non-default from r1
-        REQUIRE(obj.row().get_int(3) == 2); // non-default from r2
-        REQUIRE(obj.row().get_linklist(4)->size() == 2);
+        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
 #endif

+ 3 - 14
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object_store.cpp

@@ -26,11 +26,11 @@
 #include "schema.hpp"
 #include "schema.hpp"
 
 
 #include <realm/string_data.hpp>
 #include <realm/string_data.hpp>
+#include <realm/table.hpp>
 
 
 using namespace realm;
 using namespace realm;
 
 
 TEST_CASE("ObjectStore: table_name_for_object_type()") {
 TEST_CASE("ObjectStore: table_name_for_object_type()") {
-
     SECTION("should work with strings that aren't null-terminated") {
     SECTION("should work with strings that aren't null-terminated") {
         auto input = StringData("good_no_bad", 4);
         auto input = StringData("good_no_bad", 4);
         auto result = ObjectStore::table_name_for_object_type(input);
         auto result = ObjectStore::table_name_for_object_type(input);
@@ -63,25 +63,14 @@ TEST_CASE("ObjectStore:: property_for_column_index()") {
         REQUIRE_FALSE(it == realm->schema().end());
         REQUIRE_FALSE(it == realm->schema().end());
         ObjectSchema object_schema = *it;
         ObjectSchema object_schema = *it;
 
 
-        size_t count = table->get_column_count();
-        for (size_t col = 0; col < count; col++) {
+        auto all_columns = table->get_column_keys();
+        for (auto col : all_columns) {
             auto property = ObjectStore::property_for_column_index(table, col);
             auto property = ObjectStore::property_for_column_index(table, col);
             if (!property) {
             if (!property) {
-#if REALM_ENABLE_SYNC
-                REQUIRE(table->get_column_name(col) == sync::object_id_column_name);
-#else
                 FAIL();
                 FAIL();
-#endif
                 continue;
                 continue;
             }
             }
             auto actual_property = *object_schema.property_for_name(property->name);
             auto actual_property = *object_schema.property_for_name(property->name);
-
-            // property_for_column_index won't read the pk info, but it will set the is_index to true for pk.
-            // Property could be created with is_indexed = false , is_primary = true.
-            if (actual_property.is_primary) {
-                actual_property.is_primary = false;
-                actual_property.is_indexed = true;
-            }
             REQUIRE(property.value() == actual_property);
             REQUIRE(property.value() == actual_property);
         }
         }
    }
    }

+ 102 - 82
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/primitive_list.cpp

@@ -34,8 +34,7 @@
 #include "impl/realm_coordinator.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "impl/object_accessor_impl.hpp"
 #include "impl/object_accessor_impl.hpp"
 
 
-#include <realm/group_shared.hpp>
-#include <realm/link_view.hpp>
+#include <realm/db.hpp>
 #include <realm/query_expression.hpp>
 #include <realm/query_expression.hpp>
 #include <realm/version.hpp>
 #include <realm/version.hpp>
 
 
@@ -43,11 +42,13 @@
 
 
 using namespace realm;
 using namespace realm;
 
 
+
 template<PropertyType prop_type, typename T>
 template<PropertyType prop_type, typename T>
 struct Base {
 struct Base {
     using Type = T;
     using Type = T;
     using Wrapped = T;
     using Wrapped = T;
     using Boxed = T;
     using Boxed = T;
+    enum { is_optional = false };
 
 
     static PropertyType property_type() { return prop_type; }
     static PropertyType property_type() { return prop_type; }
     static util::Any to_any(T value) { return value; }
     static util::Any to_any(T value) { return value; }
@@ -104,21 +105,29 @@ struct String : Base<PropertyType::String, StringData> {
 
 
 struct Binary : Base<PropertyType::Data, BinaryData> {
 struct Binary : Base<PropertyType::Data, BinaryData> {
     using Boxed = std::string;
     using Boxed = std::string;
-    static std::vector<BinaryData> values() { return {BinaryData("a", 1)}; }
     static util::Any to_any(BinaryData value) { return value ? std::string(value) : util::Any(); }
     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> {
 struct Date : Base<PropertyType::Date, Timestamp> {
-    static std::vector<Timestamp> values() { return {Timestamp(1, 1)}; }
+    static std::vector<Timestamp> values()
+    {
+        return {Timestamp(3, 3), Timestamp(1, 1), Timestamp(2, 2)};
+    }
     static bool can_minmax() { return true; }
     static bool can_minmax() { return true; }
     static Timestamp min() { return Timestamp(1, 1); }
     static Timestamp min() { return Timestamp(1, 1); }
-    static Timestamp max() { return Timestamp(1, 1); }
+    static Timestamp max() { return Timestamp(3, 3); }
 };
 };
 
 
 template<typename BaseT>
 template<typename BaseT>
 struct BoxedOptional : BaseT {
 struct BoxedOptional : BaseT {
     using Type = util::Optional<typename BaseT::Type>;
     using Type = util::Optional<typename BaseT::Type>;
     using Boxed = Type;
     using Boxed = Type;
+    enum { is_optional = true };
+
     static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
     static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
     static std::vector<Type> values()
     static std::vector<Type> values()
     {
     {
@@ -137,6 +146,7 @@ struct BoxedOptional : BaseT {
 
 
 template<typename BaseT>
 template<typename BaseT>
 struct UnboxedOptional : BaseT {
 struct UnboxedOptional : BaseT {
+    enum { is_optional = true };
     static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
     static PropertyType property_type() { return BaseT::property_type()|PropertyType::Nullable; }
     static auto values() -> decltype(BaseT::values())
     static auto values() -> decltype(BaseT::values())
     {
     {
@@ -190,7 +200,7 @@ struct StringifyingContext {
         return ss.str();
         return ss.str();
     }
     }
 
 
-    std::string box(RowExpr row) { return util::to_string(row.get_index()); }
+    std::string box(Obj obj) { return util::to_string(obj.get_key().value); }
 };
 };
 
 
 namespace Catch {
 namespace Catch {
@@ -275,7 +285,8 @@ auto greater::operator()<Timestamp&, Timestamp&>(Timestamp& a, Timestamp& b) con
 
 
 TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::Double, ::String, ::Binary, ::Date,
 TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::Double, ::String, ::Binary, ::Date,
                    BoxedOptional<::Int>, BoxedOptional<::Bool>, BoxedOptional<::Float>, BoxedOptional<::Double>,
                    BoxedOptional<::Int>, BoxedOptional<::Bool>, BoxedOptional<::Float>, BoxedOptional<::Double>,
-                   UnboxedOptional<::String>, UnboxedOptional<::Binary>, UnboxedOptional<::Date>) {
+                   UnboxedOptional<::String>, UnboxedOptional<::Binary>, UnboxedOptional<::Date>)
+{
     auto values = TestType::values();
     auto values = TestType::values();
     using T = typename TestType::Type;
     using T = typename TestType::Type;
     using W = typename TestType::Wrapped;
     using W = typename TestType::Wrapped;
@@ -283,7 +294,6 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
-    config.cache = false;
     config.schema = Schema{
     config.schema = Schema{
         {"object", {
         {"object", {
             {"value", PropertyType::Array|TestType::property_type()}
             {"value", PropertyType::Array|TestType::property_type()}
@@ -295,9 +305,10 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
     auto table = r->read_group().get_table("class_object");
     auto table = r->read_group().get_table("class_object");
     auto table2 = r2->read_group().get_table("class_object");
     auto table2 = r2->read_group().get_table("class_object");
     r->begin_transaction();
     r->begin_transaction();
-    table->add_empty_row();
+    Obj obj = table->create_object();
+    ColKey col = table->get_column_key("value");
 
 
-    List list(r, *table, 0, 0);
+    List list(r, obj, col);
     auto results = list.as_results();
     auto results = list.as_results();
     CppContext ctx(r);
     CppContext ctx(r);
 
 
@@ -305,7 +316,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         REQUIRE(list.get_realm() == r);
         REQUIRE(list.get_realm() == r);
         REQUIRE(results.get_realm() == r);
         REQUIRE(results.get_realm() == r);
     }
     }
-
+#if 0
     SECTION("get_query()") {
     SECTION("get_query()") {
         REQUIRE(list.get_query().count() == 0);
         REQUIRE(list.get_query().count() == 0);
         REQUIRE(results.get_query().count() == 0);
         REQUIRE(results.get_query().count() == 0);
@@ -313,11 +324,11 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         REQUIRE(list.get_query().count() == 1);
         REQUIRE(list.get_query().count() == 1);
         REQUIRE(results.get_query().count() == 1);
         REQUIRE(results.get_query().count() == 1);
     }
     }
-
+#endif
     SECTION("get_origin_row_index()") {
     SECTION("get_origin_row_index()") {
-        REQUIRE(list.get_origin_row_index() == 0);
-        table->insert_empty_row(0);
-        REQUIRE(list.get_origin_row_index() == 1);
+        REQUIRE(list.get_parent_object_key() == obj.get_key());
+        table->create_object();
+        REQUIRE(list.get_parent_object_key() == obj.get_key());
     }
     }
 
 
     SECTION("get_type()") {
     SECTION("get_type()") {
@@ -346,7 +357,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         }
         }
 
 
         SECTION("delete row") {
         SECTION("delete row") {
-            table->move_last_over(0);
+            obj.remove();
             REQUIRE_FALSE(list.is_valid());
             REQUIRE_FALSE(list.is_valid());
             REQUIRE_FALSE(results.is_valid());
             REQUIRE_FALSE(results.is_valid());
         }
         }
@@ -372,7 +383,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         }
         }
 
 
         SECTION("delete row") {
         SECTION("delete row") {
-            table->move_last_over(0);
+            obj.remove();
             REQUIRE_THROWS(list.verify_attached());
             REQUIRE_THROWS(list.verify_attached());
         }
         }
 
 
@@ -396,7 +407,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         }
         }
 
 
         SECTION("delete row") {
         SECTION("delete row") {
-            table->move_last_over(0);
+            obj.remove();
             REQUIRE_THROWS(list.verify_in_transaction());
             REQUIRE_THROWS(list.verify_in_transaction());
         }
         }
 
 
@@ -521,18 +532,18 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
     }
     }
 
 
     SECTION("find()") {
     SECTION("find()") {
-        // cast to T needed for vector<bool>'s wonky proxy
         for (size_t i = 0; i < values.size(); ++i) {
         for (size_t i = 0; i < values.size(); ++i) {
             CAPTURE(i);
             CAPTURE(i);
-            REQUIRE(list.find(static_cast<T>(values[i])) == i);
-            REQUIRE(results.index_of(static_cast<T>(values[i])) == 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(list.find(ctx, TestType::to_any(values[i])) == i);
             REQUIRE(results.index_of(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; });
             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(list.find(Query(q)) == i);
             REQUIRE(results.index_of(std::move(q)) == i);
             REQUIRE(results.index_of(std::move(q)) == i);
+#endif
         }
         }
 
 
         list.remove(0);
         list.remove(0);
@@ -542,27 +553,23 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         REQUIRE(list.find(ctx, TestType::to_any(values[0])) == npos);
         REQUIRE(list.find(ctx, TestType::to_any(values[0])) == npos);
         REQUIRE(results.index_of(ctx, TestType::to_any(values[0])) == npos);
         REQUIRE(results.index_of(ctx, TestType::to_any(values[0])) == npos);
     }
     }
-
     SECTION("sorted index_of()") {
     SECTION("sorted index_of()") {
-        auto subtable = table->get_subtable(0, 0);
-
         auto sorted = list.sort({{"self", true}});
         auto sorted = list.sort({{"self", true}});
         std::sort(begin(values), end(values), less());
         std::sort(begin(values), end(values), less());
         for (size_t i = 0; i < values.size(); ++i) {
         for (size_t i = 0; i < values.size(); ++i) {
             CAPTURE(i);
             CAPTURE(i);
-            auto q = TestType::unwrap(values[i], [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) == v; });
-            REQUIRE(sorted.index_of(std::move(q)) == i);
+            REQUIRE(sorted.index_of<T>(values[i]) == i);
         }
         }
 
 
         sorted = list.sort({{"self", false}});
         sorted = list.sort({{"self", false}});
         std::sort(begin(values), end(values), greater());
         std::sort(begin(values), end(values), greater());
         for (size_t i = 0; i < values.size(); ++i) {
         for (size_t i = 0; i < values.size(); ++i) {
             CAPTURE(i);
             CAPTURE(i);
-            auto q = TestType::unwrap(values[i], [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) == v; });
-            REQUIRE(sorted.index_of(std::move(q)) == i);
+            REQUIRE(sorted.index_of<T>(values[i]) == i);
         }
         }
     }
     }
 
 
+#if 0
     SECTION("filtered index_of()") {
     SECTION("filtered index_of()") {
         REQUIRE_THROWS(results.index_of(table->get(0)));
         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 q = TestType::unwrap(values[0], [&] (auto v) { return table->get_subtable(0, 0)->column<W>(0) != v; });
@@ -572,28 +579,27 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
             REQUIRE(filtered.index_of(static_cast<T>(values[i])) == i - 1);
             REQUIRE(filtered.index_of(static_cast<T>(values[i])) == i - 1);
         }
         }
     }
     }
-
+#endif
     SECTION("sort()") {
     SECTION("sort()") {
-        auto subtable = table->get_subtable(0, 0);
-
         auto unsorted = list.sort(std::vector<std::pair<std::string, bool>>{});
         auto unsorted = list.sort(std::vector<std::pair<std::string, bool>>{});
         REQUIRE(unsorted == values);
         REQUIRE(unsorted == values);
 
 
-        auto sorted = list.sort(SortDescriptor(*subtable, {{0}}, {true}));
+        auto sorted = list.sort(SortDescriptor({{col}}, {true}));
         auto sorted2 = list.sort({{"self", true}});
         auto sorted2 = list.sort({{"self", true}});
         std::sort(begin(values), end(values), less());
         std::sort(begin(values), end(values), less());
         REQUIRE(sorted == values);
         REQUIRE(sorted == values);
         REQUIRE(sorted2 == values);
         REQUIRE(sorted2 == values);
 
 
-        sorted = list.sort(SortDescriptor(*subtable, {{0}}, {false}));
+        sorted = list.sort(SortDescriptor({{col}}, {false}));
         sorted2 = list.sort({{"self", false}});
         sorted2 = list.sort({{"self", false}});
         std::sort(begin(values), end(values), greater());
         std::sort(begin(values), end(values), greater());
         REQUIRE(sorted == values);
         REQUIRE(sorted == values);
         REQUIRE(sorted2 == values);
         REQUIRE(sorted2 == values);
 
 
-        REQUIRE_THROWS_WITH(list.sort({{"not self", true}}),
-                            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)));
+        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}}),
         REQUIRE_THROWS_WITH(list.sort({{"self", true}, {"self", false}}),
                             util::format("Cannot sort array of '%1' on more than one key path",
                             util::format("Cannot sort array of '%1' on more than one key path",
                                          string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
                                          string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
@@ -605,12 +611,10 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         auto values2 = values;
         auto values2 = values;
         values2.insert(values2.end(), values.begin(), values.end());
         values2.insert(values2.end(), values.begin(), values.end());
 
 
-        auto subtable = table->get_subtable(0, 0);
-
         auto undistinct = list.as_results().distinct(std::vector<std::string>{});
         auto undistinct = list.as_results().distinct(std::vector<std::string>{});
         REQUIRE(undistinct == values2);
         REQUIRE(undistinct == values2);
 
 
-        auto distinct = results.distinct(SortDescriptor(*subtable, {{0}}, {true}));
+        auto distinct = results.distinct(DistinctDescriptor({{col}}));
         auto distinct2 = results.distinct({"self"});
         auto distinct2 = results.distinct({"self"});
         REQUIRE(distinct == values);
         REQUIRE(distinct == values);
         REQUIRE(distinct2 == values);
         REQUIRE(distinct2 == values);
@@ -623,6 +627,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
                                          string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
                                          string_for_property_type(TestType::property_type() & ~PropertyType::Flags)));
     }
     }
 
 
+#if 0
     SECTION("filter()") {
     SECTION("filter()") {
         T v = values.front();
         T v = values.front();
         values.erase(values.begin());
         values.erase(values.begin());
@@ -636,6 +641,7 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
         REQUIRE(filtered.size() == 1);
         REQUIRE(filtered.size() == 1);
         REQUIRE(*filtered.first<T>() == v);
         REQUIRE(*filtered.first<T>() == v);
     }
     }
+#endif
 
 
     SECTION("min()") {
     SECTION("min()") {
         if (!TestType::can_minmax()) {
         if (!TestType::can_minmax()) {
@@ -692,58 +698,82 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
     }
     }
 
 
     SECTION("operator==()") {
     SECTION("operator==()") {
-        table->add_empty_row();
-        REQUIRE(list == List(r, *table, 0, 0));
-        REQUIRE_FALSE(list == List(r, *table, 0, 1));
+        Obj obj1 = table->create_object();
+        REQUIRE(list == List(r, obj, col));
+        REQUIRE_FALSE(list == List(r, obj1, col));
     }
     }
 
 
     SECTION("hash") {
     SECTION("hash") {
-        table->add_empty_row();
+        Obj obj1 = table->create_object();
         std::hash<List> h;
         std::hash<List> h;
-        REQUIRE(h(list) == h(List(r, *table, 0, 0)));
-        REQUIRE_FALSE(h(list) == h(List(r, *table, 0, 1)));
+        REQUIRE(h(list) == h(List(r, obj, col)));
+        REQUIRE_FALSE(h(list) == h(List(r, obj1, col)));
     }
     }
 
 
     SECTION("handover") {
     SECTION("handover") {
         r->commit_transaction();
         r->commit_transaction();
 
 
-        auto handover = r->obtain_thread_safe_reference(list);
-        auto list2 = r->resolve_thread_safe_reference(std::move(handover));
+        auto list2 = ThreadSafeReference(list).resolve<List>(r);
         REQUIRE(list == list2);
         REQUIRE(list == list2);
-
-        auto results_handover = r->obtain_thread_safe_reference(results);
-        auto results2 = r->resolve_thread_safe_reference(std::move(results_handover));
+        auto results2 = ThreadSafeReference(results).resolve<Results>(r);
         REQUIRE(results2 == values);
         REQUIRE(results2 == values);
     }
     }
 
 
     SECTION("notifications") {
     SECTION("notifications") {
         r->commit_transaction();
         r->commit_transaction();
 
 
-        CollectionChangeSet change, rchange;
+        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") {
         SECTION("add value to list") {
-            auto token = list.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
-                change = c;
-            });
-            auto rtoken = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
-                rchange = c;
-            });
+            // 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);
             advance_and_notify(*r);
+            r->begin_transaction();
+            list.remove(0);
+            r->commit_transaction();
 
 
+            advance_and_notify(*r);
             r->begin_transaction();
             r->begin_transaction();
             list.insert(0, static_cast<T>(values[0]));
             list.insert(0, static_cast<T>(values[0]));
             r->commit_transaction();
             r->commit_transaction();
+
             advance_and_notify(*r);
             advance_and_notify(*r);
             REQUIRE_INDICES(change.insertions, 0);
             REQUIRE_INDICES(change.insertions, 0);
             REQUIRE_INDICES(rchange.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") {
         SECTION("clear list") {
-            auto token = list.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
-                change = c;
-            });
-            auto rtoken = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
-                rchange = c;
-            });
             advance_and_notify(*r);
             advance_and_notify(*r);
 
 
             r->begin_transaction();
             r->begin_transaction();
@@ -752,42 +782,32 @@ TEMPLATE_TEST_CASE("primitive list", "[primitives]", ::Int, ::Bool, ::Float, ::D
             advance_and_notify(*r);
             advance_and_notify(*r);
             REQUIRE(change.deletions.count() == values.size());
             REQUIRE(change.deletions.count() == values.size());
             REQUIRE(rchange.deletions.count() == values.size());
             REQUIRE(rchange.deletions.count() == values.size());
+            REQUIRE(srchange.deletions.count() == values.size());
         }
         }
 
 
         SECTION("delete containing row") {
         SECTION("delete containing row") {
-            size_t calls = 0;
-            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;
-            });
             advance_and_notify(*r);
             advance_and_notify(*r);
-            REQUIRE(calls == 2);
+            REQUIRE(calls == 3);
 
 
             r->begin_transaction();
             r->begin_transaction();
-            table->move_last_over(0);
+            obj.remove();
             r->commit_transaction();
             r->commit_transaction();
             advance_and_notify(*r);
             advance_and_notify(*r);
-            REQUIRE(calls == 4);
+            REQUIRE(calls == 6);
             REQUIRE(change.deletions.count() == values.size());
             REQUIRE(change.deletions.count() == values.size());
             REQUIRE(rchange.deletions.count() == values.size());
             REQUIRE(rchange.deletions.count() == values.size());
+            REQUIRE(srchange.deletions.count() == values.size());
 
 
             r->begin_transaction();
             r->begin_transaction();
-            table->add_empty_row();
+            table->create_object();
             r->commit_transaction();
             r->commit_transaction();
             advance_and_notify(*r);
             advance_and_notify(*r);
-            REQUIRE(calls == 4);
+            REQUIRE(calls == 6);
         }
         }
 
 
         SECTION("deleting containing row before first run of notifier") {
         SECTION("deleting containing row before first run of notifier") {
-            auto token = list.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
-                change = c;
-            });
             r2->begin_transaction();
             r2->begin_transaction();
-            table2->move_last_over(0);
+            table2->begin()->remove();
             r2->commit_transaction();
             r2->commit_transaction();
             advance_and_notify(*r);
             advance_and_notify(*r);
             REQUIRE(change.deletions.count() == values.size());
             REQUIRE(change.deletions.count() == values.size());

+ 129 - 187
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp

@@ -23,28 +23,29 @@
 #include "util/test_utils.hpp"
 #include "util/test_utils.hpp"
 
 
 #include "binding_context.hpp"
 #include "binding_context.hpp"
+#include "impl/realm_coordinator.hpp"
 #include "object_schema.hpp"
 #include "object_schema.hpp"
 #include "object_store.hpp"
 #include "object_store.hpp"
 #include "property.hpp"
 #include "property.hpp"
 #include "results.hpp"
 #include "results.hpp"
 #include "schema.hpp"
 #include "schema.hpp"
 #include "thread_safe_reference.hpp"
 #include "thread_safe_reference.hpp"
+#include "util/scheduler.hpp"
 
 
-#include "impl/realm_coordinator.hpp"
+#include <realm/db.hpp>
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
 #include "sync/async_open_task.hpp"
 #include "sync/async_open_task.hpp"
 #endif
 #endif
 
 
-#include <realm/group.hpp>
 #include <realm/util/scope_exit.hpp>
 #include <realm/util/scope_exit.hpp>
 
 
 namespace realm {
 namespace realm {
 class TestHelper {
 class TestHelper {
 public:
 public:
-    static SharedGroup& get_shared_group(SharedRealm const& shared_realm)
+    static DBRef& get_db(SharedRealm const& shared_realm)
     {
     {
-        return *Realm::Internal::get_shared_group(*shared_realm);
+        return Realm::Internal::get_db(*shared_realm);
     }
     }
 
 
     static void begin_read(SharedRealm const& shared_realm, VersionID version)
     static void begin_read(SharedRealm const& shared_realm, VersionID version)
@@ -65,19 +66,6 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
         }},
         }},
     };
     };
 
 
-    SECTION("should return the same instance when caching is enabled") {
-        auto realm1 = Realm::get_shared_realm(config);
-        auto realm2 = Realm::get_shared_realm(config);
-        REQUIRE(realm1.get() == realm2.get());
-    }
-
-    SECTION("should return different instances when caching is disabled") {
-        config.cache = false;
-        auto realm1 = Realm::get_shared_realm(config);
-        auto realm2 = Realm::get_shared_realm(config);
-        REQUIRE(realm1.get() != realm2.get());
-    }
-
     SECTION("should validate that the config is sensible") {
     SECTION("should validate that the config is sensible") {
         SECTION("bad encryption key") {
         SECTION("bad encryption key") {
             config.encryption_key = std::vector<char>(2, 0);
             config.encryption_key = std::vector<char>(2, 0);
@@ -121,9 +109,6 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
     }
     }
 
 
     SECTION("should reject mismatched config") {
     SECTION("should reject mismatched config") {
-        SECTION("cached") { }
-        SECTION("uncached") { config.cache = false; }
-
         SECTION("schema version") {
         SECTION("schema version") {
             auto realm = Realm::get_shared_realm(config);
             auto realm = Realm::get_shared_realm(config);
             config.schema_version = 2;
             config.schema_version = 2;
@@ -221,7 +206,7 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
             auto table = ObjectStore::table_for_object_type(g, "object");
             auto table = ObjectStore::table_for_object_type(g, "object");
             REQUIRE(table);
             REQUIRE(table);
             REQUIRE(table->get_column_count() == 1);
             REQUIRE(table->get_column_count() == 1);
-            REQUIRE(table->get_column_name(0) == "value");
+            REQUIRE(table->get_column_name(*table->get_column_keys().begin()) == "value");
         }
         }
 
 
         config.schema_version = 2;
         config.schema_version = 2;
@@ -272,27 +257,28 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
         auto realm = Realm::get_shared_realm(config);
         auto realm = Realm::get_shared_realm(config);
         REQUIRE(realm->schema().size() == 1);
         REQUIRE(realm->schema().size() == 1);
         auto it = realm->schema().find("object");
         auto it = realm->schema().find("object");
+        auto table = realm->read_group().get_table("class_object");
         REQUIRE(it != realm->schema().end());
         REQUIRE(it != realm->schema().end());
+        REQUIRE(it->table_key == table->get_key());
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties[0].name == "value");
         REQUIRE(it->persisted_properties[0].name == "value");
-        REQUIRE(it->persisted_properties[0].table_column == 0);
+        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") {
     SECTION("should read the proper schema from the file if a custom version is supplied") {
         Realm::get_shared_realm(config);
         Realm::get_shared_realm(config);
 
 
         config.schema = util::none;
         config.schema = util::none;
-        config.cache = false;
         config.schema_mode = SchemaMode::Additive;
         config.schema_mode = SchemaMode::Additive;
         config.schema_version = 0;
         config.schema_version = 0;
 
 
         auto realm = Realm::get_shared_realm(config);
         auto realm = Realm::get_shared_realm(config);
         REQUIRE(realm->schema().size() == 1);
         REQUIRE(realm->schema().size() == 1);
 
 
-        auto& shared_group = TestHelper::get_shared_group(realm);
-        shared_group.begin_read();
-        shared_group.pin_version();
-        VersionID old_version = shared_group.get_version_of_current_transaction();
+        auto& db = TestHelper::get_db(realm);
+        auto rt = db->start_read();
+        VersionID old_version = rt->get_version_of_current_transaction();
+        rt = nullptr;
         realm->close();
         realm->close();
 
 
         config.schema = Schema{
         config.schema = Schema{
@@ -314,9 +300,6 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
     }
     }
 
 
     SECTION("should sensibly handle opening an uninitialized file without a schema specified") {
     SECTION("should sensibly handle opening an uninitialized file without a schema specified") {
-        SECTION("cached") { }
-        SECTION("uncached") { config.cache = false; }
-
         // create an empty file
         // create an empty file
         File(config.path, File::mode_Write);
         File(config.path, File::mode_Write);
 
 
@@ -342,14 +325,15 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
         config.schema_mode = SchemaMode::Immutable;
         config.schema_mode = SchemaMode::Immutable;
         auto realm = Realm::get_shared_realm(config);
         auto realm = Realm::get_shared_realm(config);
         auto it = realm->schema().find("object");
         auto it = realm->schema().find("object");
+        auto table = realm->read_group().get_table("class_object");
         REQUIRE(it != realm->schema().end());
         REQUIRE(it != realm->schema().end());
+        REQUIRE(it->table_key == table->get_key());
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties[0].name == "value");
         REQUIRE(it->persisted_properties[0].name == "value");
-        REQUIRE(it->persisted_properties[0].table_column == 0);
+        REQUIRE(it->persisted_properties[0].column_key == table->get_column_key("value"));
     }
     }
 
 
     SECTION("should support using different table subsets on different threads") {
     SECTION("should support using different table subsets on different threads") {
-        config.cache = false;
         auto realm1 = Realm::get_shared_realm(config);
         auto realm1 = Realm::get_shared_realm(config);
 
 
         config.schema = Schema{
         config.schema = Schema{
@@ -387,7 +371,7 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
 #ifndef _WIN32
 #ifndef _WIN32
     SECTION("should throw when creating the notification pipe fails") {
     SECTION("should throw when creating the notification pipe fails") {
         util::try_make_dir(config.path + ".note");
         util::try_make_dir(config.path + ".note");
-        auto sys_fallback_file = util::format("%1realm_%2.note", SharedGroupOptions::get_sys_tmp_dir(), std::hash<std::string>()(config.path)); // Mirror internal implementation
+        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);
         util::try_make_dir(sys_fallback_file);
         REQUIRE_THROWS(Realm::get_shared_realm(config));
         REQUIRE_THROWS(Realm::get_shared_realm(config));
         util::remove_dir(config.path + ".note");
         util::remove_dir(config.path + ".note");
@@ -395,14 +379,6 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
     }
     }
 #endif
 #endif
 
 
-    SECTION("should get different instances on different threads") {
-        auto realm1 = Realm::get_shared_realm(config);
-        std::thread([&]{
-            auto realm2 = Realm::get_shared_realm(config);
-            REQUIRE(realm1 != realm2);
-        }).join();
-    }
-
     SECTION("should detect use of Realm on incorrect thread") {
     SECTION("should detect use of Realm on incorrect thread") {
         auto realm = Realm::get_shared_realm(config);
         auto realm = Realm::get_shared_realm(config);
         std::thread([&]{
         std::thread([&]{
@@ -410,36 +386,6 @@ TEST_CASE("SharedRealm: get_shared_realm()") {
         }).join();
         }).join();
     }
     }
 
 
-    SECTION("should get different instances for different explicit execuction contexts") {
-        config.execution_context = 0;
-        auto realm1 = Realm::get_shared_realm(config);
-        config.execution_context = 1;
-        auto realm2 = Realm::get_shared_realm(config);
-        REQUIRE(realm1 != realm2);
-
-        config.execution_context = util::none;
-        auto realm3 = Realm::get_shared_realm(config);
-        REQUIRE(realm1 != realm3);
-        REQUIRE(realm2 != realm3);
-    }
-
-    SECTION("can use Realm with explicit execution context on different thread") {
-        config.execution_context = 1;
-        auto realm = Realm::get_shared_realm(config);
-        std::thread([&]{
-            REQUIRE_NOTHROW(realm->verify_thread());
-        }).join();
-    }
-
-    SECTION("should get same instance for same explicit execution context on different thread") {
-        config.execution_context = 1;
-        auto realm1 = Realm::get_shared_realm(config);
-        std::thread([&]{
-            auto realm2 = Realm::get_shared_realm(config);
-            REQUIRE(realm1 == realm2);
-        }).join();
-    }
-
     SECTION("should not modify the schema when fetching from the cache") {
     SECTION("should not modify the schema when fetching from the cache") {
         auto realm = Realm::get_shared_realm(config);
         auto realm = Realm::get_shared_realm(config);
         auto object_schema = &*realm->schema().find("object");
         auto object_schema = &*realm->schema().find("object");
@@ -465,14 +411,12 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
 
 
     SyncServer server;
     SyncServer server;
     SyncTestFile config(server, "default");
     SyncTestFile config(server, "default");
-    config.cache = false;
     config.schema = Schema{
     config.schema = Schema{
         {"object", {
         {"object", {
             {"value", PropertyType::Int},
             {"value", PropertyType::Int},
         }},
         }},
     };
     };
     SyncTestFile config2(server, "default");
     SyncTestFile config2(server, "default");
-    config2.cache = false;
     config2.schema = config.schema;
     config2.schema = config.schema;
 
 
     std::mutex mutex;
     std::mutex mutex;
@@ -495,7 +439,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
         {
         {
             auto realm = Realm::get_shared_realm(config2);
             auto realm = Realm::get_shared_realm(config2);
             realm->begin_transaction();
             realm->begin_transaction();
-            sync::create_object(realm->read_group(), *realm->read_group().get_table("class_object"));
+            realm->read_group().get_table("class_object")->create_object();
             realm->commit_transaction();
             realm->commit_transaction();
             wait_for_upload(*realm);
             wait_for_upload(*realm);
         }
         }
@@ -520,7 +464,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
         {
         {
             auto realm = Realm::get_shared_realm(config2);
             auto realm = Realm::get_shared_realm(config2);
             realm->begin_transaction();
             realm->begin_transaction();
-            sync::create_object(realm->read_group(), *realm->read_group().get_table("class_object"));
+            realm->read_group().get_table("class_object")->create_object();
             realm->commit_transaction();
             realm->commit_transaction();
             wait_for_upload(*realm);
             wait_for_upload(*realm);
         }
         }
@@ -545,7 +489,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
         {
         {
             auto realm = Realm::get_shared_realm(config2);
             auto realm = Realm::get_shared_realm(config2);
             realm->begin_transaction();
             realm->begin_transaction();
-            sync::create_object(realm->read_group(), *realm->read_group().get_table("class_object"));
+            realm->read_group().get_table("class_object")->create_object();
             realm->commit_transaction();
             realm->commit_transaction();
             wait_for_upload(*realm);
             wait_for_upload(*realm);
         }
         }
@@ -594,7 +538,6 @@ TEST_CASE("SharedRealm: notifications") {
         return;
         return;
 
 
     TestFile config;
     TestFile config;
-    config.cache = false;
     config.schema_version = 0;
     config.schema_version = 0;
     config.schema = Schema{
     config.schema = Schema{
         {"object", {
         {"object", {
@@ -614,6 +557,7 @@ TEST_CASE("SharedRealm: notifications") {
 
 
     size_t change_count = 0;
     size_t change_count = 0;
     auto realm = Realm::get_shared_realm(config);
     auto realm = Realm::get_shared_realm(config);
+    realm->read_group();
     realm->m_binding_context.reset(new Context{&change_count});
     realm->m_binding_context.reset(new Context{&change_count});
     realm->m_binding_context->realm = realm;
     realm->m_binding_context->realm = realm;
 
 
@@ -677,9 +621,12 @@ TEST_CASE("SharedRealm: notifications") {
         r2->commit_transaction();
         r2->commit_transaction();
         REQUIRE(realm->refresh());
         REQUIRE(realm->refresh());
 
 
+        auto ver = realm->current_transaction_version();
         realm->m_binding_context.reset();
         realm->m_binding_context.reset();
         // Should advance to the version created in the previous did_change()
         // Should advance to the version created in the previous did_change()
         REQUIRE(realm->refresh());
         REQUIRE(realm->refresh());
+        auto new_ver = realm->current_transaction_version();
+        REQUIRE(*new_ver > *ver);
         // No more versions, so returns false
         // No more versions, so returns false
         REQUIRE_FALSE(realm->refresh());
         REQUIRE_FALSE(realm->refresh());
     }
     }
@@ -722,7 +669,6 @@ TEST_CASE("SharedRealm: notifications") {
 
 
 TEST_CASE("SharedRealm: schema updating from external changes") {
 TEST_CASE("SharedRealm: schema updating from external changes") {
     TestFile config;
     TestFile config;
-    config.cache = false;
     config.schema_version = 0;
     config.schema_version = 0;
     config.schema_mode = SchemaMode::Additive;
     config.schema_mode = SchemaMode::Additive;
     config.schema = Schema{
     config.schema = Schema{
@@ -733,18 +679,19 @@ TEST_CASE("SharedRealm: schema updating from external changes") {
     };
     };
 
 
     SECTION("newly added columns update table columns but are not added to properties") {
     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 r1 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(config);
         auto test = [&] {
         auto test = [&] {
             r2->begin_transaction();
             r2->begin_transaction();
-            r2->read_group().get_table("class_object")->insert_column(0, type_String, "new col");
+            r2->read_group().get_table("class_object")->add_column(type_String, "new col");
             r2->commit_transaction();
             r2->commit_transaction();
 
 
             auto& object_schema = *r1->schema().find("object");
             auto& object_schema = *r1->schema().find("object");
             REQUIRE(object_schema.persisted_properties.size() == 2);
             REQUIRE(object_schema.persisted_properties.size() == 2);
-            REQUIRE(object_schema.persisted_properties[0].table_column == 0);
+            ColKey col = object_schema.persisted_properties[0].column_key;
             r1->refresh();
             r1->refresh();
-            REQUIRE(object_schema.persisted_properties[0].table_column == 1);
+            REQUIRE(object_schema.persisted_properties[0].column_key == col);
         };
         };
         SECTION("with an active read transaction") {
         SECTION("with an active read transaction") {
             r1->read_group();
             r1->read_group();
@@ -760,19 +707,19 @@ TEST_CASE("SharedRealm: schema updating from external changes") {
         auto r = Realm::get_shared_realm(config);
         auto r = Realm::get_shared_realm(config);
         r->invalidate();
         r->invalidate();
 
 
-        auto& sg = TestHelper::get_shared_group(r);
-        WriteTransaction wt(sg);
+        auto& db = TestHelper::get_db(r);
+        WriteTransaction wt(db);
         auto& table = *wt.get_table("class_object");
         auto& table = *wt.get_table("class_object");
 
 
         SECTION("removing a property") {
         SECTION("removing a property") {
-            table.remove_column(0);
+            table.remove_column(table.get_column_key("value"));
             wt.commit();
             wt.commit();
             REQUIRE_THROWS_WITH(r->refresh(),
             REQUIRE_THROWS_WITH(r->refresh(),
                                 Catch::Matchers::Contains("Property 'object.value' has been removed."));
                                 Catch::Matchers::Contains("Property 'object.value' has been removed."));
         }
         }
 
 
         SECTION("change property type") {
         SECTION("change property type") {
-            table.remove_column(1);
+            table.remove_column(table.get_column_key("value 2"));
             table.add_column(type_Float, "value 2");
             table.add_column(type_Float, "value 2");
             wt.commit();
             wt.commit();
             REQUIRE_THROWS_WITH(r->refresh(),
             REQUIRE_THROWS_WITH(r->refresh(),
@@ -780,7 +727,7 @@ TEST_CASE("SharedRealm: schema updating from external changes") {
         }
         }
 
 
         SECTION("make property optional") {
         SECTION("make property optional") {
-            table.remove_column(1);
+            table.remove_column(table.get_column_key("value 2"));
             table.add_column(type_Int, "value 2", true);
             table.add_column(type_Int, "value 2", true);
             wt.commit();
             wt.commit();
             REQUIRE_THROWS_WITH(r->refresh(),
             REQUIRE_THROWS_WITH(r->refresh(),
@@ -788,43 +735,74 @@ TEST_CASE("SharedRealm: schema updating from external changes") {
         }
         }
 
 
         SECTION("recreate column with no changes") {
         SECTION("recreate column with no changes") {
-            table.remove_column(1);
+            table.remove_column(table.get_column_key("value 2"));
             table.add_column(type_Int, "value 2");
             table.add_column(type_Int, "value 2");
             wt.commit();
             wt.commit();
             REQUIRE_NOTHROW(r->refresh());
             REQUIRE_NOTHROW(r->refresh());
         }
         }
 
 
         SECTION("remove index from non-PK") {
         SECTION("remove index from non-PK") {
-            table.remove_search_index(1);
+            table.remove_search_index(table.get_column_key("value 2"));
             wt.commit();
             wt.commit();
             REQUIRE_NOTHROW(r->refresh());
             REQUIRE_NOTHROW(r->refresh());
         }
         }
     }
     }
 }
 }
 
 
-TEST_CASE("SharedRealm: closed realm") {
+TEST_CASE("SharedRealm: close()") {
     TestFile config;
     TestFile config;
     config.schema_version = 1;
     config.schema_version = 1;
     config.schema = Schema{
     config.schema = Schema{
         {"object", {
         {"object", {
             {"value", PropertyType::Int}
             {"value", PropertyType::Int}
         }},
         }},
+        {"list", {
+            {"list", PropertyType::Object|PropertyType::Array, "object"}
+        }},
     };
     };
 
 
     auto realm = Realm::get_shared_realm(config);
     auto realm = Realm::get_shared_realm(config);
-    realm->close();
 
 
-    REQUIRE(realm->is_closed());
+    SECTION("all functions throw ClosedRealmException after close") {
+        realm->close();
 
 
-    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(realm->is_closed());
 
 
-    REQUIRE_THROWS_AS(realm->refresh(), ClosedRealmException);
-    REQUIRE_THROWS_AS(realm->invalidate(), ClosedRealmException);
-    REQUIRE_THROWS_AS(realm->compact(), ClosedRealmException);
+        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") {
 TEST_CASE("ShareRealm: in-memory mode from buffer") {
@@ -852,10 +830,12 @@ TEST_CASE("ShareRealm: in-memory mode from buffer") {
         // Verify that it can read the schema and that it is the same
         // Verify that it can read the schema and that it is the same
         REQUIRE(realm->schema().size() == 1);
         REQUIRE(realm->schema().size() == 1);
         auto it = realm->schema().find("object");
         auto it = realm->schema().find("object");
+        auto table = realm->read_group().get_table("class_object");
         REQUIRE(it != realm->schema().end());
         REQUIRE(it != realm->schema().end());
+        REQUIRE(it->table_key == table->get_key());
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties.size() == 1);
         REQUIRE(it->persisted_properties[0].name == "value");
         REQUIRE(it->persisted_properties[0].name == "value");
-        REQUIRE(it->persisted_properties[0].table_column == 0);
+        REQUIRE(it->persisted_properties[0].column_key == table->get_column_key("value"));
 
 
         // Test invalid configs
         // Test invalid configs
         realm::Realm::Config config3;
         realm::Realm::Config config3;
@@ -881,13 +861,12 @@ TEST_CASE("ShareRealm: realm closed in did_change callback") {
             {"value", PropertyType::Int}
             {"value", PropertyType::Int}
         }},
         }},
     };
     };
-    config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
     auto r1 = Realm::get_shared_realm(config);
     auto r1 = Realm::get_shared_realm(config);
 
 
     r1->begin_transaction();
     r1->begin_transaction();
     auto table = r1->read_group().get_table("class_object");
     auto table = r1->read_group().get_table("class_object");
-    table->add_empty_row();
+    table->create_object();
     r1->commit_transaction();
     r1->commit_transaction();
 
 
     // Cannot be a member var of Context since Realm.close will free the context.
     // Cannot be a member var of Context since Realm.close will free the context.
@@ -907,7 +886,7 @@ TEST_CASE("ShareRealm: realm closed in did_change callback") {
 
 
         auto r2 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(config);
         r2->begin_transaction();
         r2->begin_transaction();
-        r2->read_group().get_table("class_object")->add_empty_row(1);
+        r2->read_group().get_table("class_object")->create_object();
         r2->commit_transaction();
         r2->commit_transaction();
         r2.reset();
         r2.reset();
 
 
@@ -924,11 +903,11 @@ TEST_CASE("ShareRealm: realm closed in did_change callback") {
 
 
         auto r2 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(config);
         r2->begin_transaction();
         r2->begin_transaction();
-        r2->read_group().get_table("class_object")->add_empty_row(1);
+        r2->read_group().get_table("class_object")->create_object();
         r2->commit_transaction();
         r2->commit_transaction();
         r2.reset();
         r2.reset();
 
 
-        auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
+        auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
         coordinator->on_change();
         coordinator->on_change();
 
 
         r1->notify();
         r1->notify();
@@ -939,7 +918,7 @@ TEST_CASE("ShareRealm: realm closed in did_change callback") {
 
 
         auto r2 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(config);
         r2->begin_transaction();
         r2->begin_transaction();
-        r2->read_group().get_table("class_object")->add_empty_row(1);
+        r2->read_group().get_table("class_object")->create_object();
         r2->commit_transaction();
         r2->commit_transaction();
         r2.reset();
         r2.reset();
 
 
@@ -1049,9 +1028,8 @@ TEST_CASE("RealmCoordinator: schema cache") {
 
 
 TEST_CASE("SharedRealm: coordinator schema cache") {
 TEST_CASE("SharedRealm: coordinator schema cache") {
     TestFile config;
     TestFile config;
-    config.cache = false;
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
-    auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
+    auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
 
 
     Schema cache_schema;
     Schema cache_schema;
     uint64_t cache_sv = -1, cache_tv = -1;
     uint64_t cache_sv = -1, cache_tv = -1;
@@ -1072,17 +1050,16 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
 
 
     class ExternalWriter {
     class ExternalWriter {
     private:
     private:
-        std::unique_ptr<Replication> history;
-        std::unique_ptr<SharedGroup> shared_group;
-        std::unique_ptr<Group> read_only_group;
-
+        std::shared_ptr<Realm> m_realm;
     public:
     public:
         WriteTransaction wt;
         WriteTransaction wt;
         ExternalWriter(Realm::Config const& config)
         ExternalWriter(Realm::Config const& config)
-        : wt([&]() -> SharedGroup& {
-            Realm::open_with_config(config, history, shared_group, read_only_group, nullptr);
-            return *shared_group;
+        : 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))
         {
         {
         }
         }
     };
     };
@@ -1102,20 +1079,20 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
         REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
         REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
         REQUIRE(cache_sv == 0);
         REQUIRE(cache_sv == 0);
         REQUIRE(cache_schema == schema);
         REQUIRE(cache_schema == schema);
-        REQUIRE(cache_schema.begin()->persisted_properties[0].table_column == 0);
+        REQUIRE(cache_schema.begin()->persisted_properties[0].column_key != ColKey{});
     }
     }
 
 
     coordinator = nullptr;
     coordinator = nullptr;
     r = nullptr;
     r = nullptr;
     r = Realm::get_shared_realm(config);
     r = Realm::get_shared_realm(config);
-    coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
+    coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
     REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
     REQUIRE(coordinator->get_cached_schema(cache_schema, cache_sv, cache_tv));
 
 
     SECTION("is populated after opening an initialized file") {
     SECTION("is populated after opening an initialized file") {
         REQUIRE(cache_sv == 0);
         REQUIRE(cache_sv == 0);
         REQUIRE(cache_tv == 2); // with in-realm history the version doesn't reset
         REQUIRE(cache_tv == 2); // with in-realm history the version doesn't reset
         REQUIRE(cache_schema == schema);
         REQUIRE(cache_schema == schema);
-        REQUIRE(cache_schema.begin()->persisted_properties[0].table_column == 0);
+        REQUIRE(cache_schema.begin()->persisted_properties[0].column_key != ColKey{});
     }
     }
 
 
     SECTION("transaction version is bumped after a local write") {
     SECTION("transaction version is bumped after a local write") {
@@ -1131,7 +1108,7 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
 
 
         SECTION("non-schema change") {
         SECTION("non-schema change") {
             external_write(config, [](auto& wt) {
             external_write(config, [](auto& wt) {
-                wt.get_table("class_object")->add_empty_row();
+                wt.get_table("class_object")->create_object();
             });
             });
         }
         }
         SECTION("schema change") {
         SECTION("schema change") {
@@ -1149,7 +1126,7 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
     SECTION("notify() with a read transaction bumps transaction version") {
     SECTION("notify() with a read transaction bumps transaction version") {
         r->read_group();
         r->read_group();
         external_write(config, [](auto& wt) {
         external_write(config, [](auto& wt) {
-            wt.get_table("class_object")->add_empty_row();
+            wt.get_table("class_object")->create_object();
         });
         });
 
 
         r->notify();
         r->notify();
@@ -1174,7 +1151,7 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
 
 
     SECTION("transaction version is bumped after refresh() following external non-schema write") {
     SECTION("transaction version is bumped after refresh() following external non-schema write") {
         external_write(config, [](auto& wt) {
         external_write(config, [](auto& wt) {
-            wt.get_table("class_object")->add_empty_row();
+            wt.get_table("class_object")->create_object();
         });
         });
 
 
         r->refresh();
         r->refresh();
@@ -1273,7 +1250,6 @@ TEST_CASE("SharedRealm: coordinator schema cache") {
 
 
 TEST_CASE("SharedRealm: dynamic schema mode doesn't invalidate object schema pointers when schema hasn't changed") {
 TEST_CASE("SharedRealm: dynamic schema mode doesn't invalidate object schema pointers when schema hasn't changed") {
     TestFile config;
     TestFile config;
-    config.cache = false;
 
 
     // Prepopulate the Realm with the schema.
     // Prepopulate the Realm with the schema.
     Realm::Config config_with_schema = config;
     Realm::Config config_with_schema = config;
@@ -1316,7 +1292,6 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
     size_t schema_changed_called = 0;
     size_t schema_changed_called = 0;
     Schema changed_fixed_schema;
     Schema changed_fixed_schema;
     TestFile config;
     TestFile config;
-    config.cache = false;
     auto dynamic_config = config;
     auto dynamic_config = config;
 
 
     config.schema = Schema{
     config.schema = Schema{
@@ -1329,6 +1304,7 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
     };
     };
     config.schema_version = 1;
     config.schema_version = 1;
     auto r1 = Realm::get_shared_realm(config);
     auto r1 = Realm::get_shared_realm(config);
+    r1->read_group();
     r1->m_binding_context.reset(new Context(&schema_changed_called, &changed_fixed_schema));
     r1->m_binding_context.reset(new Context(&schema_changed_called, &changed_fixed_schema));
 
 
     SECTION("Fixed schema") {
     SECTION("Fixed schema") {
@@ -1340,7 +1316,7 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
             };
             };
             r1->update_schema(new_schema, 2);
             r1->update_schema(new_schema, 2);
             REQUIRE(schema_changed_called == 1);
             REQUIRE(schema_changed_called == 1);
-            REQUIRE(changed_fixed_schema.find("object3")->property_for_name("value")->table_column == 0);
+            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") {
         SECTION("Open a new Realm instance with same config won't trigger") {
@@ -1359,17 +1335,17 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
         SECTION("Schema is changed by another Realm") {
         SECTION("Schema is changed by another Realm") {
             auto r2 = Realm::get_shared_realm(config);
             auto r2 = Realm::get_shared_realm(config);
             r2->begin_transaction();
             r2->begin_transaction();
-            r2->read_group().get_table("class_object1")->insert_column(0, type_String, "new col");
+            r2->read_group().get_table("class_object1")->add_column(type_String, "new col");
             r2->commit_transaction();
             r2->commit_transaction();
             r1->refresh();
             r1->refresh();
             REQUIRE(schema_changed_called == 1);
             REQUIRE(schema_changed_called == 1);
-            REQUIRE(changed_fixed_schema.find("object1")->property_for_name("value")->table_column == 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.
         // This is not a valid use case. m_schema won't be refreshed.
         SECTION("Schema is changed by this Realm won't trigger") {
         SECTION("Schema is changed by this Realm won't trigger") {
             r1->begin_transaction();
             r1->begin_transaction();
-            r1->read_group().get_table("class_object1")->insert_column(0, type_String, "new col");
+            r1->read_group().get_table("class_object1")->add_column(type_String, "new col");
             r1->commit_transaction();
             r1->commit_transaction();
             REQUIRE(schema_changed_called == 0);
             REQUIRE(schema_changed_called == 0);
         }
         }
@@ -1390,26 +1366,26 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
             r2->set_schema_subset(new_schema);
             r2->set_schema_subset(new_schema);
             REQUIRE(schema_changed_called == 0);
             REQUIRE(schema_changed_called == 0);
             REQUIRE(dynamic_schema_changed_called == 1);
             REQUIRE(dynamic_schema_changed_called == 1);
-            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->table_column == 0);
+            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
         }
         }
 
 
-        SECTION("Non schema related transaction will alway trigger in dynamic mode") {
+        SECTION("Non schema related transaction will always trigger in dynamic mode") {
             auto r1 = Realm::get_shared_realm(config);
             auto r1 = Realm::get_shared_realm(config);
             // An empty transaction will trigger the schema changes always in dynamic mode.
             // An empty transaction will trigger the schema changes always in dynamic mode.
             r1->begin_transaction();
             r1->begin_transaction();
             r1->commit_transaction();
             r1->commit_transaction();
             r2->refresh();
             r2->refresh();
             REQUIRE(dynamic_schema_changed_called == 1);
             REQUIRE(dynamic_schema_changed_called == 1);
-            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->table_column == 0);
+            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
         }
         }
 
 
         SECTION("Schema is changed by another Realm") {
         SECTION("Schema is changed by another Realm") {
             r1->begin_transaction();
             r1->begin_transaction();
-            r1->read_group().get_table("class_object1")->insert_column(0, type_String, "new col");
+            r1->read_group().get_table("class_object1")->add_column(type_String, "new col");
             r1->commit_transaction();
             r1->commit_transaction();
             r2->refresh();
             r2->refresh();
             REQUIRE(dynamic_schema_changed_called == 1);
             REQUIRE(dynamic_schema_changed_called == 1);
-            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->table_column == 1);
+            REQUIRE(changed_dynamic_schema.find("object1")->property_for_name("value")->column_key != ColKey{});
         }
         }
     }
     }
 }
 }
@@ -1418,7 +1394,6 @@ TEST_CASE("SharedRealm: SchemaChangedFunction") {
 TEST_CASE("SharedRealm: compact on launch") {
 TEST_CASE("SharedRealm: compact on launch") {
     // Make compactable Realm
     // Make compactable Realm
     TestFile config;
     TestFile config;
-    config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
     int num_opens = 0;
     int num_opens = 0;
     config.should_compact_on_launch_function = [&](size_t total_bytes, size_t used_bytes) {
     config.should_compact_on_launch_function = [&](size_t total_bytes, size_t used_bytes) {
@@ -1437,9 +1412,8 @@ TEST_CASE("SharedRealm: compact on launch") {
     r->begin_transaction();
     r->begin_transaction();
     auto table = r->read_group().get_table("class_object");
     auto table = r->read_group().get_table("class_object");
     size_t count = 1000;
     size_t count = 1000;
-    table->add_empty_row(count);
     for (size_t i = 0; i < count; ++i)
     for (size_t i = 0; i < count; ++i)
-        table->set_string(0, i, util::format("Foo_%1", i % 10).c_str());
+        table->create_object().set_all(util::format("Foo_%1", i % 10).c_str());
     r->commit_transaction();
     r->commit_transaction();
     REQUIRE(table->size() == count);
     REQUIRE(table->size() == count);
     r->close();
     r->close();
@@ -1459,12 +1433,13 @@ TEST_CASE("SharedRealm: compact on launch") {
         REQUIRE(r->read_group().get_table("class_object")->size() == count);
         REQUIRE(r->read_group().get_table("class_object")->size() == count);
 
 
         // Registering for a collection notification shouldn't crash when compact on launch is used.
         // 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.async([](std::exception_ptr) { });
+        Results results(r, r->read_group().get_table("class_object"));
+        results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) { });
         r->close();
         r->close();
     }
     }
 
 
     SECTION("compact function does not get invoked if realm is open on another thread") {
     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);
         r = Realm::get_shared_realm(config);
         REQUIRE(num_opens == 2);
         REQUIRE(num_opens == 2);
         std::thread([&]{
         std::thread([&]{
@@ -1559,7 +1534,6 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
     _impl::RealmCoordinator::assert_no_open_realms();
     _impl::RealmCoordinator::assert_no_open_realms();
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
-    config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
 
 
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
@@ -1569,7 +1543,7 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
         }},
         }},
     });
     });
 
 
-    auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
+    auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
     auto table = r->read_group().get_table("class_object");
     auto table = r->read_group().get_table("class_object");
 
 
     SECTION("BindingContext notified even if no callbacks are registered") {
     SECTION("BindingContext notified even if no callbacks are registered") {
@@ -1602,10 +1576,10 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
             binding_context_start_notify_calls = 0;
             binding_context_start_notify_calls = 0;
             binding_context_end_notify_calls = 0;
             binding_context_end_notify_calls = 0;
             JoiningThread([&] {
             JoiningThread([&] {
-                auto r2 = coordinator->get_realm();
+                auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
                 r2->begin_transaction();
                 r2->begin_transaction();
                 auto table2 = r2->read_group().get_table("class_object");
                 auto table2 = r2->read_group().get_table("class_object");
-                table2->add_empty_row();
+                table2->create_object();
                 r2->commit_transaction();
                 r2->commit_transaction();
             });
             });
             advance_and_notify(*r);
             advance_and_notify(*r);
@@ -1619,8 +1593,9 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
         static int binding_context_end_notify_calls = 0;
         static int binding_context_end_notify_calls = 0;
         static int notification_calls = 0;
         static int notification_calls = 0;
 
 
-        Results results1(r, table->where().greater_equal(0, 0));
-        Results results2(r, table->where().less(0, 10));
+        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) {
         auto token1 = results1.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
             REQUIRE_FALSE(err);
             REQUIRE_FALSE(err);
@@ -1655,7 +1630,7 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
             notification_calls = 0;
             notification_calls = 0;
             coordinator->on_change();
             coordinator->on_change();
             r->begin_transaction();
             r->begin_transaction();
-            table->add_empty_row();
+            table->create_object();
             r->commit_transaction();
             r->commit_transaction();
             REQUIRE(binding_context_start_notify_calls == 1);
             REQUIRE(binding_context_start_notify_calls == 1);
             REQUIRE(binding_context_end_notify_calls == 1);
             REQUIRE(binding_context_end_notify_calls == 1);
@@ -1666,10 +1641,10 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
             binding_context_end_notify_calls = 0;
             binding_context_end_notify_calls = 0;
             notification_calls = 0;
             notification_calls = 0;
             JoiningThread([&] {
             JoiningThread([&] {
-                auto r2 = coordinator->get_realm();
+                auto r2 = coordinator->get_realm(util::Scheduler::get_frozen());
                 r2->begin_transaction();
                 r2->begin_transaction();
                 auto table2 = r2->read_group().get_table("class_object");
                 auto table2 = r2->read_group().get_table("class_object");
-                table2->add_empty_row();
+                table2->create_object();
                 r2->commit_transaction();
                 r2->commit_transaction();
             });
             });
             advance_and_notify(*r);
             advance_and_notify(*r);
@@ -1715,9 +1690,9 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
             do_close = true;
             do_close = true;
 
 
             JoiningThread([&] {
             JoiningThread([&] {
-                auto r = coordinator->get_realm();
+                auto r = coordinator->get_realm(util::Scheduler::get_frozen());
                 r->begin_transaction();
                 r->begin_transaction();
-                r->read_group().get_table("class_object")->add_empty_row();
+                r->read_group().get_table("class_object")->create_object();
                 r->commit_transaction();
                 r->commit_transaction();
             });
             });
 
 
@@ -1739,9 +1714,9 @@ TEST_CASE("BindingContext is notified about delivery of change notifications") {
             do_close = true;
             do_close = true;
 
 
             JoiningThread([&] {
             JoiningThread([&] {
-                auto r = coordinator->get_realm();
+                auto r = coordinator->get_realm(util::Scheduler::get_frozen());
                 r->begin_transaction();
                 r->begin_transaction();
-                r->read_group().get_table("class_object")->add_empty_row();
+                r->read_group().get_table("class_object")->create_object();
                 r->commit_transaction();
                 r->commit_transaction();
             });
             });
 
 
@@ -1756,7 +1731,7 @@ TEST_CASE("Statistics on Realms") {
     _impl::RealmCoordinator::assert_no_open_realms();
     _impl::RealmCoordinator::assert_no_open_realms();
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
-    config.cache = false;
+    // config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
 
 
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
@@ -1772,7 +1747,7 @@ TEST_CASE("Statistics on Realms") {
     }
     }
 }
 }
 
 
-#if REALM_PLATFORM_APPLE
+#if REALM_PLATFORM_APPLE && NOTIFIER_BACKGROUND_ERRORS
 TEST_CASE("BindingContext is notified in case of notifier errors") {
 TEST_CASE("BindingContext is notified in case of notifier errors") {
     _impl::RealmCoordinator::assert_no_open_realms();
     _impl::RealmCoordinator::assert_no_open_realms();
 
 
@@ -1797,7 +1772,6 @@ TEST_CASE("BindingContext is notified in case of notifier errors") {
     };
     };
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
-    config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
 
 
     auto r = Realm::get_shared_realm(config);
     auto r = Realm::get_shared_realm(config);
@@ -1807,7 +1781,7 @@ TEST_CASE("BindingContext is notified in case of notifier errors") {
       }},
       }},
     });
     });
 
 
-    auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
+    auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
     auto table = r->read_group().get_table("class_object");
     auto table = r->read_group().get_table("class_object");
     Results results(r, *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_start_notify_calls = 0;
@@ -1852,7 +1826,7 @@ TEST_CASE("RealmCoordinator: get_unbound_realm()") {
         }},
         }},
     };
     };
 
 
-    ThreadSafeReference<Realm> ref;
+    ThreadSafeReference ref;
     std::thread([&] { ref = _impl::RealmCoordinator::get_coordinator(config)->get_unbound_realm(); }).join();
     std::thread([&] { ref = _impl::RealmCoordinator::get_coordinator(config)->get_unbound_realm(); }).join();
 
 
     SECTION("checks thread after being resolved") {
     SECTION("checks thread after being resolved") {
@@ -1875,34 +1849,7 @@ TEST_CASE("RealmCoordinator: get_unbound_realm()") {
         util::EventLoop::main().run_until([&] { return called; });
         util::EventLoop::main().run_until([&] { return called; });
     }
     }
 
 
-    SECTION("does not check thread if resolved using an execution context") {
-        auto realm = Realm::get_shared_realm(std::move(ref), AbstractExecutionContextID(1));
-        REQUIRE_NOTHROW(realm->verify_thread());
-        std::thread([&] {
-            REQUIRE_NOTHROW(realm->verify_thread());
-        }).join();
-    }
-
-    SECTION("resolves to existing cached Realm for the thread if caching is enabled") {
-        auto r1 = Realm::get_shared_realm(config);
-        auto r2 = Realm::get_shared_realm(std::move(ref));
-        REQUIRE(r1 == r2);
-    }
-
-    SECTION("resolves to existing cached Realm for the execution context if caching is enabled") {
-        config.execution_context = AbstractExecutionContextID(1);
-        auto r1 = Realm::get_shared_realm(config);
-        config.execution_context = AbstractExecutionContextID(2);
-        auto r2 = Realm::get_shared_realm(config);
-        auto r3 = Realm::get_shared_realm(std::move(ref), AbstractExecutionContextID(1));
-        REQUIRE(r1 == r3);
-        REQUIRE(r1 != r2);
-        REQUIRE(r2 != r3);
-    }
-
     SECTION("resolves to a new Realm if caching is disabled") {
     SECTION("resolves to a new Realm if caching is disabled") {
-        // Cache disabled for local realm, enabled for unbound
-        config.cache = false;
         auto r1 = Realm::get_shared_realm(config);
         auto r1 = Realm::get_shared_realm(config);
         auto r2 = Realm::get_shared_realm(std::move(ref));
         auto r2 = Realm::get_shared_realm(std::move(ref));
         REQUIRE(r1 != r2);
         REQUIRE(r1 != r2);
@@ -1912,10 +1859,5 @@ TEST_CASE("RealmCoordinator: get_unbound_realm()") {
         auto r3 = Realm::get_shared_realm(std::move(ref));
         auto r3 = Realm::get_shared_realm(std::move(ref));
         REQUIRE(r1 != r3);
         REQUIRE(r1 != r3);
         REQUIRE(r2 != r3);
         REQUIRE(r2 != r3);
-
-        // New local with cache enabled should grab the resolved unbound
-        config.cache = true;
-        auto r4 = Realm::get_shared_realm(config);
-        REQUIRE(r4 == r2);
     }
     }
 }
 }

File diff suppressed because it is too large
+ 287 - 202
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp


+ 19 - 39
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/schema.cpp

@@ -24,7 +24,6 @@
 #include "property.hpp"
 #include "property.hpp"
 #include "schema.hpp"
 #include "schema.hpp"
 
 
-#include <realm/descriptor.hpp>
 #include <realm/group.hpp>
 #include <realm/group.hpp>
 #include <realm/table.hpp>
 #include <realm/table.hpp>
 
 
@@ -114,17 +113,9 @@ TEST_CASE("ObjectSchema") {
 
 
     SECTION("from a Group") {
     SECTION("from a Group") {
         Group g;
         Group g;
-        TableRef pk = g.add_table("pk");
-        pk->add_column(type_String, "pk_table");
-        pk->add_column(type_String, "pk_property");
-        pk->add_empty_row();
-        pk->set_string(0, 0, "table");
-        pk->set_string(1, 0, "pk");
-
-        TableRef table = g.add_table("class_table");
-        TableRef target = g.add_table("class_target");
 
 
-        table->add_column(type_Int, "pk");
+        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_Int, "int");
         table->add_column(type_Bool, "bool");
         table->add_column(type_Bool, "bool");
@@ -145,13 +136,8 @@ TEST_CASE("ObjectSchema") {
         table->add_column(type_Binary, "data?", true);
         table->add_column(type_Binary, "data?", true);
         table->add_column(type_Timestamp, "date?", true);
         table->add_column(type_Timestamp, "date?", true);
 
 
-        table->add_column(type_Table, "subtable 1");
-        size_t col = table->add_column(type_Table, "subtable 2");
-        table->get_subdescriptor(col)->add_column(type_Int, "value");
-
         auto add_list = [](TableRef table, DataType type, StringData name, bool nullable) {
         auto add_list = [](TableRef table, DataType type, StringData name, bool nullable) {
-            size_t col = table->add_column(type_Table, name);
-            table->get_subdescriptor(col)->add_column(type, ObjectStore::ArrayColumnName, nullptr, nullable);
+            table->add_column_list(type, name, nullable);
         };
         };
 
 
         add_list(table, type_Int, "int array", false);
         add_list(table, type_Int, "int array", false);
@@ -169,30 +155,32 @@ TEST_CASE("ObjectSchema") {
         add_list(table, type_Binary, "data? array", true);
         add_list(table, type_Binary, "data? array", true);
         add_list(table, type_Timestamp, "date? array", true);
         add_list(table, type_Timestamp, "date? array", true);
 
 
-        size_t indexed_start = table->get_column_count();
-        table->add_column(type_Int, "indexed int");
-        table->add_column(type_Bool, "indexed bool");
-        table->add_column(type_String, "indexed string");
-        table->add_column(type_Timestamp, "indexed date");
+        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"));
 
 
-        table->add_column(type_Int, "indexed int?", true);
-        table->add_column(type_Bool, "indexed bool?", true);
-        table->add_column(type_String, "indexed string?", true);
-        table->add_column(type_Timestamp, "indexed date?", true);
+        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 (size_t i = indexed_start; i < table->get_column_count(); ++i)
-            table->add_search_index(i);
+        for (auto col : indexed_cols)
+            table->add_search_index(col);
 
 
-        ObjectSchema os(g, "table");
+        ObjectSchema os(g, "table", table->get_key());
+        REQUIRE(os.table_key == table->get_key());
 
 
 #define REQUIRE_PROPERTY(name, type, ...) do { \
 #define REQUIRE_PROPERTY(name, type, ...) do { \
     Property* prop; \
     Property* prop; \
     REQUIRE((prop = os.property_for_name(name))); \
     REQUIRE((prop = os.property_for_name(name))); \
     REQUIRE((*prop == Property{name, PropertyType::type, __VA_ARGS__})); \
     REQUIRE((*prop == Property{name, PropertyType::type, __VA_ARGS__})); \
-    REQUIRE(prop->table_column == expected_col++); \
+    REQUIRE(prop->column_key == *expected_col++); \
 } while (0)
 } while (0)
 
 
-        size_t expected_col = 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(os.property_for_name("nonexistent property") == nullptr);
 
 
@@ -217,11 +205,6 @@ TEST_CASE("ObjectSchema") {
         REQUIRE_PROPERTY("data?", Data|PropertyType::Nullable);
         REQUIRE_PROPERTY("data?", Data|PropertyType::Nullable);
         REQUIRE_PROPERTY("date?", Date|PropertyType::Nullable);
         REQUIRE_PROPERTY("date?", Date|PropertyType::Nullable);
 
 
-        // Unsupported column type should be skipped entirely
-        REQUIRE(os.property_for_name("subtable 1") == nullptr);
-        REQUIRE(os.property_for_name("subtable 2") == nullptr);
-        expected_col += 2;
-
         REQUIRE_PROPERTY("int array", Int|PropertyType::Array);
         REQUIRE_PROPERTY("int array", Int|PropertyType::Array);
         REQUIRE_PROPERTY("bool array", Bool|PropertyType::Array);
         REQUIRE_PROPERTY("bool array", Bool|PropertyType::Array);
         REQUIRE_PROPERTY("float array", Float|PropertyType::Array);
         REQUIRE_PROPERTY("float array", Float|PropertyType::Array);
@@ -246,9 +229,6 @@ TEST_CASE("ObjectSchema") {
         REQUIRE_PROPERTY("indexed bool?", Bool|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 string?", String|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
         REQUIRE_PROPERTY("indexed date?", Date|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
         REQUIRE_PROPERTY("indexed date?", Date|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
-
-        pk->set_string(1, 0, "nonexistent property");
-        REQUIRE(ObjectSchema(g, "table").primary_key_property() == nullptr);
     }
     }
 }
 }
 
 

+ 429 - 252
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/thread_safe_reference.cpp

@@ -28,15 +28,15 @@
 #include "results.hpp"
 #include "results.hpp"
 #include "schema.hpp"
 #include "schema.hpp"
 #include "thread_safe_reference.hpp"
 #include "thread_safe_reference.hpp"
+#include "util/scheduler.hpp"
 
 
 #include "impl/object_accessor_impl.hpp"
 #include "impl/object_accessor_impl.hpp"
 
 
+#include <realm/db.hpp>
 #include <realm/history.hpp>
 #include <realm/history.hpp>
+#include <realm/string_data.hpp>
 #include <realm/util/optional.hpp>
 #include <realm/util/optional.hpp>
 
 
-#include <future>
-#include <thread>
-
 using namespace realm;
 using namespace realm;
 
 
 static TableRef get_table(Realm& realm, StringData object_name) {
 static TableRef get_table(Realm& realm, StringData object_name) {
@@ -70,7 +70,6 @@ TEST_CASE("thread safe reference") {
     };
     };
 
 
     InMemoryTestFile config;
     InMemoryTestFile config;
-    config.cache = false;
     config.automatic_change_notifications = false;
     config.automatic_change_notifications = false;
     SharedRealm r = Realm::get_shared_realm(config);
     SharedRealm r = Realm::get_shared_realm(config);
     r->update_schema(schema);
     r->update_schema(schema);
@@ -80,123 +79,111 @@ TEST_CASE("thread safe reference") {
     auto foo = create_object(r, "foo object", {{"ignore me", INT64_C(0)}});
     auto foo = create_object(r, "foo object", {{"ignore me", INT64_C(0)}});
     r->commit_transaction();
     r->commit_transaction();
 
 
-    SECTION("disallowed during write transactions") {
+    const auto int_obj_col = r->schema().find("int object")->persisted_properties[0].column_key;
+
+    SECTION("allowed during write transactions") {
         SECTION("obtain") {
         SECTION("obtain") {
             r->begin_transaction();
             r->begin_transaction();
-            REQUIRE_THROWS(r->obtain_thread_safe_reference(foo));
+            REQUIRE_NOTHROW(ThreadSafeReference(foo));
         }
         }
         SECTION("resolve") {
         SECTION("resolve") {
-            auto ref = r->obtain_thread_safe_reference(foo);
+            auto ref = ThreadSafeReference(foo);
             r->begin_transaction();
             r->begin_transaction();
-            REQUIRE_THROWS(r->resolve_thread_safe_reference(std::move(ref)));
+            REQUIRE_NOTHROW(ref.resolve<Object>(r));
         }
         }
     }
     }
 
 
     SECTION("cleanup properly unpins version") {
     SECTION("cleanup properly unpins version") {
         auto history = make_in_realm_history(config.path);
         auto history = make_in_realm_history(config.path);
-        SharedGroup shared_group(*history, config.options());
+        auto shared_group = DB::create(*history, config.options());
 
 
         auto get_current_version = [&]() -> VersionID {
         auto get_current_version = [&]() -> VersionID {
-            shared_group.begin_read();
-            auto version = shared_group.get_version_of_current_transaction();
-            shared_group.end_read();
+            auto rt = shared_group->start_read();
+            auto version = rt->get_version_of_current_transaction();
             return version;
             return version;
         };
         };
 
 
         auto reference_version = get_current_version();
         auto reference_version = get_current_version();
-        auto ref = util::make_optional(r->obtain_thread_safe_reference(foo));
+        auto ref = util::make_optional(ThreadSafeReference(foo));
         r->begin_transaction(); r->commit_transaction(); // Advance version
         r->begin_transaction(); r->commit_transaction(); // Advance version
 
 
         REQUIRE(get_current_version() != reference_version); // Ensure advanced
         REQUIRE(get_current_version() != reference_version); // Ensure advanced
-        REQUIRE_NOTHROW(shared_group.begin_read(reference_version)); shared_group.end_read(); // Ensure pinned
-        bool did_run_section = false;
+        REQUIRE_NOTHROW(shared_group->start_read(reference_version)); // Ensure pinned
 
 
-        SECTION("destroyed without being resolved") {
-            did_run_section = true;
-            ref = {}; // Destroy thread safe reference, unpinning version
-        }
-        SECTION("exception thrown on resolve") {
-            did_run_section = true;
-            r->begin_transaction(); // Get into state that'll throw exception on resolve
-            REQUIRE_THROWS(r->resolve_thread_safe_reference(std::move(*ref)));
-            r->commit_transaction();
-        }
-        {
-            // Clean up old versions by creating a write with dirty data
-            r->begin_transaction();
-            foo.row().set_int(0, 1);
-            r->commit_transaction();
-        }
-
-        catch2_ensure_section_run_workaround(did_run_section, "cleanup properly unpins version", [&](){
-            REQUIRE_THROWS(shared_group.begin_read(reference_version)); // Ensure unpinned
-        });
+        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("version mismatch") {
-#ifndef _MSC_VER // Visual C++'s buggy <future> needs its template argument to be default constructible so skip this test
         SECTION("resolves at older version") {
         SECTION("resolves at older version") {
             r->begin_transaction();
             r->begin_transaction();
-            auto num = create_object(r, "int object", {{"value", INT64_C(7)}});
+            Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
             r->commit_transaction();
             r->commit_transaction();
 
 
-            REQUIRE(num.row().get_int(0) == 7);
-            auto ref = std::async([config]() -> auto {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Object num = Object(r, "int object", 0);
-                REQUIRE(num.row().get_int(0) == 7);
+            ColKey col = num.get_object_schema().property_for_name("value")->column_key;
+            ObjKey k = num.obj().get_key();
 
 
-                r->begin_transaction();
-                num.row().set_int(0, 9);
-                r->commit_transaction();
+            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();
 
 
-                return r->obtain_thread_safe_reference(num);
-            }).get();
+                ref = num;
+            };
 
 
-            REQUIRE(num.row().get_int(0) == 7);
-            Object num_prime = r->resolve_thread_safe_reference(std::move(ref));
-            REQUIRE(num_prime.row().get_int(0) == 9);
-            REQUIRE(num.row().get_int(0) == 9);
+            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();
             r->begin_transaction();
-            num.row().set_int(0, 11);
+            num.obj().set(col, 11);
             r->commit_transaction();
             r->commit_transaction();
 
 
-            REQUIRE(num_prime.row().get_int(0) == 11);
-            REQUIRE(num.row().get_int(0) == 11);
+            REQUIRE(num_prime.obj().get<Int>(col) == 11);
+            REQUIRE(num.obj().get<Int>(col) == 11);
         }
         }
-#endif
 
 
         SECTION("resolve at newer version") {
         SECTION("resolve at newer version") {
             r->begin_transaction();
             r->begin_transaction();
-            auto num = create_object(r, "int object", {{"value", INT64_C(7)}});
+            Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
             r->commit_transaction();
             r->commit_transaction();
 
 
-            REQUIRE(num.row().get_int(0) == 7);
-            auto ref = r->obtain_thread_safe_reference(num);
-            std::thread([ref = std::move(ref), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Object num = Object(r, "int object", 0);
+            ColKey col = num.get_object_schema().property_for_name("value")->column_key;
+            ObjKey k = num.obj().get_key();
 
 
-                r->begin_transaction();
-                num.row().set_int(0, 9);
-                r->commit_transaction();
-                REQUIRE(num.row().get_int(0) == 9);
+            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);
 
 
-                Object num_prime = r->resolve_thread_safe_reference(std::move(ref));
-                REQUIRE(num_prime.row().get_int(0) == 9);
+                r2->begin_transaction();
+                num.obj().set(col, 9);
+                r2->commit_transaction();
+                REQUIRE(num.obj().get<Int>(col) == 9);
 
 
-                r->begin_transaction();
-                num_prime.row().set_int(0, 11);
-                r->commit_transaction();
+                Object num_prime = ref.resolve<Object>(r2);
+                REQUIRE(num_prime.obj().get<Int>(col) == 9);
 
 
-                REQUIRE(num.row().get_int(0) == 11);
-                REQUIRE(num_prime.row().get_int(0) == 11);
-            }).join();
+                r2->begin_transaction();
+                num_prime.obj().set(col, 11);
+                r2->commit_transaction();
 
 
-            REQUIRE(num.row().get_int(0) == 7);
+                REQUIRE(num.obj().get<Int>(col) == 11);
+                REQUIRE(num_prime.obj().get<Int>(col) == 11);
+            }
+
+            REQUIRE(num.obj().get<Int>(col) == 7);
             r->refresh();
             r->refresh();
-            REQUIRE(num.row().get_int(0) == 11);
+            REQUIRE(num.obj().get<Int>(col) == 11);
         }
         }
 
 
         SECTION("resolve at newer version when schema is specified") {
         SECTION("resolve at newer version when schema is specified") {
@@ -204,76 +191,79 @@ TEST_CASE("thread safe reference") {
             config.schema = schema;
             config.schema = schema;
             SharedRealm r = Realm::get_shared_realm(config);
             SharedRealm r = Realm::get_shared_realm(config);
             r->begin_transaction();
             r->begin_transaction();
-            auto num = create_object(r, "int object", {{"value", INT64_C(7)}});
+            Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
             r->commit_transaction();
             r->commit_transaction();
 
 
-            auto ref = r->obtain_thread_safe_reference(num);
+            ColKey col = num.get_object_schema().property_for_name("value")->column_key;
+            auto ref = ThreadSafeReference(num);
 
 
             r->begin_transaction();
             r->begin_transaction();
-            num.row().set_int(0, 9);
+            num.obj().set(col, 9);
             r->commit_transaction();
             r->commit_transaction();
 
 
-            REQUIRE_NOTHROW(r->resolve_thread_safe_reference(std::move(ref)));
+            REQUIRE_NOTHROW(ref.resolve<Object>(r));
         }
         }
 
 
         SECTION("resolve references at multiple versions") {
         SECTION("resolve references at multiple versions") {
             auto commit_new_num = [&](int64_t value) -> Object {
             auto commit_new_num = [&](int64_t value) -> Object {
                 r->begin_transaction();
                 r->begin_transaction();
-                auto num = create_object(r, "int object", {{"value", value}});
+                Object num = create_object(r, "int object", {{"value", value}});
                 r->commit_transaction();
                 r->commit_transaction();
                 return num;
                 return num;
             };
             };
 
 
-            auto ref1 = r->obtain_thread_safe_reference(commit_new_num(1));
-            auto ref2 = r->obtain_thread_safe_reference(commit_new_num(2));
-            std::thread([ref1 = std::move(ref1), ref2 = std::move(ref2), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Object num1 = r->resolve_thread_safe_reference(std::move(ref1));
-                Object num2 = r->resolve_thread_safe_reference(std::move(ref2));
+            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);
 
 
-                REQUIRE(num1.row().get_int(0) == 1);
-                REQUIRE(num2.row().get_int(0) == 2);
-            }).join();
+                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") {
     SECTION("same thread") {
         r->begin_transaction();
         r->begin_transaction();
-        auto num = create_object(r, "int object", {{"value", INT64_C(7)}});
+        Object num = create_object(r, "int object", {{"value", INT64_C(7)}});
         r->commit_transaction();
         r->commit_transaction();
 
 
-        REQUIRE(num.row().get_int(0) == 7);
-        auto ref = r->obtain_thread_safe_reference(num);
+        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;
         bool did_run_section = false;
 
 
         SECTION("same realm") {
         SECTION("same realm") {
             did_run_section = true;
             did_run_section = true;
             {
             {
-                Object num = r->resolve_thread_safe_reference(std::move(ref));
-                REQUIRE(num.row().get_int(0) == 7);
+                Object num = ref.resolve<Object>(r);
+                REQUIRE(num.obj().get<Int>(col) == 7);
                 r->begin_transaction();
                 r->begin_transaction();
-                num.row().set_int(0, 9);
+                num.obj().set(col, 9);
                 r->commit_transaction();
                 r->commit_transaction();
-                REQUIRE(num.row().get_int(0) == 9);
+                REQUIRE(num.obj().get<Int>(col) == 9);
             }
             }
-            REQUIRE(num.row().get_int(0) == 9);
+            REQUIRE(num.obj().get<Int>(col) == 9);
         }
         }
         SECTION("different realm") {
         SECTION("different realm") {
             did_run_section = true;
             did_run_section = true;
             {
             {
                 SharedRealm r = Realm::get_shared_realm(config);
                 SharedRealm r = Realm::get_shared_realm(config);
-                Object num = r->resolve_thread_safe_reference(std::move(ref));
-                REQUIRE(num.row().get_int(0) == 7);
+                Object num = ref.resolve<Object>(r);
+                REQUIRE(num.obj().get<Int>(col) == 7);
                 r->begin_transaction();
                 r->begin_transaction();
-                num.row().set_int(0, 9);
+                num.obj().set(col, 9);
                 r->commit_transaction();
                 r->commit_transaction();
-                REQUIRE(num.row().get_int(0) == 9);
+                REQUIRE(num.obj().get<Int>(col) == 9);
             }
             }
-            REQUIRE(num.row().get_int(0) == 7);
+            REQUIRE(num.obj().get<Int>(col) == 7);
         }
         }
         catch2_ensure_section_run_workaround(did_run_section, "same thread", [&](){
         catch2_ensure_section_run_workaround(did_run_section, "same thread", [&](){
             r->begin_transaction(); // advance to latest version by starting a write
             r->begin_transaction(); // advance to latest version by starting a write
-            REQUIRE(num.row().get_int(0) == 9);
+            REQUIRE(num.obj().get<Int>(col) == 9);
             r->cancel_transaction();
             r->cancel_transaction();
         });
         });
     }
     }
@@ -285,73 +275,77 @@ TEST_CASE("thread safe reference") {
             auto num = create_object(r, "int object", {{"value", INT64_C(0)}});
             auto num = create_object(r, "int object", {{"value", INT64_C(0)}});
             r->commit_transaction();
             r->commit_transaction();
 
 
-            auto ref_str = r->obtain_thread_safe_reference(str);
-            auto ref_num = r->obtain_thread_safe_reference(num);
-            std::thread([ref_str = std::move(ref_str), ref_num = std::move(ref_num), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Object str = r->resolve_thread_safe_reference(std::move(ref_str));
-                Object num = r->resolve_thread_safe_reference(std::move(ref_num));
+            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.row().get_string(0).is_null());
-                REQUIRE(num.row().get_int(0) == 0);
+                REQUIRE(str.obj().get<String>(col_str).is_null());
+                REQUIRE(num.obj().get<Int>(col_num) == 0);
 
 
-                r->begin_transaction();
-                str.row().set_string(0, "the meaning of life");
-                num.row().set_int(0, 42);
-                r->commit_transaction();
-            }).join();
+                r2->begin_transaction();
+                str.obj().set(col_str, "the meaning of life");
+                num.obj().set(col_num, 42);
+                r2->commit_transaction();
+            }
 
 
-            REQUIRE(str.row().get_string(0).is_null());
-            REQUIRE(num.row().get_int(0) == 0);
+            REQUIRE(str.obj().get<String>(col_str).is_null());
+            REQUIRE(num.obj().get<Int>(col_num) == 0);
 
 
             r->refresh();
             r->refresh();
 
 
-            REQUIRE(str.row().get_string(0) == "the meaning of life");
-            REQUIRE(num.row().get_int(0) == 42);
+            REQUIRE(str.obj().get<String>(col_str) == "the meaning of life");
+            REQUIRE(num.obj().get<Int>(col_num) == 42);
         }
         }
 
 
         SECTION("object list") {
         SECTION("object list") {
             r->begin_transaction();
             r->begin_transaction();
             auto zero = create_object(r, "int object", {{"value", INT64_C(0)}});
             auto zero = create_object(r, "int object", {{"value", INT64_C(0)}});
-            create_object(r, "int array object", {{"value", AnyVector{zero}}});
-            List list(r, *get_table(*r, "int array object"), 0, 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();
             r->commit_transaction();
 
 
             REQUIRE(list.size() == 1);
             REQUIRE(list.size() == 1);
-            REQUIRE(list.get(0).get_int(0) == 0);
-            auto ref = r->obtain_thread_safe_reference(list);
-            std::thread([ref = std::move(ref), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                List list = r->resolve_thread_safe_reference(std::move(ref));
+            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.size() == 1);
-                REQUIRE(list.get(0).get_int(0) == 0);
+                REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 0);
 
 
-                r->begin_transaction();
+                r2->begin_transaction();
                 list.remove_all();
                 list.remove_all();
-                auto one = create_object(r, "int object", {{"value", INT64_C(1)}});
-                auto two = create_object(r, "int object", {{"value", INT64_C(2)}});
-                list.add(one.row());
-                list.add(two.row());
-                r->commit_transaction();
+                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.size() == 2);
-                REQUIRE(list.get(0).get_int(0) == 1);
-                REQUIRE(list.get(1).get_int(0) == 2);
-            }).join();
+                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.size() == 1);
-            REQUIRE(list.get(0).get_int(0) == 0);
+            REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 0);
 
 
             r->refresh();
             r->refresh();
 
 
             REQUIRE(list.size() == 2);
             REQUIRE(list.size() == 2);
-            REQUIRE(list.get(0).get_int(0) == 1);
-            REQUIRE(list.get(1).get_int(0) == 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") {
         SECTION("sorted object results") {
             auto& table = *get_table(*r, "string object");
             auto& table = *get_table(*r, "string object");
-            auto results = Results(r, table.where().not_equal(0, "C")).sort({table, {{0}}, {false}});
+            auto col = table.get_column_key("value");
+            auto results = Results(r, table.where().not_equal(col, "C")).sort({{{col}}, {false}});
 
 
             r->begin_transaction();
             r->begin_transaction();
             create_object(r, "string object", {{"value", "A"s}});
             create_object(r, "string object", {{"value", "A"s}});
@@ -361,45 +355,46 @@ TEST_CASE("thread safe reference") {
             r->commit_transaction();
             r->commit_transaction();
 
 
             REQUIRE(results.size() == 3);
             REQUIRE(results.size() == 3);
-            REQUIRE(results.get(0).get_string(0) == "D");
-            REQUIRE(results.get(1).get_string(0) == "B");
-            REQUIRE(results.get(2).get_string(0) == "A");
-            auto ref = r->obtain_thread_safe_reference(results);
-            std::thread([ref = std::move(ref), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Results results = r->resolve_thread_safe_reference(std::move(ref));
+            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.size() == 3);
-                REQUIRE(results.get(0).get_string(0) == "D");
-                REQUIRE(results.get(1).get_string(0) == "B");
-                REQUIRE(results.get(2).get_string(0) == "A");
+                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->begin_transaction();
-                results.get(2).move_last_over();
-                results.get(0).move_last_over();
-                create_object(r, "string object", {{"value", "E"s}});
-                r->commit_transaction();
+                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.size() == 2);
-                REQUIRE(results.get(0).get_string(0) == "E");
-                REQUIRE(results.get(1).get_string(0) == "B");
-            }).join();
+                REQUIRE(results.get(0).get<StringData>(col) == "E");
+                REQUIRE(results.get(1).get<StringData>(col) == "B");
+            }
 
 
             REQUIRE(results.size() == 3);
             REQUIRE(results.size() == 3);
-            REQUIRE(results.get(0).get_string(0) == "D");
-            REQUIRE(results.get(1).get_string(0) == "B");
-            REQUIRE(results.get(2).get_string(0) == "A");
+            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();
             r->refresh();
 
 
             REQUIRE(results.size() == 2);
             REQUIRE(results.size() == 2);
-            REQUIRE(results.get(0).get_string(0) == "E");
-            REQUIRE(results.get(1).get_string(0) == "B");
+            REQUIRE(results.get(0).get<StringData>(col) == "E");
+            REQUIRE(results.get(1).get<StringData>(col) == "B");
         }
         }
 
 
         SECTION("distinct object results") {
         SECTION("distinct object results") {
             auto& table = *get_table(*r, "string object");
             auto& table = *get_table(*r, "string object");
-            auto results = Results(r, table.where()).distinct({table, {{0}}}).sort({{"value", true}});
+            auto col = table.get_column_key("value");
+            auto results = Results(r, table.where()).distinct({{{col}}}).sort({{"value", true}});
 
 
             r->begin_transaction();
             r->begin_transaction();
             create_object(r, "string object", {{"value", "A"s}});
             create_object(r, "string object", {{"value", "A"s}});
@@ -408,63 +403,64 @@ TEST_CASE("thread safe reference") {
             r->commit_transaction();
             r->commit_transaction();
 
 
             REQUIRE(results.size() == 2);
             REQUIRE(results.size() == 2);
-            REQUIRE(results.get(0).get_string(0) == "A");
-            REQUIRE(results.get(1).get_string(0) == "B");
-            auto ref = r->obtain_thread_safe_reference(results);
-            std::thread([ref = std::move(ref), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Results results = r->resolve_thread_safe_reference(std::move(ref));
+            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.size() == 2);
-                REQUIRE(results.get(0).get_string(0) == "A");
-                REQUIRE(results.get(1).get_string(0) == "B");
+                REQUIRE(results.get(0).get<StringData>(col) == "A");
+                REQUIRE(results.get(1).get<StringData>(col) == "B");
 
 
-                r->begin_transaction();
-                results.get(0).move_last_over();
-                create_object(r, "string object", {{"value", "C"s}});
-                r->commit_transaction();
+                r2->begin_transaction();
+                results.get(0).remove();
+                create_object(r2, "string object", {{"value", "C"s}});
+                r2->commit_transaction();
 
 
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.size() == 3);
-                REQUIRE(results.get(0).get_string(0) == "A");
-                REQUIRE(results.get(1).get_string(0) == "B");
-                REQUIRE(results.get(2).get_string(0) == "C");
-            }).join();
+                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.size() == 2);
-            REQUIRE(results.get(0).get_string(0) == "A");
-            REQUIRE(results.get(1).get_string(0) == "B");
+            REQUIRE(results.get(0).get<StringData>(col) == "A");
+            REQUIRE(results.get(1).get<StringData>(col) == "B");
 
 
             r->refresh();
             r->refresh();
 
 
             REQUIRE(results.size() == 3);
             REQUIRE(results.size() == 3);
-            REQUIRE(results.get(0).get_string(0) == "A");
-            REQUIRE(results.get(1).get_string(0) == "B");
-            REQUIRE(results.get(2).get_string(0) == "C");
+            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") {
         SECTION("int list") {
             r->begin_transaction();
             r->begin_transaction();
-            create_object(r, "int array", {{"value", AnyVector{INT64_C(0)}}});
-            List list(r, *get_table(*r, "int array"), 0, 0);
+            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();
             r->commit_transaction();
 
 
-            auto ref = r->obtain_thread_safe_reference(list);
-            std::thread([ref = std::move(ref), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                List list = r->resolve_thread_safe_reference(std::move(ref));
+            auto ref = ThreadSafeReference(list);
+            {
+                SharedRealm r2 = Realm::get_shared_realm(config);
+                List list = ref.resolve<List>(r2);
                 REQUIRE(list.size() == 1);
                 REQUIRE(list.size() == 1);
                 REQUIRE(list.get<int64_t>(0) == 0);
                 REQUIRE(list.get<int64_t>(0) == 0);
 
 
-                r->begin_transaction();
+                r2->begin_transaction();
                 list.remove_all();
                 list.remove_all();
-                list.add(1);
-                list.add(2);
-                r->commit_transaction();
+                list.add(int64_t(1));
+                list.add(int64_t(2));
+                r2->commit_transaction();
 
 
                 REQUIRE(list.size() == 2);
                 REQUIRE(list.size() == 2);
                 REQUIRE(list.get<int64_t>(0) == 1);
                 REQUIRE(list.get<int64_t>(0) == 1);
                 REQUIRE(list.get<int64_t>(1) == 2);
                 REQUIRE(list.get<int64_t>(1) == 2);
-            }).join();
+            };
 
 
             REQUIRE(list.size() == 1);
             REQUIRE(list.size() == 1);
             REQUIRE(list.get<int64_t>(0) == 0);
             REQUIRE(list.get<int64_t>(0) == 0);
@@ -478,8 +474,9 @@ TEST_CASE("thread safe reference") {
 
 
         SECTION("sorted int results") {
         SECTION("sorted int results") {
             r->begin_transaction();
             r->begin_transaction();
-            create_object(r, "int array", {{"value", AnyVector{INT64_C(0), INT64_C(2), INT64_C(1)}}});
-            List list(r, *get_table(*r, "int array"), 0, 0);
+            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();
             r->commit_transaction();
 
 
             auto results = list.sort({{"self", true}});
             auto results = list.sort({{"self", true}});
@@ -488,10 +485,11 @@ TEST_CASE("thread safe reference") {
             REQUIRE(results.get<int64_t>(0) == 0);
             REQUIRE(results.get<int64_t>(0) == 0);
             REQUIRE(results.get<int64_t>(1) == 1);
             REQUIRE(results.get<int64_t>(1) == 1);
             REQUIRE(results.get<int64_t>(2) == 2);
             REQUIRE(results.get<int64_t>(2) == 2);
-            auto ref = r->obtain_thread_safe_reference(results);
+            auto ref = ThreadSafeReference(results);
             std::thread([ref = std::move(ref), config]() mutable {
             std::thread([ref = std::move(ref), config]() mutable {
+                config.scheduler = util::Scheduler::get_frozen();
                 SharedRealm r = Realm::get_shared_realm(config);
                 SharedRealm r = Realm::get_shared_realm(config);
-                Results results = r->resolve_thread_safe_reference(std::move(ref));
+                Results results = ref.resolve<Results>(r);
 
 
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.get<int64_t>(0) == 0);
                 REQUIRE(results.get<int64_t>(0) == 0);
@@ -499,9 +497,10 @@ TEST_CASE("thread safe reference") {
                 REQUIRE(results.get<int64_t>(2) == 2);
                 REQUIRE(results.get<int64_t>(2) == 2);
 
 
                 r->begin_transaction();
                 r->begin_transaction();
-                List list(r, *get_table(*r, "int array"), 0, 0);
+                auto table = get_table(*r, "int array");
+                List list(r, *table->begin(), table->get_column_key("value"));
                 list.remove(1);
                 list.remove(1);
-                list.add(-1);
+                list.add(int64_t(-1));
                 r->commit_transaction();
                 r->commit_transaction();
 
 
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.size() == 3);
@@ -525,8 +524,10 @@ TEST_CASE("thread safe reference") {
 
 
         SECTION("distinct int results") {
         SECTION("distinct int results") {
             r->begin_transaction();
             r->begin_transaction();
-            create_object(r, "int array", {{"value", AnyVector{INT64_C(3), INT64_C(2), INT64_C(1), INT64_C(1), INT64_C(2)}}});
-            List list(r, *get_table(*r, "int array"), 0, 0);
+            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();
             r->commit_transaction();
 
 
             auto results = list.as_results().distinct({"self"}).sort({{"self", true}});
             auto results = list.as_results().distinct({"self"}).sort({{"self", true}});
@@ -536,10 +537,11 @@ TEST_CASE("thread safe reference") {
             REQUIRE(results.get<int64_t>(1) == 2);
             REQUIRE(results.get<int64_t>(1) == 2);
             REQUIRE(results.get<int64_t>(2) == 3);
             REQUIRE(results.get<int64_t>(2) == 3);
 
 
-            auto ref = r->obtain_thread_safe_reference(results);
+            auto ref = ThreadSafeReference(results);
             std::thread([ref = std::move(ref), config]() mutable {
             std::thread([ref = std::move(ref), config]() mutable {
+                config.scheduler = util::Scheduler::get_frozen();
                 SharedRealm r = Realm::get_shared_realm(config);
                 SharedRealm r = Realm::get_shared_realm(config);
-                Results results = r->resolve_thread_safe_reference(std::move(ref));
+                Results results = ref.resolve<Results>(r);
 
 
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.size() == 3);
                 REQUIRE(results.get<int64_t>(0) == 1);
                 REQUIRE(results.get<int64_t>(0) == 1);
@@ -547,7 +549,8 @@ TEST_CASE("thread safe reference") {
                 REQUIRE(results.get<int64_t>(2) == 3);
                 REQUIRE(results.get<int64_t>(2) == 3);
 
 
                 r->begin_transaction();
                 r->begin_transaction();
-                List list(r, *get_table(*r, "int array"), 0, 0);
+                auto table = get_table(*r, "int array");
+                List list(r, *table->begin(), table->get_column_key("value"));
                 list.remove(1);
                 list.remove(1);
                 list.remove(0);
                 list.remove(0);
                 r->commit_transaction();
                 r->commit_transaction();
@@ -570,49 +573,49 @@ TEST_CASE("thread safe reference") {
         }
         }
 
 
         SECTION("multiple types") {
         SECTION("multiple types") {
-            auto results = Results(r, get_table(*r, "int object")->where().equal(0, 5));
+            auto results = Results(r, get_table(*r, "int object")->where().equal(int_obj_col, 5));
 
 
             r->begin_transaction();
             r->begin_transaction();
             auto num = create_object(r, "int object", {{"value", INT64_C(5)}});
             auto num = create_object(r, "int object", {{"value", INT64_C(5)}});
-            create_object(r, "int array object", {{"value", AnyVector{}}});
-            List list(r, *get_table(*r, "int array object"), 0, 0);
+            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();
             r->commit_transaction();
 
 
             REQUIRE(list.size() == 0);
             REQUIRE(list.size() == 0);
             REQUIRE(results.size() == 1);
             REQUIRE(results.size() == 1);
-            REQUIRE(results.get(0).get_int(0) == 5);
-            auto ref_num = r->obtain_thread_safe_reference(num);
-            auto ref_list = r->obtain_thread_safe_reference(list);
-            auto ref_results = r->obtain_thread_safe_reference(results);
-            std::thread([ref_num = std::move(ref_num), ref_list = std::move(ref_list),
-                         ref_results = std::move(ref_results), config]() mutable {
-                SharedRealm r = Realm::get_shared_realm(config);
-                Object num = r->resolve_thread_safe_reference(std::move(ref_num));
-                List list = r->resolve_thread_safe_reference(std::move(ref_list));
-                Results results = r->resolve_thread_safe_reference(std::move(ref_results));
+            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(list.size() == 0);
                 REQUIRE(results.size() == 1);
                 REQUIRE(results.size() == 1);
-                REQUIRE(results.get(0).get_int(0) == 5);
+                REQUIRE(results.get(0).get<int64_t>(int_obj_col) == 5);
 
 
-                r->begin_transaction();
-                num.row().set_int(0, 6);
-                list.add(num.row().get_index());
-                r->commit_transaction();
+                r2->begin_transaction();
+                num.obj().set_all(6);
+                list.add(num.obj().get_key());
+                r2->commit_transaction();
 
 
                 REQUIRE(list.size() == 1);
                 REQUIRE(list.size() == 1);
-                REQUIRE(list.get(0).get_int(0) == 6);
+                REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 6);
                 REQUIRE(results.size() == 0);
                 REQUIRE(results.size() == 0);
-            }).join();
+            }
 
 
             REQUIRE(list.size() == 0);
             REQUIRE(list.size() == 0);
             REQUIRE(results.size() == 1);
             REQUIRE(results.size() == 1);
-            REQUIRE(results.get(0).get_int(0) == 5);
+            REQUIRE(results.get(0).get<int64_t>(int_obj_col) == 5);
 
 
             r->refresh();
             r->refresh();
 
 
             REQUIRE(list.size() == 1);
             REQUIRE(list.size() == 1);
-            REQUIRE(list.get(0).get_int(0) == 6);
+            REQUIRE(list.get(0).get<int64_t>(int_obj_col) == 6);
             REQUIRE(results.size() == 0);
             REQUIRE(results.size() == 0);
         }
         }
     }
     }
@@ -620,13 +623,13 @@ TEST_CASE("thread safe reference") {
     SECTION("resolve at version where handed over thing has been deleted") {
     SECTION("resolve at version where handed over thing has been deleted") {
         Object obj;
         Object obj;
         auto delete_and_resolve = [&](auto&& list) {
         auto delete_and_resolve = [&](auto&& list) {
-            auto ref = r->obtain_thread_safe_reference(list);
+            auto ref = ThreadSafeReference(list);
 
 
             r->begin_transaction();
             r->begin_transaction();
-            obj.row().move_last_over();
+            obj.obj().remove();
             r->commit_transaction();
             r->commit_transaction();
 
 
-            return r->resolve_thread_safe_reference(std::move(ref));
+            return ref.resolve<typename std::remove_reference<decltype(list)>::type>(r);
         };
         };
 
 
         SECTION("object") {
         SECTION("object") {
@@ -640,7 +643,8 @@ TEST_CASE("thread safe reference") {
         SECTION("object list") {
         SECTION("object list") {
             r->begin_transaction();
             r->begin_transaction();
             obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
             obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
-            List list(r, *get_table(*r, "int array object"), 0, 0);
+            auto col = get_table(*r, "int array object")->get_column_key("value");
+            List list(r, obj.obj(), col);
             r->commit_transaction();
             r->commit_transaction();
 
 
             REQUIRE(!delete_and_resolve(list).is_valid());
             REQUIRE(!delete_and_resolve(list).is_valid());
@@ -648,8 +652,9 @@ TEST_CASE("thread safe reference") {
 
 
         SECTION("int list") {
         SECTION("int list") {
             r->begin_transaction();
             r->begin_transaction();
-            obj = create_object(r, "int array", {{"value", AnyVector{INT64_C(1)}}});
-            List list(r, *get_table(*r, "int array"), 0, 0);
+            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();
             r->commit_transaction();
 
 
             REQUIRE(!delete_and_resolve(list).is_valid());
             REQUIRE(!delete_and_resolve(list).is_valid());
@@ -658,7 +663,8 @@ TEST_CASE("thread safe reference") {
         SECTION("object results") {
         SECTION("object results") {
             r->begin_transaction();
             r->begin_transaction();
             obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
             obj = create_object(r, "int array object", {{"value", AnyVector{AnyDict{{"value", INT64_C(0)}}}}});
-            List list(r, *get_table(*r, "int array object"), 0, 0);
+            auto col = get_table(*r, "int array object")->get_column_key("value");
+            List list(r, obj.obj(), col);
             r->commit_transaction();
             r->commit_transaction();
 
 
             auto results = delete_and_resolve(list.sort({{"value", true}}));
             auto results = delete_and_resolve(list.sort({{"value", true}}));
@@ -668,22 +674,193 @@ TEST_CASE("thread safe reference") {
 
 
         SECTION("int results") {
         SECTION("int results") {
             r->begin_transaction();
             r->begin_transaction();
-            obj = create_object(r, "int array", {{"value", AnyVector{INT64_C(1)}}});
-            List list(r, *get_table(*r, "int array"), 0, 0);
+            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();
             r->commit_transaction();
 
 
-            auto results = delete_and_resolve(list.sort({{"self", true}}));
+            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.is_valid());
-            REQUIRE(results.size() == 0);
+            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("lifetime") {
         SECTION("retains source realm") { // else version will become unpinned
         SECTION("retains source realm") { // else version will become unpinned
-            auto ref = r->obtain_thread_safe_reference(foo);
+            auto ref = ThreadSafeReference(foo);
             r = nullptr;
             r = nullptr;
             r = Realm::get_shared_realm(config);
             r = Realm::get_shared_realm(config);
-            REQUIRE_NOTHROW(r->resolve_thread_safe_reference(std::move(ref)));
+            REQUIRE_NOTHROW(ref.resolve<Object>(r));
         }
         }
     }
     }
 
 
@@ -693,17 +870,17 @@ TEST_CASE("thread safe reference") {
         r->commit_transaction();
         r->commit_transaction();
         REQUIRE(num.get_object_schema().name == "int object");
         REQUIRE(num.get_object_schema().name == "int object");
 
 
-        auto ref = r->obtain_thread_safe_reference(num);
-        std::thread([ref = std::move(ref), config]() mutable {
-            SharedRealm r = Realm::get_shared_realm(config);
-            Object num = r->resolve_thread_safe_reference(std::move(ref));
+        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");
             REQUIRE(num.get_object_schema().name == "int object");
-        }).join();
+        }
     }
     }
 
 
-    SECTION("disallow multiple resolves") {
-        auto ref = r->obtain_thread_safe_reference(foo);
-        r->resolve_thread_safe_reference(std::move(ref));
-        REQUIRE_THROWS(r->resolve_thread_safe_reference(std::move(ref)));
+    SECTION("allow multiple resolves") {
+        auto ref = ThreadSafeReference(foo);
+        ref.resolve<Object>(r);
+        REQUIRE_NOTHROW(ref.resolve<Object>(r));
     }
     }
 }
 }

File diff suppressed because it is too large
+ 195 - 429
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/transaction_log_parsing.cpp


+ 3 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/index_helpers.hpp

@@ -29,8 +29,9 @@
 } while (0)
 } while (0)
 
 
 #define REQUIRE_COLUMN_INDICES(columns, col, ...) do { \
 #define REQUIRE_COLUMN_INDICES(columns, col, ...) do { \
-    REQUIRE((columns).size() > col); \
-    REQUIRE_INDICES((columns)[col], __VA_ARGS__); \
+    auto it = (columns).find(col); \
+    REQUIRE(it != (columns).end()); \
+    REQUIRE_INDICES(it->second, __VA_ARGS__); \
 } while (0)
 } while (0)
 
 
 #define REQUIRE_MOVES(c, ...) do { \
 #define REQUIRE_MOVES(c, ...) do { \

+ 32 - 24
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp

@@ -29,6 +29,7 @@
 #include "schema.hpp"
 #include "schema.hpp"
 #endif
 #endif
 
 
+#include <realm/db.hpp>
 #include <realm/disable_sync_to_disk.hpp>
 #include <realm/disable_sync_to_disk.hpp>
 #include <realm/history.hpp>
 #include <realm/history.hpp>
 #include <realm/string_data.hpp>
 #include <realm/string_data.hpp>
@@ -82,7 +83,17 @@ TestFile::TestFile()
 
 
 TestFile::~TestFile()
 TestFile::~TestFile()
 {
 {
-    unlink(path.c_str());
+    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()
 InMemoryTestFile::InMemoryTestFile()
@@ -123,8 +134,9 @@ SyncTestFile::SyncTestFile(SyncServer& server, std::string name, bool is_partial
     schema_mode = SchemaMode::Additive;
     schema_mode = SchemaMode::Additive;
 }
 }
 
 
-SyncServer::SyncServer(StartImmediately start_immediately)
-: m_server(util::make_temp_dir(), util::none, ([&] {
+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;
     using namespace std::literals::chrono_literals;
 
 
     sync::Server::Config config;
     sync::Server::Config config;
@@ -135,15 +147,13 @@ SyncServer::SyncServer(StartImmediately start_immediately)
 #else
 #else
     config.logger = new TestLogger;
     config.logger = new TestLogger;
 #endif
 #endif
-    config.log_compaction_clock = this;
-#if REALM_SYNC_VER_MAJOR > 4 || (REALM_SYNC_VER_MAJOR == 4 && REALM_SYNC_VER_MINOR >= 7)
+    m_logger.reset(config.logger);
+    config.history_compaction_clock = this;
     config.disable_history_compaction = false;
     config.disable_history_compaction = false;
-#else
-    config.enable_log_compaction = true;
-#endif
     config.history_ttl = 1s;
     config.history_ttl = 1s;
     config.history_compaction_interval = 1s;
     config.history_compaction_interval = 1s;
     config.state_realm_dir = util::make_temp_dir();
     config.state_realm_dir = util::make_temp_dir();
+    config.listen_address = "127.0.0.1";
 
 
     return config;
     return config;
 })())
 })())
@@ -154,20 +164,8 @@ SyncServer::SyncServer(StartImmediately start_immediately)
     SyncManager::shared().set_log_level(util::Logger::Level::off);
     SyncManager::shared().set_log_level(util::Logger::Level::off);
 #endif
 #endif
 
 
-    uint64_t port;
-    while (true) {
-        // Try to pick a random available port, or loop forever if other
-        // problems occur because there's no specific error for "port in use"
-        try {
-            port = fastrand(65536 - 1000) + 1000;
-            m_server.start("127.0.0.1", util::to_string(port));
-            break;
-        }
-        catch (std::runtime_error const&) {
-            continue;
-        }
-    }
-    m_url = util::format("realm://127.0.0.1:%1", port);
+    m_server.start();
+    m_url = util::format("realm://127.0.0.1:%1", m_server.listen_endpoint().port());
     if (start_immediately)
     if (start_immediately)
         start();
         start();
 }
 }
@@ -305,17 +303,27 @@ private:
     std::map<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
     std::map<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
 } s_worker;
 } s_worker;
 
 
-void advance_and_notify(Realm& realm)
+void on_change_but_no_notify(Realm& realm)
 {
 {
     s_worker.on_change(_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
     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();
     realm.notify();
 }
 }
 
 
 #else // REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
 #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)
 void advance_and_notify(Realm& realm)
 {
 {
-    _impl::RealmCoordinator::get_existing_coordinator(realm.config().path)->on_change();
+    on_change_but_no_notify(realm);
     realm.notify();
     realm.notify();
 }
 }
 #endif
 #endif

+ 21 - 13
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp

@@ -22,27 +22,28 @@
 #include "shared_realm.hpp"
 #include "shared_realm.hpp"
 #include "util/tagged_bool.hpp"
 #include "util/tagged_bool.hpp"
 
 
-#include <realm/group_shared.hpp>
 #include <realm/util/logger.hpp>
 #include <realm/util/logger.hpp>
 #include <realm/util/optional.hpp>
 #include <realm/util/optional.hpp>
 
 
+#include <thread>
+
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
 #include "sync/sync_config.hpp"
 #include "sync/sync_config.hpp"
 
 
 #include <realm/sync/client.hpp>
 #include <realm/sync/client.hpp>
 #include <realm/sync/server.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 {
 namespace realm {
-struct SyncConfig;
 class Schema;
 class Schema;
 enum class SyncSessionStopPolicy;
 enum class SyncSessionStopPolicy;
+struct DBOptions;
+struct SyncConfig;
 }
 }
 
 
-// {"identity":"test", "access": ["download", "upload"]}
-static const std::string s_test_token = "eyJpZGVudGl0eSI6InRlc3QiLCAiYWNjZXNzIjogWyJkb3dubG9hZCIsICJ1cGxvYWQiXX0=";
-
-#endif
-
 class JoiningThread {
 class JoiningThread {
 public:
 public:
     template<typename... Args>
     template<typename... Args>
@@ -59,13 +60,16 @@ struct TestFile : realm::Realm::Config {
     TestFile();
     TestFile();
     ~TestFile();
     ~TestFile();
 
 
-    auto options() const
+    // The file should outlive the object, ie. should not be deleted in destructor
+    void persist()
     {
     {
-        realm::SharedGroupOptions options;
-        options.durability = in_memory ? realm::SharedGroupOptions::Durability::MemOnly
-                                       : realm::SharedGroupOptions::Durability::Full;
-        return options;
+        m_persist = true;
     }
     }
+
+    realm::DBOptions options() const;
+
+private:
+    bool m_persist = false;
 };
 };
 
 
 struct InMemoryTestFile : TestFile {
 struct InMemoryTestFile : TestFile {
@@ -73,6 +77,7 @@ struct InMemoryTestFile : TestFile {
 };
 };
 
 
 void advance_and_notify(realm::Realm& realm);
 void advance_and_notify(realm::Realm& realm);
+void on_change_but_no_notify(realm::Realm& realm);
 
 
 #if REALM_ENABLE_SYNC
 #if REALM_ENABLE_SYNC
 
 
@@ -88,7 +93,7 @@ using StartImmediately = realm::util::TaggedBool<class StartImmediatelyTag>;
 
 
 class SyncServer : private realm::sync::Clock {
 class SyncServer : private realm::sync::Clock {
 public:
 public:
-    SyncServer(StartImmediately start_immediately=true);
+    SyncServer(StartImmediately start_immediately=true, std::string local_dir = "");
     ~SyncServer();
     ~SyncServer();
 
 
     void start();
     void start();
@@ -96,6 +101,7 @@ public:
 
 
     std::string url_for_realm(realm::StringData realm_name) const;
     std::string url_for_realm(realm::StringData realm_name) const;
     std::string base_url() const { return m_url; }
     std::string base_url() const { return m_url; }
+    std::string local_root_dir() const { return m_local_root_dir; }
 
 
     template <class R, class P>
     template <class R, class P>
     void advance_clock(std::chrono::duration<R, P> duration = std::chrono::seconds(1)) noexcept
     void advance_clock(std::chrono::duration<R, P> duration = std::chrono::seconds(1)) noexcept
@@ -104,6 +110,8 @@ public:
     }
     }
 
 
 private:
 private:
+    std::string m_local_root_dir;
+    std::unique_ptr<realm::util::Logger> m_logger;
     realm::sync::Server m_server;
     realm::sync::Server m_server;
     std::thread m_thread;
     std::thread m_thread;
     std::string m_url;
     std::string m_url;

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/build.sh

@@ -23,7 +23,7 @@ rm -rf ci.build
 mkdir -p ci.build
 mkdir -p ci.build
 cd ci.build
 cd ci.build
 
 
-cmake_flags=""
+cmake_flags="-DCMAKE_CXX_FLAGS=-Werror"
 if [ "${flavor}" = "android" ]; then
 if [ "${flavor}" = "android" ]; then
   [ -z $ANDROID_NDK_PATH ] && (echo "ANDROID_NDK_PATH is not set!"; exit 1)
   [ -z $ANDROID_NDK_PATH ] && (echo "ANDROID_NDK_PATH is not set!"; exit 1)
   cmake_flags="-DREALM_PLATFORM=Android -DANDROID_NDK=${ANDROID_NDK_PATH}"
   cmake_flags="-DREALM_PLATFORM=Android -DANDROID_NDK=${ANDROID_NDK_PATH}"

+ 4 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/workflow/test_coverage.sh

@@ -20,15 +20,17 @@ if [ "${TMPDIR}" = "" ]; then
 fi
 fi
 
 
 echo "TMPDIR: ${TMPDIR}"
 echo "TMPDIR: ${TMPDIR}"
-ls "${TMPDIR}/realm*" | while read filename; do rm -rf "$filename"; done
+find $TMPDIR -name 'realm*' -exec rm -rf "{}" \+ || true
 rm -rf coverage.build
 rm -rf coverage.build
 mkdir -p coverage.build
 mkdir -p coverage.build
 cd coverage.build
 cd coverage.build
 
 
-cmake_flags=""
+cmake_flags="-DCMAKE_CXX_FLAGS=-Werror"
 if [ "${sync}" = "sync" ]; then
 if [ "${sync}" = "sync" ]; then
     cmake_flags="${cmake_flags} -DREALM_ENABLE_SYNC=1 -DREALM_ENABLE_SERVER=1 -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}"
     cmake_flags="${cmake_flags} -DREALM_ENABLE_SYNC=1 -DREALM_ENABLE_SERVER=1 -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}"
 fi
 fi
 
 
 cmake ${cmake_flags} -DCMAKE_BUILD_TYPE=Coverage -DDEPENDENCIES_FILE="dependencies${deps_suffix}.list" ..
 cmake ${cmake_flags} -DCMAKE_BUILD_TYPE=Coverage -DDEPENDENCIES_FILE="dependencies${deps_suffix}.list" ..
 make VERBOSE=1 -j${nprocs} generate-coverage-cobertura
 make VERBOSE=1 -j${nprocs} generate-coverage-cobertura
+
+find $TMPDIR -name 'realm*' -exec rm -rf "{}" \+ || true

+ 4 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.hpp

@@ -44,7 +44,7 @@ public:
     id box(realm::List&&);
     id box(realm::List&&);
     id box(realm::Results&&);
     id box(realm::Results&&);
     id box(realm::Object&&);
     id box(realm::Object&&);
-    id box(realm::RowExpr);
+    id box(realm::Obj&&);
 
 
     id box(bool v) { return @(v); }
     id box(bool v) { return @(v); }
     id box(double v) { return @(v); }
     id box(double v) { return @(v); }
@@ -60,8 +60,8 @@ public:
     id box(realm::util::Optional<float> v) { return v ? @(*v) : NSNull.null; }
     id box(realm::util::Optional<float> v) { return v ? @(*v) : NSNull.null; }
     id box(realm::util::Optional<int64_t> v) { return v ? @(*v) : NSNull.null; }
     id box(realm::util::Optional<int64_t> v) { return v ? @(*v) : NSNull.null; }
 
 
-    void will_change(realm::Row const&, realm::Property const&);
-    void will_change(realm::Object& obj, realm::Property const& prop) { will_change(obj.row(), prop); }
+    void will_change(realm::Obj const&, realm::Property const&);
+    void will_change(realm::Object& obj, realm::Property const& prop) { will_change(obj.obj(), prop); }
     void did_change();
     void did_change();
 
 
     RLMOptionalId value_for_property(id dict, realm::Property const&, size_t prop_index);
     RLMOptionalId value_for_property(id dict, realm::Property const&, size_t prop_index);
@@ -79,7 +79,7 @@ public:
     }
     }
 
 
     template<typename T>
     template<typename T>
-    T unbox(id v, realm::CreatePolicy = realm::CreatePolicy::Skip, size_t = 0);
+    T unbox(id v, realm::CreatePolicy = realm::CreatePolicy::Skip, realm::ObjKey = {});
 
 
     bool is_null(id v) { return v == NSNull.null; }
     bool is_null(id v) { return v == NSNull.null; }
     id null_value() { return NSNull.null; }
     id null_value() { return NSNull.null; }

+ 83 - 68
Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.mm

@@ -34,34 +34,48 @@
 
 
 #import <objc/runtime.h>
 #import <objc/runtime.h>
 #import <objc/message.h>
 #import <objc/message.h>
-#import <realm/descriptor.hpp>
 
 
 #pragma mark - Helper functions
 #pragma mark - Helper functions
 
 
+namespace realm {
+template<>
+Obj ConstObj::get<Obj>(ColKey col) const {
+    ObjKey key = get<ObjKey>(col);
+    return key ? get_target_table(col)->get_object(key) : Obj();
+}
+}
+
 namespace {
 namespace {
+using realm::ColKey;
+
+template<typename T>
+bool is_null(T const& v) {
+    return !v;
+}
+template<>
+bool is_null(realm::Timestamp const& v) {
+    return v.is_null();
+}
+
 template<typename T>
 template<typename T>
 T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
 T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
     RLMVerifyAttached(obj);
     RLMVerifyAttached(obj);
-    return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
+    return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].column_key);
 }
 }
 
 
 template<typename T>
 template<typename T>
 id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
 id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
     RLMVerifyAttached(obj);
     RLMVerifyAttached(obj);
     auto& prop = obj->_info->objectSchema->persisted_properties[index];
     auto& prop = obj->_info->objectSchema->persisted_properties[index];
-    auto col = prop.table_column;
-    if (obj->_row.is_null(col)) {
-        return nil;
-    }
-
     RLMAccessorContext ctx(obj, &prop);
     RLMAccessorContext ctx(obj, &prop);
-    return ctx.box(obj->_row.get<T>(col));
+    auto value = obj->_row.get<T>(prop.column_key);
+    return is_null(value) ? nil : ctx.box(std::move(value));
 }
 }
 
 
 template<typename T>
 template<typename T>
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key, T val) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
-    obj->_row.set(colIndex, val);
+    obj->_row.set(key, val);
 }
 }
 
 
 template<typename Fn>
 template<typename Fn>
@@ -74,55 +88,55 @@ auto translateError(Fn&& fn) {
     }
     }
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSString *const val) {
               __unsafe_unretained NSString *const val) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
     translateError([&] {
     translateError([&] {
-        obj->_row.set(colIndex, RLMStringDataWithNSString(val));
+        obj->_row.set(key, RLMStringDataWithNSString(val));
     });
     });
 }
 }
 
 
 [[gnu::noinline]]
 [[gnu::noinline]]
-void setNull(realm::Row& row, size_t col) {
-    translateError([&] { row.set_null(col); });
+void setNull(realm::Obj& row, ColKey key) {
+    translateError([&] { row.set_null(key); });
 }
 }
 
 
 void setValue(__unsafe_unretained RLMObjectBase *const obj,
 void setValue(__unsafe_unretained RLMObjectBase *const obj,
-              NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
+              ColKey key, __unsafe_unretained NSDate *const date) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
     if (date) {
     if (date) {
-        obj->_row.set(colIndex, RLMTimestampForNSDate(date));
+        obj->_row.set(key, RLMTimestampForNSDate(date));
     }
     }
     else {
     else {
-        setNull(obj->_row, colIndex);
+        setNull(obj->_row, key);
     }
     }
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSData *const data) {
               __unsafe_unretained NSData *const data) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
     translateError([&] {
     translateError([&] {
-        obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
+        obj->_row.set(key, RLMBinaryDataForNSData(data));
     });
     });
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained RLMObjectBase *const val) {
               __unsafe_unretained RLMObjectBase *const val) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
     if (!val) {
     if (!val) {
-        obj->_row.nullify_link(colIndex);
+        obj->_row.set(key, realm::null());
         return;
         return;
     }
     }
 
 
     RLMAddObjectToRealm(val, obj->_realm, RLMUpdatePolicyError);
     RLMAddObjectToRealm(val, obj->_realm, RLMUpdatePolicyError);
 
 
     // make sure it is the correct type
     // make sure it is the correct type
-    if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) {
+    if (val->_row.get_table() != obj->_row.get_table()->get_link_target(key)) {
         @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
         @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
                             val->_objectSchema.className,
                             val->_objectSchema.className,
-                            obj->_info->propertyForTableColumn(colIndex).objectClassName);
+                            obj->_info->propertyForTableColumn(key).objectClassName);
     }
     }
-    obj->_row.set_link(colIndex, val->_row.get_index());
+    obj->_row.set(key, val->_row.get_key());
 }
 }
 
 
 // array getter/setter
 // array getter/setter
@@ -132,13 +146,13 @@ RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger prop
     return [[RLMManagedArray alloc] initWithParent:obj property:prop];
     return [[RLMManagedArray alloc] initWithParent:obj property:prop];
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained id<NSFastEnumeration> const value) {
               __unsafe_unretained id<NSFastEnumeration> const value) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
-    auto prop = obj->_info->propertyForTableColumn(colIndex);
+    auto prop = obj->_info->propertyForTableColumn(key);
     RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
     RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
 
 
-    realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
+    realm::List list(obj->_realm->_realm, obj->_row, key);
     RLMClassInfo *info = obj->_info;
     RLMClassInfo *info = obj->_info;
     if (list.get_type() == realm::PropertyType::Object) {
     if (list.get_type() == realm::PropertyType::Object) {
         info = &obj->_info->linkTargetType(prop.index);
         info = &obj->_info->linkTargetType(prop.index);
@@ -149,51 +163,51 @@ void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
     });
     });
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSNumber<RLMInt> *const intObject) {
               __unsafe_unretained NSNumber<RLMInt> *const intObject) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
 
 
     if (intObject) {
     if (intObject) {
-        obj->_row.set(colIndex, intObject.longLongValue);
+        obj->_row.set(key, intObject.longLongValue);
     }
     }
     else {
     else {
-        setNull(obj->_row, colIndex);
+        setNull(obj->_row, key);
     }
     }
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
               __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
 
 
     if (floatObject) {
     if (floatObject) {
-        obj->_row.set(colIndex, floatObject.floatValue);
+        obj->_row.set(key, floatObject.floatValue);
     }
     }
     else {
     else {
-        setNull(obj->_row, colIndex);
+        setNull(obj->_row, key);
     }
     }
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
               __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
 
 
     if (doubleObject) {
     if (doubleObject) {
-        obj->_row.set(colIndex, doubleObject.doubleValue);
+        obj->_row.set(key, doubleObject.doubleValue);
     }
     }
     else {
     else {
-        setNull(obj->_row, colIndex);
+        setNull(obj->_row, key);
     }
     }
 }
 }
 
 
-void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
               __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
               __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
     RLMVerifyInWriteTransaction(obj);
     RLMVerifyInWriteTransaction(obj);
 
 
     if (boolObject) {
     if (boolObject) {
-        obj->_row.set(colIndex, (bool)boolObject.boolValue);
+        obj->_row.set(key, (bool)boolObject.boolValue);
     }
     }
     else {
     else {
-        setNull(obj->_row, colIndex);
+        setNull(obj->_row, key);
     }
     }
 }
 }
 
 
@@ -203,9 +217,7 @@ RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const ob
     auto& objectInfo = obj->_realm->_info[property.objectClassName];
     auto& objectInfo = obj->_realm->_info[property.objectClassName];
     auto& linkOrigin = obj->_info->objectSchema->computed_properties[property.index].link_origin_property_name;
     auto& linkOrigin = obj->_info->objectSchema->computed_properties[property.index].link_origin_property_name;
     auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
     auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
-    auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(),
-                                                                 objectInfo.table(),
-                                                                 linkingProperty->table_column);
+    auto backlinkView = obj->_row.get_backlink_view(objectInfo.table(), linkingProperty->column_key);
     realm::Results results(obj->_realm->_realm, std::move(backlinkView));
     realm::Results results(obj->_realm->_realm, std::move(backlinkView));
     return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
     return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
 }
 }
@@ -278,7 +290,7 @@ id managedGetter(RLMProperty *prop, const char *type) {
         case RLMPropertyTypeData:
         case RLMPropertyTypeData:
             return makeBoxedGetter<realm::BinaryData>(index);
             return makeBoxedGetter<realm::BinaryData>(index);
         case RLMPropertyTypeObject:
         case RLMPropertyTypeObject:
-            return makeBoxedGetter<realm::RowExpr>(index);
+            return makeBoxedGetter<realm::Obj>(index);
         case RLMPropertyTypeAny:
         case RLMPropertyTypeAny:
             @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
             @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
         case RLMPropertyTypeLinkingObjects:
         case RLMPropertyTypeLinkingObjects:
@@ -300,11 +312,11 @@ id makeSetter(__unsafe_unretained RLMProperty *const prop) {
 
 
     return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
     return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
         auto set = [&] {
         auto set = [&] {
-            setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
+            setValue(obj, obj->_info->objectSchema->persisted_properties[index].column_key,
                      static_cast<StorageType>(val));
                      static_cast<StorageType>(val));
         };
         };
         if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
         if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
-                                                             obj->_row.get_index(), *obj->_info)) {
+                                                             obj->_row.get_key(), *obj->_info)) {
             info->willChange(name);
             info->willChange(name);
             set();
             set();
             info->didChange(name);
             info->didChange(name);
@@ -351,7 +363,7 @@ id managedSetter(RLMProperty *prop, const char *type) {
     }
     }
 }
 }
 
 
-// call getter for superclass for property at colIndex
+// call getter for superclass for property at key
 id superGet(RLMObjectBase *obj, NSString *propName) {
 id superGet(RLMObjectBase *obj, NSString *propName) {
     typedef id (*getter_type)(RLMObjectBase *, SEL);
     typedef id (*getter_type)(RLMObjectBase *, SEL);
     RLMProperty *prop = obj->_objectSchema[propName];
     RLMProperty *prop = obj->_objectSchema[propName];
@@ -360,7 +372,7 @@ id superGet(RLMObjectBase *obj, NSString *propName) {
     return superGetter(obj, prop.getterSel);
     return superGetter(obj, prop.getterSel);
 }
 }
 
 
-// call setter for superclass for property at colIndex
+// call setter for superclass for property at key
 void superSet(RLMObjectBase *obj, NSString *propName, id val) {
 void superSet(RLMObjectBase *obj, NSString *propName, id val) {
     typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
     typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
     RLMProperty *prop = obj->_objectSchema[propName];
     RLMProperty *prop = obj->_objectSchema[propName];
@@ -643,11 +655,11 @@ id RLMAccessorContext::box(realm::List&& l) {
 
 
 id RLMAccessorContext::box(realm::Object&& o) {
 id RLMAccessorContext::box(realm::Object&& o) {
     REALM_ASSERT(currentProperty);
     REALM_ASSERT(currentProperty);
-    return RLMCreateObjectAccessor(_info.linkTargetType(currentProperty.index), o.row());
+    return RLMCreateObjectAccessor(_info.linkTargetType(currentProperty.index), o.obj());
 }
 }
 
 
-id RLMAccessorContext::box(realm::RowExpr r) {
-    return RLMCreateObjectAccessor(_info, r);
+id RLMAccessorContext::box(realm::Obj&& r) {
+    return RLMCreateObjectAccessor(_info, std::move(r));
 }
 }
 
 
 id RLMAccessorContext::box(realm::Results&& r) {
 id RLMAccessorContext::box(realm::Results&& r) {
@@ -656,35 +668,38 @@ id RLMAccessorContext::box(realm::Results&& r) {
                                      results:std::move(r)];
                                      results:std::move(r)];
 }
 }
 
 
+using realm::ObjKey;
+using realm::CreatePolicy;
+
 template<>
 template<>
-realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, realm::CreatePolicy, size_t) {
+realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, CreatePolicy, ObjKey) {
     id v = RLMCoerceToNil(value);
     id v = RLMCoerceToNil(value);
     return RLMTimestampForNSDate(v);
     return RLMTimestampForNSDate(v);
 }
 }
 
 
 template<>
 template<>
-bool RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+bool RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return [v boolValue];
     return [v boolValue];
 }
 }
 template<>
 template<>
-double RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+double RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return [v doubleValue];
     return [v doubleValue];
 }
 }
 template<>
 template<>
-float RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+float RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return [v floatValue];
     return [v floatValue];
 }
 }
 template<>
 template<>
-long long RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+long long RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return [v longLongValue];
     return [v longLongValue];
 }
 }
 template<>
 template<>
-realm::BinaryData RLMAccessorContext::unbox(id v, realm::CreatePolicy, size_t) {
+realm::BinaryData RLMAccessorContext::unbox(id v, CreatePolicy, ObjKey) {
     v = RLMCoerceToNil(v);
     v = RLMCoerceToNil(v);
     return RLMBinaryDataForNSData(v);
     return RLMBinaryDataForNSData(v);
 }
 }
 template<>
 template<>
-realm::StringData RLMAccessorContext::unbox(id v, realm::CreatePolicy, size_t) {
+realm::StringData RLMAccessorContext::unbox(id v, CreatePolicy, ObjKey) {
     v = RLMCoerceToNil(v);
     v = RLMCoerceToNil(v);
     return RLMStringDataWithNSString(v);
     return RLMStringDataWithNSString(v);
 }
 }
@@ -696,30 +711,30 @@ static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
 }
 }
 
 
 template<>
 template<>
-realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
     return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
 }
 }
 template<>
 template<>
-realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
     return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
 }
 }
 template<>
 template<>
-realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
     return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
 }
 }
 template<>
 template<>
-realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy, size_t) {
+realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy, ObjKey) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
     return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
 }
 }
 
 
 template<>
 template<>
-realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::CreatePolicy createPolicy, size_t) {
-    bool create = createPolicy != realm::CreatePolicy::Skip;
+realm::Obj RLMAccessorContext::unbox(__unsafe_unretained id const v, CreatePolicy createPolicy, ObjKey) {
+    bool create = createPolicy != CreatePolicy::Skip;
     auto policy = static_cast<RLMUpdatePolicy>(createPolicy);
     auto policy = static_cast<RLMUpdatePolicy>(createPolicy);
     RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
     RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
     if (!link) {
     if (!link) {
         if (!create)
         if (!create)
-            return realm::RowExpr();
+            return realm::Obj();
         return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
         return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
     }
     }
 
 
@@ -740,7 +755,7 @@ realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::
 
 
     if (!link->_realm) {
     if (!link->_realm) {
         if (!create)
         if (!create)
-            return realm::RowExpr();
+            return realm::Obj();
         if (!_promote_existing)
         if (!_promote_existing)
             return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
             return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
         RLMAddObjectToRealm(link, _realm, policy);
         RLMAddObjectToRealm(link, _realm, policy);
@@ -753,10 +768,10 @@ realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, realm::
     return link->_row;
     return link->_row;
 }
 }
 
 
-void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
-    _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
+void RLMAccessorContext::will_change(realm::Obj const& row, realm::Property const& prop) {
+    _observationInfo = RLMGetObservationInfo(nullptr, row.get_key(), _info);
     if (_observationInfo) {
     if (_observationInfo) {
-        _kvoPropertyName = _info.propertyForTableColumn(prop.table_column).name;
+        _kvoPropertyName = _info.propertyForTableColumn(prop.column_key).name;
         _observationInfo->willChange(_kvoPropertyName);
         _observationInfo->willChange(_kvoPropertyName);
     }
     }
 }
 }

+ 81 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMArray.h

@@ -91,6 +91,15 @@ NS_ASSUME_NONNULL_BEGIN
  */
  */
 @property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
 @property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
 
 
+/**
+ Indicates if the array is frozen.
+
+ Frozen arrays are immutable and can be accessed from any thread. Frozen arrays
+ are created by calling `-freeze` on a managed live array. Unmanaged arrays are
+ never frozen.
+ */
+@property (nonatomic, readonly, getter = isFrozen) BOOL frozen;
+
 #pragma mark - Accessing Objects from an Array
 #pragma mark - Accessing Objects from an Array
 
 
 /**
 /**
@@ -290,6 +299,15 @@ NS_ASSUME_NONNULL_BEGIN
  */
  */
 - (RLMResults<RLMObjectType> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties;
 - (RLMResults<RLMObjectType> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties;
 
 
+/**
+ Returns a distinct `RLMResults` from the array.
+
+ @param keyPaths     The key paths to distinct on.
+
+ @return    An `RLMResults` with the distinct values of the keypath(s).
+ */
+- (RLMResults<RLMObjectType> *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths;
+
 /// :nodoc:
 /// :nodoc:
 - (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index;
 - (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index;
 
 
@@ -347,14 +365,54 @@ NS_ASSUME_NONNULL_BEGIN
 
 
  @warning This method cannot be called during a write transaction, or when the
  @warning This method cannot be called during a write transaction, or when the
           containing Realm is read-only.
           containing Realm is read-only.
- @warning This method may only be called on a managed array.
+ @warning This method may only be called on a non-frozen managed array.
 
 
  @param block The block to be called each time the array changes.
  @param block The block to be called each time the array changes.
  @return A token which must be held for as long as you want updates to be delivered.
  @return A token which must be held for as long as you want updates to be delivered.
  */
  */
-- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray<RLMObjectType> *__nullable array,
-                                                         RLMCollectionChange *__nullable changes,
-                                                         NSError *__nullable error))block __attribute__((warn_unused_result));
+- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray<RLMObjectType> *_Nullable array,
+                                                         RLMCollectionChange *_Nullable changes,
+                                                         NSError *_Nullable error))block
+__attribute__((warn_unused_result));
+
+/**
+ Registers a block to be called each time the array changes.
+
+ The block will be asynchronously called with the initial array, and then
+ called again after each write transaction which changes any of the objects in
+ the array, which objects are in the results, or the order of the objects in the
+ array.
+
+ The `changes` parameter will be `nil` the first time the block is called.
+ For each call after that, it will contain information about
+ which rows in the array were added, removed or modified. If a write transaction
+ did not modify any objects in the array, the block is not called at all.
+ See the `RLMCollectionChange` documentation for information on how the changes
+ are reported and an example of updating a `UITableView`.
+
+ If an error occurs the block will be called with `nil` for the results
+ parameter and a non-`nil` error. Currently the only errors that can occur are
+ when opening the Realm on the background worker thread.
+
+ Notifications are delivered on the given queue. If the queue is blocked and
+ notifications can't be delivered instantly, multiple notifications may be
+ coalesced into a single notification.
+
+ You must retain the returned token for as long as you want updates to continue
+ to be sent to the block. To stop receiving updates, call `-invalidate` on the token.
+
+ @warning This method cannot be called when the containing Realm is read-only or frozen.
+ @warning The queue must be a serial queue.
+
+ @param block The block to be called whenever a change occurs.
+ @param queue The serial queue to deliver notifications to.
+ @return A token which must be held for as long as you want updates to be delivered.
+ */
+- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray<RLMObjectType> *_Nullable array,
+                                                         RLMCollectionChange *_Nullable changes,
+                                                         NSError *_Nullable error))block
+                                         queue:(nullable dispatch_queue_t)queue
+__attribute__((warn_unused_result));
 
 
 #pragma mark - Aggregating Property Values
 #pragma mark - Aggregating Property Values
 
 
@@ -414,6 +472,25 @@ NS_ASSUME_NONNULL_BEGIN
  */
  */
 - (nullable NSNumber *)averageOfProperty:(NSString *)property;
 - (nullable NSNumber *)averageOfProperty:(NSString *)property;
 
 
+#pragma mark - Freeze
+
+/**
+ Returns a frozen (immutable) snapshot of this array.
+
+ The frozen copy is an immutable array which contains the same data as this
+ array currently contains, but will not update when writes are made to the
+ containing Realm. Unlike live arrays, frozen arrays can be accessed from any
+ thread.
+
+ @warning This method cannot be called during a write transaction, or when the
+          containing Realm is read-only.
+ @warning This method may only be called on a managed array.
+ @warning Holding onto a frozen array for an extended period while performing
+          write transaction on the Realm may result in the Realm file growing
+          to large sizes. See `RLMRealmConfiguration.maximumNumberOfActiveVersions`
+          for more information.
+ */
+- (instancetype)freeze;
 
 
 #pragma mark - Unavailable Methods
 #pragma mark - Unavailable Methods
 
 

+ 14 - 2
Carthage/Checkouts/realm-cocoa/Realm/RLMArray.mm

@@ -533,18 +533,30 @@ static bool canAggregate(RLMPropertyType type, bool allowDate) {
     @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
     @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
 }
 }
 
 
+- (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
+    @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
+}
+
 // The compiler complains about the method's argument type not matching due to
 // The compiler complains about the method's argument type not matching due to
 // it not having the generic type attached, but it doesn't seem to be possible
 // it not having the generic type attached, but it doesn't seem to be possible
 // to actually include the generic type
 // to actually include the generic type
 // http://www.openradar.me/radar?id=6135653276319744
 // http://www.openradar.me/radar?id=6135653276319744
 #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
 #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
 - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block {
 - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block {
+    return [self addNotificationBlock:block queue:nil];
+}
+- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block
+                                         queue:(nullable dispatch_queue_t)queue {
+    @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
+}
+
+- (instancetype)freeze {
     @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
     @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm");
 }
 }
 
 
 #pragma mark - Thread Confined Protocol Conformance
 #pragma mark - Thread Confined Protocol Conformance
 
 
-- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
+- (realm::ThreadSafeReference)makeThreadSafeReference {
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
 }
 }
 
 
@@ -552,7 +564,7 @@ static bool canAggregate(RLMPropertyType type, bool allowDate) {
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
 }
 }
 
 
-+ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
++ (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
                                      metadata:(id)metadata
                                      metadata:(id)metadata
                                         realm:(RLMRealm *)realm {
                                         realm:(RLMRealm *)realm {
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
     REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");

+ 0 - 1
Carthage/Checkouts/realm-cocoa/Realm/RLMArray_Private.hpp

@@ -22,7 +22,6 @@
 
 
 #import "RLMResults_Private.hpp"
 #import "RLMResults_Private.hpp"
 
 
-#import <realm/link_view_fwd.hpp>
 #import <realm/table_ref.hpp>
 #import <realm/table_ref.hpp>
 
 
 namespace realm {
 namespace realm {

+ 10 - 10
Carthage/Checkouts/realm-cocoa/Realm/RLMClassInfo.hpp

@@ -17,14 +17,17 @@
 ////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////
 
 
 #import <Foundation/Foundation.h>
 #import <Foundation/Foundation.h>
+
+#import <realm/table_ref.hpp>
+
 #import <unordered_map>
 #import <unordered_map>
 #import <vector>
 #import <vector>
 
 
 namespace realm {
 namespace realm {
     class ObjectSchema;
     class ObjectSchema;
     class Schema;
     class Schema;
-    class Table;
     struct Property;
     struct Property;
+    struct ColKey;
 }
 }
 
 
 class RLMObservationInfo;
 class RLMObservationInfo;
@@ -62,11 +65,11 @@ public:
 
 
     // Get the table for this object type. Will return nullptr only if it's a
     // Get the table for this object type. Will return nullptr only if it's a
     // read-only Realm that is missing the table entirely.
     // read-only Realm that is missing the table entirely.
-    realm::Table *_Nullable table() const;
+    realm::TableRef table() const;
 
 
     // Get the RLMProperty for a given table column, or `nil` if it is a column
     // Get the RLMProperty for a given table column, or `nil` if it is a column
     // not used by the current schema
     // not used by the current schema
-    RLMProperty *_Nullable propertyForTableColumn(NSUInteger) const noexcept;
+    RLMProperty *_Nullable propertyForTableColumn(realm::ColKey) const noexcept;
 
 
     // Get the RLMProperty that's used as the primary key, or `nil` if there is
     // Get the RLMProperty that's used as the primary key, or `nil` if there is
     // no primary key for the current schema
     // no primary key for the current schema
@@ -74,8 +77,8 @@ public:
 
 
     // Get the table column for the given property. The property must be a valid
     // Get the table column for the given property. The property must be a valid
     // persisted property.
     // persisted property.
-    NSUInteger tableColumn(NSString *propertyName) const;
-    NSUInteger tableColumn(RLMProperty *property) const;
+    realm::ColKey tableColumn(NSString *propertyName) const;
+    realm::ColKey tableColumn(RLMProperty *property) const;
 
 
     // Get the info for the target of the link at the given property index.
     // Get the info for the target of the link at the given property index.
     RLMClassInfo &linkTargetType(size_t propertyIndex);
     RLMClassInfo &linkTargetType(size_t propertyIndex);
@@ -83,11 +86,8 @@ public:
     // Get the info for the target of the given property
     // Get the info for the target of the given property
     RLMClassInfo &linkTargetType(realm::Property const& property);
     RLMClassInfo &linkTargetType(realm::Property const& property);
 
 
-    void releaseTable() { m_table = nullptr; }
-
-private:
-    mutable realm::Table *_Nullable m_table = nullptr;
-    std::vector<RLMClassInfo *> m_linkTargets;
+    // Get the corresponding ClassInfo for the given Realm
+    RLMClassInfo &freeze(RLMRealm *);
 };
 };
 
 
 // A per-RLMRealm object schema map which stores RLMClassInfo keyed on the name
 // A per-RLMRealm object schema map which stores RLMClassInfo keyed on the name

+ 17 - 18
Carthage/Checkouts/realm-cocoa/Realm/RLMClassInfo.mm

@@ -35,20 +35,20 @@
 using namespace realm;
 using namespace realm;
 
 
 RLMClassInfo::RLMClassInfo(RLMRealm *realm, RLMObjectSchema *rlmObjectSchema,
 RLMClassInfo::RLMClassInfo(RLMRealm *realm, RLMObjectSchema *rlmObjectSchema,
-                             const realm::ObjectSchema *objectSchema)
+                           const realm::ObjectSchema *objectSchema)
 : realm(realm), rlmObjectSchema(rlmObjectSchema), objectSchema(objectSchema) { }
 : realm(realm), rlmObjectSchema(rlmObjectSchema), objectSchema(objectSchema) { }
 
 
-realm::Table *RLMClassInfo::table() const {
-    if (!m_table) {
-        m_table = ObjectStore::table_for_object_type(realm.group, objectSchema->name).get();
+realm::TableRef RLMClassInfo::table() const {
+    if (auto key = objectSchema->table_key) {
+        return realm.group.get_table(objectSchema->table_key);
     }
     }
-    return m_table;
+    return nullptr;
 }
 }
 
 
-RLMProperty *RLMClassInfo::propertyForTableColumn(NSUInteger col) const noexcept {
+RLMProperty *RLMClassInfo::propertyForTableColumn(ColKey col) const noexcept {
     auto const& props = objectSchema->persisted_properties;
     auto const& props = objectSchema->persisted_properties;
     for (size_t i = 0; i < props.size(); ++i) {
     for (size_t i = 0; i < props.size(); ++i) {
-        if (props[i].table_column == col) {
+        if (props[i].column_key == col) {
             return rlmObjectSchema.properties[i];
             return rlmObjectSchema.properties[i];
         }
         }
     }
     }
@@ -59,23 +59,16 @@ RLMProperty *RLMClassInfo::propertyForPrimaryKey() const noexcept {
     return rlmObjectSchema.primaryKeyProperty;
     return rlmObjectSchema.primaryKeyProperty;
 }
 }
 
 
-NSUInteger RLMClassInfo::tableColumn(NSString *propertyName) const {
+realm::ColKey RLMClassInfo::tableColumn(NSString *propertyName) const {
     return tableColumn(RLMValidatedProperty(rlmObjectSchema, propertyName));
     return tableColumn(RLMValidatedProperty(rlmObjectSchema, propertyName));
 }
 }
 
 
-NSUInteger RLMClassInfo::tableColumn(RLMProperty *property) const {
-    return objectSchema->persisted_properties[property.index].table_column;
+realm::ColKey RLMClassInfo::tableColumn(RLMProperty *property) const {
+    return objectSchema->persisted_properties[property.index].column_key;
 }
 }
 
 
 RLMClassInfo &RLMClassInfo::linkTargetType(size_t propertyIndex) {
 RLMClassInfo &RLMClassInfo::linkTargetType(size_t propertyIndex) {
-    if (propertyIndex < m_linkTargets.size() && m_linkTargets[propertyIndex]) {
-        return *m_linkTargets[propertyIndex];
-    }
-    if (m_linkTargets.size() <= propertyIndex) {
-        m_linkTargets.resize(propertyIndex + 1);
-    }
-    m_linkTargets[propertyIndex] = &realm->_info[rlmObjectSchema.properties[propertyIndex].objectClassName];
-    return *m_linkTargets[propertyIndex];
+    return realm->_info[rlmObjectSchema.properties[propertyIndex].objectClassName];
 }
 }
 
 
 RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) {
 RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) {
@@ -83,6 +76,12 @@ RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) {
     return linkTargetType(&property - &objectSchema->persisted_properties[0]);
     return linkTargetType(&property - &objectSchema->persisted_properties[0]);
 }
 }
 
 
+RLMClassInfo &RLMClassInfo::freeze(__unsafe_unretained RLMRealm *const frozenRealm) {
+    REALM_ASSERT(frozenRealm.frozen);
+    // FIXME
+    return frozenRealm->_info[rlmObjectSchema.className];
+}
+
 RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); }
 RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); }
 RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); }
 RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); }
 RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); }
 RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); }

+ 27 - 0
Carthage/Checkouts/realm-cocoa/Realm/RLMCollection.h

@@ -309,6 +309,33 @@ typedef RLM_CLOSED_ENUM(int32_t, RLMPropertyType);
  */
  */
 - (nullable NSNumber *)averageOfProperty:(NSString *)property;
 - (nullable NSNumber *)averageOfProperty:(NSString *)property;
 
 
+#pragma mark - Freeze
+
+/**
+ Indicates if the collection is frozen.
+
+ Frozen collections are immutable and can be accessed from any thread. The
+ objects read from a frozen collection will also be frozen.
+ */
+@property (nonatomic, readonly, getter=isFrozen) BOOL frozen;
+
+/**
+ Returns a frozen (immutable) snapshot of this collection.
+
+ The frozen copy is an immutable collection which contains the same data as
+ this collection currently contains, but will not update when writes are made
+ to the containing Realm. Unlike live collections, frozen collections can be
+ accessed from any thread.
+
+ @warning This method cannot be called during a write transaction, or when the containing Realm is read-only.
+ @warning Holding onto a frozen collection for an extended period while
+          performing write transaction on the Realm may result in the Realm
+          file growing to large sizes. See
+          `RLMRealmConfiguration.maximumNumberOfActiveVersions`
+          for more information.
+ */
+- (instancetype)freeze;
+
 @end
 @end
 
 
 /**
 /**

+ 90 - 49
Carthage/Checkouts/realm-cocoa/Realm/RLMCollection.mm

@@ -203,9 +203,9 @@ NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key, RLMClas
             RLMAccessorContext context(info);
             RLMAccessorContext context(info);
             for (size_t i = 0; i < count; ++i) {
             for (size_t i = 0; i < count; ++i) {
                 RLMListBase *list = [[cls alloc] init];
                 RLMListBase *list = [[cls alloc] init];
-                list._rlmArray = [[RLMManagedArray alloc] initWithList:realm::List(info.realm->_realm, *info.table(),
-                                                                                   info.tableColumn(prop),
-                                                                                   collection.get(i).get_index())
+                list._rlmArray = [[RLMManagedArray alloc] initWithList:realm::List(info.realm->_realm,
+                                                                                   collection.get(i),
+                                                                                   info.tableColumn(prop))
                                                             parentInfo:&info
                                                             parentInfo:&info
                                                               property:prop];
                                                               property:prop];
                 [array addObject:list];
                 [array addObject:list];
@@ -294,33 +294,6 @@ std::vector<std::pair<std::string, bool>> RLMSortDescriptorsToKeypathArray(NSArr
     return keypaths;
     return keypaths;
 }
 }
 
 
-@implementation RLMCancellationToken {
-    realm::NotificationToken _token;
-    __unsafe_unretained RLMRealm *_realm;
-}
-- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm {
-    self = [super init];
-    if (self) {
-        _token = std::move(token);
-        _realm = realm;
-    }
-    return self;
-}
-
-- (RLMRealm *)realm {
-    return _realm;
-}
-
-- (void)suppressNextNotification {
-    _token.suppress_next();
-}
-
-- (void)invalidate {
-    _token = {};
-}
-
-@end
-
 @implementation RLMCollectionChange {
 @implementation RLMCollectionChange {
     realm::CollectionChangeSet _indices;
     realm::CollectionChangeSet _indices;
 }
 }
@@ -369,23 +342,26 @@ static NSArray *toIndexPathArray(realm::IndexSet const& set, NSUInteger section)
 
 
 - (NSArray<NSIndexPath *> *)insertionsInSection:(NSUInteger)section {
 - (NSArray<NSIndexPath *> *)insertionsInSection:(NSUInteger)section {
     return toIndexPathArray(_indices.insertions, section);
     return toIndexPathArray(_indices.insertions, section);
-
 }
 }
 
 
 - (NSArray<NSIndexPath *> *)modificationsInSection:(NSUInteger)section {
 - (NSArray<NSIndexPath *> *)modificationsInSection:(NSUInteger)section {
     return toIndexPathArray(_indices.modifications, section);
     return toIndexPathArray(_indices.modifications, section);
+}
 
 
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<RLMCollectionChange: %p> insertions: %@, deletions: %@, modifications: %@",
+            (__bridge void *)self, self.insertions, self.deletions, self.modifications];
 }
 }
+
 @end
 @end
 
 
-template<typename Collection>
-RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
-                                              Collection& collection,
-                                              void (^block)(id, RLMCollectionChange *, NSError *),
-                                              bool suppressInitialChange) {
-    auto skip = suppressInitialChange ? std::make_shared<bool>(true) : nullptr;
-    auto cb = [=, &collection](realm::CollectionChangeSet const& changes,
-                               std::exception_ptr err) {
+namespace {
+struct CollectionCallbackWrapper {
+    void (^block)(id, RLMCollectionChange *, NSError *);
+    id collection;
+    bool ignoreChangesInInitialNotification;
+
+    void operator()(realm::CollectionChangeSet const& changes, std::exception_ptr err) {
         if (err) {
         if (err) {
             try {
             try {
                 rethrow_exception(err);
                 rethrow_exception(err);
@@ -398,22 +374,87 @@ RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
             }
             }
         }
         }
 
 
-        if (skip && *skip) {
-            *skip = false;
-            block(objcCollection, nil, nil);
+        if (ignoreChangesInInitialNotification) {
+            ignoreChangesInInitialNotification = false;
+            block(collection, nil, nil);
         }
         }
         else if (changes.empty()) {
         else if (changes.empty()) {
-            block(objcCollection, nil, nil);
+            block(collection, nil, nil);
         }
         }
         else {
         else {
-            block(objcCollection, [[RLMCollectionChange alloc] initWithChanges:changes], nil);
+            block(collection, [[RLMCollectionChange alloc] initWithChanges:changes], nil);
         }
         }
-    };
+    }
+};
+} // anonymous namespace
+
+@interface RLMCancellationToken : RLMNotificationToken
+@end
+
+@implementation RLMCancellationToken {
+@public
+    __unsafe_unretained RLMRealm *_realm;
+    realm::NotificationToken _token;
+    std::mutex _mutex;
+}
+
+- (RLMRealm *)realm {
+    std::lock_guard<std::mutex> lock(_mutex);
+    return _realm;
+}
+
+- (void)suppressNextNotification {
+    std::lock_guard<std::mutex> lock(_mutex);
+    if (_realm) {
+        _token.suppress_next();
+    }
+}
+
+- (void)invalidate {
+    std::lock_guard<std::mutex> lock(_mutex);
+    _token = {};
+    _realm = nil;
+}
 
 
-    return [[RLMCancellationToken alloc] initWithToken:collection.add_notification_callback(cb)
-                                                 realm:(RLMRealm *)[objcCollection realm]];
+template<typename RLMCollection>
+RLMNotificationToken *RLMAddNotificationBlock(RLMCollection *collection,
+                                              void (^block)(id, RLMCollectionChange *, NSError *),
+                                              dispatch_queue_t queue) {
+    RLMRealm *realm = collection.realm;
+    if (!realm) {
+        @throw RLMException(@"Linking objects notifications are only supported on managed objects.");
+    }
+    bool skipFirst = std::is_same_v<RLMCollection, RLMResults>;
+    auto token = [[RLMCancellationToken alloc] init];
+
+    if (!queue) {
+        [realm verifyNotificationsAreSupported:true];
+        token->_realm = realm;
+        token->_token = RLMGetBackingCollection(collection).add_notification_callback(CollectionCallbackWrapper{block, collection, skipFirst});
+        return token;
+    }
+
+    RLMThreadSafeReference *tsr = [RLMThreadSafeReference referenceWithThreadConfined:collection];
+    token->_realm = realm;
+    RLMRealmConfiguration *config = realm.configuration;
+    dispatch_async(queue, ^{
+        std::lock_guard<std::mutex> lock(token->_mutex);
+        if (!token->_realm) {
+            return;
+        }
+        NSError *error;
+        RLMRealm *realm = token->_realm = [RLMRealm realmWithConfiguration:config queue:queue error:&error];
+        if (!realm) {
+            block(nil, nil, error);
+            return;
+        }
+        RLMCollection *collection = [realm resolveThreadSafeReference:tsr];
+        token->_token = RLMGetBackingCollection(collection).add_notification_callback(CollectionCallbackWrapper{block, collection, skipFirst});
+    });
+    return token;
 }
 }
+@end
 
 
 // Explicitly instantiate the templated function for the two types we'll use it on
 // Explicitly instantiate the templated function for the two types we'll use it on
-template RLMNotificationToken *RLMAddNotificationBlock<realm::List>(id, realm::List&, void (^)(id, RLMCollectionChange *, NSError *), bool);
-template RLMNotificationToken *RLMAddNotificationBlock<realm::Results>(id, realm::Results&, void (^)(id, RLMCollectionChange *, NSError *), bool);
+template RLMNotificationToken *RLMAddNotificationBlock<>(RLMManagedArray *, void (^)(id, RLMCollectionChange *, NSError *), dispatch_queue_t);
+template RLMNotificationToken *RLMAddNotificationBlock<>(RLMResults *, void (^)(id, RLMCollectionChange *, NSError *), dispatch_queue_t);

+ 7 - 9
Carthage/Checkouts/realm-cocoa/Realm/RLMCollection_Private.hpp

@@ -28,7 +28,7 @@ namespace realm {
     struct NotificationToken;
     struct NotificationToken;
 }
 }
 class RLMClassInfo;
 class RLMClassInfo;
-@class RLMFastEnumerator;
+@class RLMFastEnumerator, RLMManagedArray;
 
 
 @protocol RLMFastEnumerable
 @protocol RLMFastEnumerable
 @property (nonatomic, readonly) RLMRealm *realm;
 @property (nonatomic, readonly) RLMRealm *realm;
@@ -64,19 +64,17 @@ NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id<RL
 - (RLMRealm *)realm;
 - (RLMRealm *)realm;
 @end
 @end
 
 
-@interface RLMCancellationToken : RLMNotificationToken
-- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm;
-@end
-
 @interface RLMCollectionChange ()
 @interface RLMCollectionChange ()
 - (instancetype)initWithChanges:(realm::CollectionChangeSet)indices;
 - (instancetype)initWithChanges:(realm::CollectionChangeSet)indices;
 @end
 @end
 
 
-template<typename Collection>
-RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
-                                              Collection& collection,
+realm::List& RLMGetBackingCollection(RLMManagedArray *);
+realm::Results& RLMGetBackingCollection(RLMResults *);
+
+template<typename RLMCollection>
+RLMNotificationToken *RLMAddNotificationBlock(RLMCollection *collection,
                                               void (^block)(id, RLMCollectionChange *, NSError *),
                                               void (^block)(id, RLMCollectionChange *, NSError *),
-                                              bool suppressInitialChange=false);
+                                              dispatch_queue_t queue);
 
 
 template<typename Collection>
 template<typename Collection>
 NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key, RLMClassInfo& info);
 NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key, RLMClassInfo& info);

+ 2 - 9
Carthage/Checkouts/realm-cocoa/Realm/RLMConstants.h

@@ -152,15 +152,8 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) {
 
 
     /** Denotes an error that occurs if there is a schema version mismatch, so that a migration is required. */
     /** Denotes an error that occurs if there is a schema version mismatch, so that a migration is required. */
     RLMErrorSchemaMismatch = 10,
     RLMErrorSchemaMismatch = 10,
-
-    /** Denotes an error that occurs when attempting to open an incompatible synchronized Realm file.
-
-     This error occurs when the Realm file was created with an older version of Realm and an automatic migration
-     to the current version is not possible. When such an error occurs, the original file is moved to a backup
-     location, and future attempts to open the synchronized Realm will result in a new file being created.
-     If you wish to migrate any data from the backup Realm, you can open it using the provided Realm configuration.
-     */
-    RLMErrorIncompatibleSyncedFile = 11,
+    // Error code 11 is obsolete
+    // RLMErrorIncompatibleSyncedFile = 11,
     /**
     /**
      Denotates an error where an operation was requested which cannot be performed on an open file.
      Denotates an error where an operation was requested which cannot be performed on an open file.
      */
      */

+ 4 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMListBase.h

@@ -18,7 +18,7 @@
 
 
 #import <Foundation/Foundation.h>
 #import <Foundation/Foundation.h>
 
 
-@class RLMArray, RLMObjectBase, RLMResults, RLMProperty;
+@class RLMArray, RLMObjectBase, RLMResults, RLMProperty, RLMLinkingObjects;
 
 
 NS_ASSUME_NONNULL_BEGIN
 NS_ASSUME_NONNULL_BEGIN
 
 
@@ -33,10 +33,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 
 @interface RLMLinkingObjectsHandle : NSObject
 @interface RLMLinkingObjectsHandle : NSObject
 - (instancetype)initWithObject:(RLMObjectBase *)object property:(RLMProperty *)property;
 - (instancetype)initWithObject:(RLMObjectBase *)object property:(RLMProperty *)property;
+- (instancetype)initWithLinkingObjects:(RLMResults *)linkingObjects;
+- (instancetype)freeze;
 
 
-@property (nonatomic, readonly) RLMResults *results;
-@property (nonatomic, readonly) RLMObjectBase *parent;
-@property (nonatomic, readonly) RLMProperty *property;
+@property (nonatomic, readonly) RLMLinkingObjects *results;
 @end
 @end
 
 
 NS_ASSUME_NONNULL_END
 NS_ASSUME_NONNULL_END

+ 27 - 15
Carthage/Checkouts/realm-cocoa/Realm/RLMListBase.mm

@@ -85,7 +85,8 @@
 @end
 @end
 
 
 @implementation RLMLinkingObjectsHandle {
 @implementation RLMLinkingObjectsHandle {
-    realm::Row _row;
+    realm::TableKey _tableKey;
+    realm::ObjKey _objKey;
     RLMClassInfo *_info;
     RLMClassInfo *_info;
     RLMRealm *_realm;
     RLMRealm *_realm;
     RLMProperty *_property;
     RLMProperty *_property;
@@ -97,8 +98,9 @@
     if (!(self = [super init])) {
     if (!(self = [super init])) {
         return nil;
         return nil;
     }
     }
-
-    _row = object->_row;
+    auto& obj = object->_row;
+    _tableKey = obj.get_table()->get_key();
+    _objKey = obj.get_key();
     _info = object->_info;
     _info = object->_info;
     _realm = object->_realm;
     _realm = object->_realm;
     _property = prop;
     _property = prop;
@@ -106,30 +108,40 @@
     return self;
     return self;
 }
 }
 
 
+- (instancetype)initWithLinkingObjects:(RLMResults *)linkingObjects {
+    if (!(self = [super init])) {
+        return nil;
+    }
+    _realm = linkingObjects.realm;
+    _results = linkingObjects;
+
+    return self;
+}
+
+- (instancetype)freeze {
+    RLMLinkingObjectsHandle *frozen = [[self.class alloc] init];
+    frozen->_results = [self.results freeze];
+    return frozen;
+}
+
 - (RLMResults *)results {
 - (RLMResults *)results {
     if (_results) {
     if (_results) {
         return _results;
         return _results;
     }
     }
-    if (!_row.is_attached()) {
+    [_realm verifyThread];
+
+    auto table = _realm.group.get_table(_tableKey);
+    if (!table->is_valid(_objKey)) {
         @throw RLMException(@"Object has been deleted or invalidated.");
         @throw RLMException(@"Object has been deleted or invalidated.");
     }
     }
-    [_realm verifyThread];
 
 
+    auto obj = _realm.group.get_table(_tableKey)->get_object(_objKey);
     auto& objectInfo = _realm->_info[_property.objectClassName];
     auto& objectInfo = _realm->_info[_property.objectClassName];
     auto& linkOrigin = _info->objectSchema->computed_properties[_property.index].link_origin_property_name;
     auto& linkOrigin = _info->objectSchema->computed_properties[_property.index].link_origin_property_name;
     auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
     auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
-    auto backlinkView = _row.get_table()->get_backlink_view(_row.get_index(),
-                                                            objectInfo.table(),
-                                                            linkingProperty->table_column);
-    realm::Results results(_realm->_realm, std::move(backlinkView));
+    realm::Results results(_realm->_realm, obj.get_backlink_view(objectInfo.table(), linkingProperty->column_key));
     _results = [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
     _results = [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
     _realm = nil;
     _realm = nil;
     return _results;
     return _results;
 }
 }
-
-- (RLMObjectBase *)parent {
-    RLMObjectBase *obj = RLMCreateManagedAccessor(_info->rlmObjectSchema.accessorClass, _info);
-    obj->_row = _row;
-    return obj;
-}
 @end
 @end

+ 46 - 20
Carthage/Checkouts/realm-cocoa/Realm/RLMManagedArray.mm

@@ -85,8 +85,7 @@
                            property:(__unsafe_unretained RLMProperty *const)property {
                            property:(__unsafe_unretained RLMProperty *const)property {
     __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
     __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
     auto col = parentObject->_info->tableColumn(property);
     auto col = parentObject->_info->tableColumn(property);
-    auto& row = parentObject->_row;
-    return [self initWithList:realm::List(realm->_realm, *row.get_table(), col, row.get_index())
+    return [self initWithList:realm::List(realm->_realm, parentObject->_row, col)
                    parentInfo:parentObject->_info
                    parentInfo:parentObject->_info
                      property:property];
                      property:property];
 }
 }
@@ -107,7 +106,7 @@ void RLMEnsureArrayObservationInfo(std::unique_ptr<RLMObservationInfo>& info,
     if (!info && array.class == [RLMManagedArray class]) {
     if (!info && array.class == [RLMManagedArray class]) {
         auto lv = static_cast<RLMManagedArray *>(array);
         auto lv = static_cast<RLMManagedArray *>(array);
         info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
         info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
-                                                    lv->_backingList.get_origin_row_index(),
+                                                    lv->_backingList.get_parent_object_key(),
                                                     observed);
                                                     observed);
     }
     }
 }
 }
@@ -179,7 +178,7 @@ static void changeArray(__unsafe_unretained RLMManagedArray *const ar,
                         NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) {
                         NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) {
     translateErrors([&] { ar->_backingList.verify_in_transaction(); });
     translateErrors([&] { ar->_backingList.verify_in_transaction(); });
     RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(),
     RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(),
-                                                     ar->_backingList.get_origin_row_index(),
+                                                     ar->_backingList.get_parent_object_key(),
                                                      *ar->_ownerInfo);
                                                      *ar->_ownerInfo);
     if (info) {
     if (info) {
         NSIndexSet *indexes = is();
         NSIndexSet *indexes = is();
@@ -401,35 +400,35 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
     }
     }
 }
 }
 
 
-- (size_t)columnForProperty:(NSString *)propertyName {
+- (realm::ColKey)columnForProperty:(NSString *)propertyName {
     if (_backingList.get_type() == realm::PropertyType::Object) {
     if (_backingList.get_type() == realm::PropertyType::Object) {
         return _objectInfo->tableColumn(propertyName);
         return _objectInfo->tableColumn(propertyName);
     }
     }
     if (![propertyName isEqualToString:@"self"]) {
     if (![propertyName isEqualToString:@"self"]) {
         @throw RLMException(@"Arrays of '%@' can only be aggregated on \"self\"", RLMTypeToString(_type));
         @throw RLMException(@"Arrays of '%@' can only be aggregated on \"self\"", RLMTypeToString(_type));
     }
     }
-    return 0;
+    return {};
 }
 }
 
 
 - (id)minOfProperty:(NSString *)property {
 - (id)minOfProperty:(NSString *)property {
-    size_t column = [self columnForProperty:property];
+    auto column = [self columnForProperty:property];
     auto value = translateErrors(self, [&] { return _backingList.min(column); }, @"minOfProperty");
     auto value = translateErrors(self, [&] { return _backingList.min(column); }, @"minOfProperty");
     return value ? RLMMixedToObjc(*value) : nil;
     return value ? RLMMixedToObjc(*value) : nil;
 }
 }
 
 
 - (id)maxOfProperty:(NSString *)property {
 - (id)maxOfProperty:(NSString *)property {
-    size_t column = [self columnForProperty:property];
+    auto column = [self columnForProperty:property];
     auto value = translateErrors(self, [&] { return _backingList.max(column); }, @"maxOfProperty");
     auto value = translateErrors(self, [&] { return _backingList.max(column); }, @"maxOfProperty");
     return value ? RLMMixedToObjc(*value) : nil;
     return value ? RLMMixedToObjc(*value) : nil;
 }
 }
 
 
 - (id)sumOfProperty:(NSString *)property {
 - (id)sumOfProperty:(NSString *)property {
-    size_t column = [self columnForProperty:property];
+    auto column = [self columnForProperty:property];
     return RLMMixedToObjc(translateErrors(self, [&] { return _backingList.sum(column); }, @"sumOfProperty"));
     return RLMMixedToObjc(translateErrors(self, [&] { return _backingList.sum(column); }, @"sumOfProperty"));
 }
 }
 
 
 - (id)averageOfProperty:(NSString *)property {
 - (id)averageOfProperty:(NSString *)property {
-    size_t column = [self columnForProperty:property];
+    auto column = [self columnForProperty:property];
     auto value = translateErrors(self, [&] { return _backingList.average(column); }, @"averageOfProperty");
     auto value = translateErrors(self, [&] { return _backingList.average(column); }, @"averageOfProperty");
     return value ? @(*value) : nil;
     return value ? @(*value) : nil;
 }
 }
@@ -451,6 +450,13 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
     });
     });
 }
 }
 
 
+- (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
+    return translateErrors([&] {
+        auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingList.as_results()];
+        return [results distinctResultsUsingKeyPaths:keyPaths];
+    });
+}
+
 - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
 - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
     if (_type != RLMPropertyTypeObject) {
     if (_type != RLMPropertyTypeObject) {
         @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
         @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
@@ -497,6 +503,24 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
     });
     });
 }
 }
 
 
+- (BOOL)isFrozen {
+    return _realm.isFrozen;
+}
+
+- (instancetype)freeze {
+    if (self.frozen) {
+        return self;
+    }
+
+    RLMRealm *frozenRealm = [_realm freeze];
+    auto& parentInfo = _ownerInfo->freeze(frozenRealm);
+    return translateRLMResultsErrors([&] {
+        return [[self.class alloc] initWithList:_backingList.freeze(frozenRealm->_realm)
+                                     parentInfo:&parentInfo
+                                       property:parentInfo.rlmObjectSchema[_key]];
+    });
+}
+
 // The compiler complains about the method's argument type not matching due to
 // The compiler complains about the method's argument type not matching due to
 // it not having the generic type attached, but it doesn't seem to be possible
 // it not having the generic type attached, but it doesn't seem to be possible
 // to actually include the generic type
 // to actually include the generic type
@@ -504,16 +528,21 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
 #pragma clang diagnostic push
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
 #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
 - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block {
 - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block {
-    [_realm verifyNotificationsAreSupported:true];
-    return RLMAddNotificationBlock(self, _backingList, block);
+    return RLMAddNotificationBlock(self, block, nil);
+}
+- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block queue:(dispatch_queue_t)queue {
+    return RLMAddNotificationBlock(self, block, queue);
 }
 }
 #pragma clang diagnostic pop
 #pragma clang diagnostic pop
 
 
+realm::List& RLMGetBackingCollection(RLMManagedArray *self) {
+    return self->_backingList;
+}
+
 #pragma mark - Thread Confined Protocol Conformance
 #pragma mark - Thread Confined Protocol Conformance
 
 
-- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
-    auto list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList);
-    return std::make_unique<realm::ThreadSafeReference<realm::List>>(std::move(list_reference));
+- (realm::ThreadSafeReference)makeThreadSafeReference {
+    return _backingList;
 }
 }
 
 
 - (RLMManagedArrayHandoverMetadata *)objectiveCMetadata {
 - (RLMManagedArrayHandoverMetadata *)objectiveCMetadata {
@@ -523,13 +552,10 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
     return metadata;
     return metadata;
 }
 }
 
 
-+ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
++ (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
                                      metadata:(RLMManagedArrayHandoverMetadata *)metadata
                                      metadata:(RLMManagedArrayHandoverMetadata *)metadata
                                         realm:(RLMRealm *)realm {
                                         realm:(RLMRealm *)realm {
-    REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<realm::List> *>(reference.get()));
-    auto list_reference = static_cast<realm::ThreadSafeReference<realm::List> *>(reference.get());
-
-    auto list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference));
+    auto list = reference.resolve<realm::List>(realm->_realm);
     if (!list.is_valid()) {
     if (!list.is_valid()) {
         return nil;
         return nil;
     }
     }

+ 20 - 42
Carthage/Checkouts/realm-cocoa/Realm/RLMMigration.mm

@@ -56,7 +56,6 @@ using namespace realm;
 
 
 @implementation RLMMigration {
 @implementation RLMMigration {
     realm::Schema *_schema;
     realm::Schema *_schema;
-    std::unordered_map<NSString *, realm::IndexSet> _deletedObjectIndices;
 }
 }
 
 
 - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
 - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
@@ -87,26 +86,29 @@ using namespace realm;
     // objects. It's unclear how this could be useful, but changing it would
     // objects. It's unclear how this could be useful, but changing it would
     // also be a pointless breaking change and it's unlikely to be hurting anyone.
     // also be a pointless breaking change and it's unlikely to be hurting anyone.
     if (objects && !oldObjects) {
     if (objects && !oldObjects) {
-        for (auto i = objects.count; i > 0; --i) {
+        for (RLMObject *object in objects) {
             @autoreleasepool {
             @autoreleasepool {
-                block(nil, objects[i - 1]);
+                block(nil, object);
             }
             }
         }
         }
         return;
         return;
     }
     }
 
 
-    auto count = oldObjects.count;
-    if (count == 0) {
+    if (oldObjects.count == 0 || objects.count == 0) {
         return;
         return;
     }
     }
-    auto deletedObjects = _deletedObjectIndices.find(className);
-    for (auto i = count; i > 0; --i) {
-        auto index = i - 1;
-        if (deletedObjects != _deletedObjectIndices.end() && deletedObjects->second.contains(index)) {
-            continue;
-        }
+
+    auto& info = _realm->_info[className];
+    for (RLMObject *oldObject in oldObjects) {
         @autoreleasepool {
         @autoreleasepool {
-            block(oldObjects[index], objects[index]);
+            Obj newObj;
+            try {
+                newObj = info.table()->get_object(oldObject->_row.get_key());
+            }
+            catch (InvalidKey const&) {
+                continue;
+            }
+            block(oldObject, (id)RLMCreateObjectAccessor(info, std::move(newObj)));
         }
         }
     }
     }
 }
 }
@@ -124,8 +126,6 @@ using namespace realm;
 
 
         block(self, _oldRealm->_realm->schema_version());
         block(self, _oldRealm->_realm->schema_version());
 
 
-        [self deleteObjectsMarkedForDeletion];
-
         _oldRealm = nil;
         _oldRealm = nil;
         _realm = nil;
         _realm = nil;
     }
     }
@@ -140,31 +140,7 @@ using namespace realm;
 }
 }
 
 
 - (void)deleteObject:(RLMObject *)object {
 - (void)deleteObject:(RLMObject *)object {
-    _deletedObjectIndices[object.objectSchema.className].add(object->_row.get_index());
-}
-
-- (void)deleteObjectsMarkedForDeletion {
-    for (auto& objectType : _deletedObjectIndices) {
-        TableRef table = ObjectStore::table_for_object_type(_realm.group, objectType.first.UTF8String);
-        if (!table) {
-            continue;
-        }
-
-        auto& indices = objectType.second;
-        // Just clear the table if we're removing all of the rows
-        if (table->size() == indices.count()) {
-            table->clear();
-        }
-        // Otherwise delete in reverse order to avoid invaliding any of the
-        // not-yet-deleted indices
-        else {
-            for (auto it = std::make_reverse_iterator(indices.end()), end = std::make_reverse_iterator(indices.begin()); it != end; ++it) {
-                for (size_t i = it->second; i > it->first; --i) {
-                    table->move_last_over(i - 1);
-                }
-            }
-        }
-    }
+    [_realm deleteObject:object];
 }
 }
 
 
 - (BOOL)deleteDataForClassName:(NSString *)name {
 - (BOOL)deleteDataForClassName:(NSString *)name {
@@ -176,9 +152,11 @@ using namespace realm;
     if (!table) {
     if (!table) {
         return false;
         return false;
     }
     }
-    _deletedObjectIndices[name].set(table->size());
-    if (![_realm.schema schemaForClassName:name]) {
-        realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String);
+    if ([_realm.schema schemaForClassName:name]) {
+        table->clear();
+    }
+    else {
+        _realm.group.remove_table(table->get_key());
     }
     }
 
 
     return true;
     return true;

+ 65 - 3
Carthage/Checkouts/realm-cocoa/Realm/RLMObject.h

@@ -359,6 +359,13 @@ NS_ASSUME_NONNULL_BEGIN
  */
  */
 @property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
 @property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
 
 
+/**
+ Indicates if this object is frozen.
+
+ @see `-[RLMObject freeze]`
+ */
+@property (nonatomic, readonly, getter = isFrozen) BOOL frozen;
+
 
 
 #pragma mark - Customizing your Objects
 #pragma mark - Customizing your Objects
 
 
@@ -548,7 +555,7 @@ typedef void (^RLMObjectChangeBlock)(BOOL deleted,
  deletes the object or modifies any of the managed properties of the object,
  deletes the object or modifies any of the managed properties of the object,
  including self-assignments that set a property to its existing value.
  including self-assignments that set a property to its existing value.
 
 
- For write transactions performed on different threads or in differen
+ For write transactions performed on different threads or in different
  processes, the block will be called when the managing Realm is
  processes, the block will be called when the managing Realm is
  (auto)refreshed to a version including the changes, while for local write
  (auto)refreshed to a version including the changes, while for local write
  transactions it will be called at some point in the future after the write
  transactions it will be called at some point in the future after the write
@@ -578,14 +585,54 @@ typedef void (^RLMObjectChangeBlock)(BOOL deleted,
  */
  */
 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block;
 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block;
 
 
+/**
+ Registers a block to be called each time the object changes.
+
+ The block will be asynchronously called after each write transaction which
+ deletes the object or modifies any of the managed properties of the object,
+ including self-assignments that set a property to its existing value.
+
+ For write transactions performed on different threads or in different
+ processes, the block will be called when the managing Realm is
+ (auto)refreshed to a version including the changes, while for local write
+ transactions it will be called at some point in the future after the write
+ transaction is committed.
+
+ Notifications are delivered on the given queue. If the queue is blocked and
+ notifications can't be delivered instantly, multiple notifications may be
+ coalesced into a single notification.
+
+ Unlike with `RLMArray` and `RLMResults`, there is no "initial" callback made
+ after you add a new notification block.
+
+ Only objects which are managed by a Realm can be observed in this way. You
+ must retain the returned token for as long as you want updates to be sent to
+ the block. To stop receiving updates, call `-invalidate` on the token.
+
+ It is safe to capture a strong reference to the observed object within the
+ callback block. There is no retain cycle due to that the callback is retained
+ by the returned token and not by the object itself.
+
+ @warning This method cannot be called during a write transaction, when the
+          containing Realm is read-only, or on an unmanaged object.
+ @warning The queue must be a serial queue.
+
+ @param block The block to be called whenever a change occurs.
+ @param queue The serial queue to deliver notifications to.
+ @return A token which must be held for as long as you want updates to be delivered.
+ */
+- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block queue:(dispatch_queue_t)queue;
+
 #pragma mark - Other Instance Methods
 #pragma mark - Other Instance Methods
 
 
 /**
 /**
  Returns YES if another Realm object instance points to the same object as the receiver in the Realm managing
  Returns YES if another Realm object instance points to the same object as the receiver in the Realm managing
  the receiver.
  the receiver.
 
 
- For object types with a primary, key, `isEqual:` is overridden to use the same logic as this
- method (along with a corresponding implementation for `hash`).
+ For frozen objects and object types with a primary key, `isEqual:` is
+ overridden to use the same logic as this method (along with a corresponding
+ implementation for `hash`). Non-frozen objects without primary keys use
+ pointer identity for `isEqual:` and `hash`.
 
 
  @param object  The object to compare the receiver to.
  @param object  The object to compare the receiver to.
 
 
@@ -593,6 +640,21 @@ typedef void (^RLMObjectChangeBlock)(BOOL deleted,
  */
  */
 - (BOOL)isEqualToObject:(RLMObject *)object;
 - (BOOL)isEqualToObject:(RLMObject *)object;
 
 
+/**
+ Returns a frozen (immutable) snapshot of this object.
+
+ The frozen copy is an immutable object which contains the same data as this
+ object currently contains, but will not update when writes are made to the
+ containing Realm. Unlike live objects, frozen objects can be accessed from any
+ thread.
+
+ - warning: Holding onto a frozen object for an extended period while performing write
+ transaction on the Realm may result in the Realm file growing to large sizes. See
+ `Realm.Configuration.maximumNumberOfActiveVersions` for more information.
+ - warning: This method can only be called on a managed object.
+ */
+- (instancetype)freeze NS_RETURNS_RETAINED;
+
 #pragma mark - Dynamic Accessors
 #pragma mark - Dynamic Accessors
 
 
 /// :nodoc:
 /// :nodoc:

+ 14 - 140
Carthage/Checkouts/realm-cocoa/Realm/RLMObject.mm

@@ -29,15 +29,8 @@
 #import "RLMRealm_Private.hpp"
 #import "RLMRealm_Private.hpp"
 #import "RLMSchema_Private.h"
 #import "RLMSchema_Private.h"
 
 
-#import "collection_notifications.hpp"
 #import "object.hpp"
 #import "object.hpp"
 
 
-@interface RLMPropertyChange ()
-@property (nonatomic, readwrite, strong) NSString *name;
-@property (nonatomic, readwrite, strong, nullable) id previousValue;
-@property (nonatomic, readwrite, strong, nullable) id value;
-@end
-
 // We declare things in RLMObject which are actually implemented in RLMObjectBase
 // We declare things in RLMObject which are actually implemented in RLMObjectBase
 // for documentation's sake, which leads to -Wunimplemented-method warnings.
 // for documentation's sake, which leads to -Wunimplemented-method warnings.
 // Other alternatives to this would be to disable -Wunimplemented-method for this
 // Other alternatives to this would be to disable -Wunimplemented-method for this
@@ -159,27 +152,21 @@
     return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
     return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
 }
 }
 
 
+- (instancetype)freeze {
+    return RLMObjectFreeze(self);
+}
+
+- (BOOL)isFrozen {
+    return _realm.isFrozen;
+}
+
 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
-    return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
-                                                 NSArray *oldValues, NSArray *newValues, NSError *error) {
-        if (error) {
-            block(false, nil, error);
-        }
-        else if (!propertyNames) {
-            block(true, nil, nil);
-        }
-        else {
-            auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
-            for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
-                auto prop = [RLMPropertyChange new];
-                prop.name = propertyNames[i];
-                prop.previousValue = RLMCoerceToNil(oldValues[i]);
-                prop.value = RLMCoerceToNil(newValues[i]);
-                [properties addObject:prop];
-            }
-            block(false, properties, nil);
-        }
-    });
+    return RLMObjectAddNotificationBlock(self, block, nil);
+}
+
+- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block
+                                         queue:(nonnull dispatch_queue_t)queue {
+    return RLMObjectAddNotificationBlock(self, block, queue);
 }
 }
 
 
 + (NSString *)className {
 + (NSString *)className {
@@ -249,116 +236,3 @@ BOOL RLMIsObjectOrSubclass(Class klass) {
 BOOL RLMIsObjectSubclass(Class klass) {
 BOOL RLMIsObjectSubclass(Class klass) {
     return RLMIsKindOfClass(class_getSuperclass(class_getSuperclass(klass)), RLMObjectBase.class);
     return RLMIsKindOfClass(class_getSuperclass(class_getSuperclass(klass)), RLMObjectBase.class);
 }
 }
-
-@interface RLMObjectNotificationToken : RLMCancellationToken
-@end
-@implementation RLMObjectNotificationToken {
-@public
-    realm::Object _object;
-}
-@end
-
-RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
-    if (!obj->_realm) {
-        @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
-    }
-    [obj->_realm verifyNotificationsAreSupported:true];
-
-    struct {
-        void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
-        RLMObjectBase *object;
-
-        NSArray<NSString *> *propertyNames = nil;
-        NSArray *oldValues = nil;
-        bool deleted = false;
-
-        void populateProperties(realm::CollectionChangeSet const& c) {
-            if (propertyNames) {
-                return;
-            }
-            if (!c.deletions.empty()) {
-                deleted = true;
-                return;
-            }
-            if (c.columns.empty()) {
-                return;
-            }
-
-            auto properties = [NSMutableArray new];
-            for (size_t i = 0; i < c.columns.size(); ++i) {
-                if (c.columns[i].empty()) {
-                    continue;
-                }
-                if (auto prop = object->_info->propertyForTableColumn(i)) {
-                    [properties addObject:prop.name];
-                }
-            }
-            if (properties.count) {
-                propertyNames = properties;
-            }
-        }
-
-        NSArray *readValues(realm::CollectionChangeSet const& c) {
-            if (c.empty()) {
-                return nil;
-            }
-            populateProperties(c);
-            if (!propertyNames) {
-                return nil;
-            }
-
-            auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
-            for (NSString *name in propertyNames) {
-                id value = [object valueForKey:name];
-                if (!value || [value isKindOfClass:[RLMArray class]]) {
-                    [values addObject:NSNull.null];
-                }
-                else {
-                    [values addObject:value];
-                }
-            }
-            return values;
-        }
-
-        void before(realm::CollectionChangeSet const& c) {
-            @autoreleasepool {
-                oldValues = readValues(c);
-            }
-        }
-
-        void after(realm::CollectionChangeSet const& c) {
-            @autoreleasepool {
-                auto newValues = readValues(c);
-                if (deleted) {
-                    block(nil, nil, nil, nil);
-                }
-                else if (newValues) {
-                    block(propertyNames, oldValues, newValues, nil);
-                }
-                propertyNames = nil;
-                oldValues = nil;
-            }
-        }
-
-        void error(std::exception_ptr err) {
-            @autoreleasepool {
-                try {
-                    rethrow_exception(err);
-                }
-                catch (...) {
-                    NSError *error = nil;
-                    RLMRealmTranslateException(&error);
-                    block(nil, nil, nil, error);
-                }
-            }
-        }
-    } callback{block, obj};
-
-    realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
-    auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
-    token->_object = std::move(object);
-    return token;
-}
-
-@implementation RLMPropertyChange
-@end

+ 257 - 15
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectBase.mm

@@ -290,12 +290,12 @@ id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
 
 
 - (BOOL)isInvalidated {
 - (BOOL)isInvalidated {
     // if not unmanaged and our accessor has been detached, we have been deleted
     // if not unmanaged and our accessor has been detached, we have been deleted
-    return self.class == _objectSchema.accessorClass && !_row.is_attached();
+    return self.class == _objectSchema.accessorClass && !_row.is_valid();
 }
 }
 
 
 - (BOOL)isEqual:(id)object {
 - (BOOL)isEqual:(id)object {
     if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
     if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
-        if (_objectSchema.primaryKeyProperty) {
+        if (_objectSchema.primaryKeyProperty || _realm.isFrozen) {
             return RLMObjectBaseAreEqual(self, other);
             return RLMObjectBaseAreEqual(self, other);
         }
         }
     }
     }
@@ -304,12 +304,22 @@ id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
 
 
 - (NSUInteger)hash {
 - (NSUInteger)hash {
     if (_objectSchema.primaryKeyProperty) {
     if (_objectSchema.primaryKeyProperty) {
+        // If we have a primary key property, that's an immutable value which we
+        // can use as the identity of the object.
         id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
         id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
 
 
         // modify the hash of our primary key value to avoid potential (although unlikely) collisions
         // modify the hash of our primary key value to avoid potential (although unlikely) collisions
         return [primaryProperty hash] ^ 1;
         return [primaryProperty hash] ^ 1;
     }
     }
+    else if (_realm.isFrozen) {
+        // The object key can never change for frozen objects, so that's usable
+        // for objects without primary keys
+        return _row.get_key().value;
+    }
     else {
     else {
+        // Non-frozen objects without primary keys don't have any immutable
+        // concept of identity that we can hash so we have to fall back to
+        // pointer equality
         return [super hash];
         return [super hash];
     }
     }
 }
 }
@@ -366,29 +376,23 @@ id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
 
 
 #pragma mark - Thread Confined Protocol Conformance
 #pragma mark - Thread Confined Protocol Conformance
 
 
-- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
-    Object object(_realm->_realm, *_info->objectSchema, _row);
-    realm::ThreadSafeReference<Object> reference = _realm->_realm->obtain_thread_safe_reference(std::move(object));
-    return std::make_unique<realm::ThreadSafeReference<Object>>(std::move(reference));
+- (realm::ThreadSafeReference)makeThreadSafeReference {
+    return Object(_realm->_realm, *_info->objectSchema, _row);
 }
 }
 
 
 - (id)objectiveCMetadata {
 - (id)objectiveCMetadata {
     return nil;
     return nil;
 }
 }
 
 
-+ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
++ (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
                                      metadata:(__unused id)metadata
                                      metadata:(__unused id)metadata
                                         realm:(RLMRealm *)realm {
                                         realm:(RLMRealm *)realm {
-    REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Object> *>(reference.get()));
-    auto object_reference = static_cast<realm::ThreadSafeReference<Object> *>(reference.get());
-
-    Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference));
+    Object object = reference.resolve<Object>(realm->_realm);
     if (!object.is_valid()) {
     if (!object.is_valid()) {
         return nil;
         return nil;
     }
     }
     NSString *objectClassName = @(object.get_object_schema().name.c_str());
     NSString *objectClassName = @(object.get_object_schema().name.c_str());
-
-    return RLMCreateObjectAccessor(realm->_info[objectClassName], object.row().get_index());
+    return RLMCreateObjectAccessor(realm->_info[objectClassName], object.obj());
 }
 }
 
 
 @end
 @end
@@ -446,12 +450,30 @@ BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
         return NO;
         return NO;
     }
     }
     // if either are detached
     // if either are detached
-    if (!o1->_row.is_attached() || !o2->_row.is_attached()) {
+    if (!o1->_row.is_valid() || !o2->_row.is_valid()) {
         return NO;
         return NO;
     }
     }
     // if table and index are the same
     // if table and index are the same
     return o1->_row.get_table() == o2->_row.get_table()
     return o1->_row.get_table() == o2->_row.get_table()
-        && o1->_row.get_index() == o2->_row.get_index();
+        && o1->_row.get_key() == o2->_row.get_key();
+}
+
+id RLMObjectFreeze(RLMObjectBase *obj) {
+    if (!obj->_realm && !obj.isInvalidated) {
+        @throw RLMException(@"Unmanaged objects cannot be frozen.");
+    }
+    RLMVerifyAttached(obj);
+    if (obj->_realm.frozen) {
+        return obj;
+    }
+    RLMRealm *frozenRealm = [obj->_realm freeze];
+    RLMObjectBase *frozen = RLMCreateManagedAccessor(obj.class, &frozenRealm->_info[obj->_info->rlmObjectSchema.className]);
+    frozen->_row = frozenRealm->_realm->import_copy_of(obj->_row);
+    if (!frozen->_row.is_valid()) {
+        @throw RLMException(@"Cannot freeze an object in the same write transaction as it was created in.");
+    }
+    RLMInitializeSwiftAccessorGenerics(frozen);
+    return frozen;
 }
 }
 
 
 id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
 id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
@@ -466,3 +488,223 @@ id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
         @throw;
         @throw;
     }
     }
 }
 }
+
+#pragma mark - Notifications
+
+namespace {
+struct ObjectChangeCallbackWrapper {
+    RLMObjectNotificationCallback block;
+    RLMObjectBase *object;
+
+    NSArray<NSString *> *propertyNames = nil;
+    NSArray *oldValues = nil;
+    bool deleted = false;
+
+    void populateProperties(realm::CollectionChangeSet const& c) {
+        if (propertyNames) {
+            return;
+        }
+        if (!c.deletions.empty()) {
+            deleted = true;
+            return;
+        }
+        if (c.columns.empty()) {
+            return;
+        }
+
+        auto properties = [NSMutableArray new];
+        for (RLMProperty *property in object->_info->rlmObjectSchema.properties) {
+            if (c.columns.count(object->_info->tableColumn(property).value)) {
+                [properties addObject:property.name];
+            }
+        }
+        if (properties.count) {
+            propertyNames = properties;
+        }
+    }
+
+    NSArray *readValues(realm::CollectionChangeSet const& c) {
+        if (c.empty()) {
+            return nil;
+        }
+        populateProperties(c);
+        if (!propertyNames) {
+            return nil;
+        }
+
+        auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
+        for (NSString *name in propertyNames) {
+            id value = [object valueForKey:name];
+            if (!value || [value isKindOfClass:[RLMArray class]]) {
+                [values addObject:NSNull.null];
+            }
+            else {
+                [values addObject:value];
+            }
+        }
+        return values;
+    }
+
+    void before(realm::CollectionChangeSet const& c) {
+        @autoreleasepool {
+            oldValues = readValues(c);
+        }
+    }
+
+    void after(realm::CollectionChangeSet const& c) {
+        @autoreleasepool {
+            auto newValues = readValues(c);
+            if (deleted) {
+                block(nil, nil, nil, nil, nil);
+            }
+            else if (newValues) {
+                block(object, propertyNames, oldValues, newValues, nil);
+            }
+            propertyNames = nil;
+            oldValues = nil;
+        }
+    }
+
+    void error(std::exception_ptr err) {
+        @autoreleasepool {
+            try {
+                rethrow_exception(err);
+            }
+            catch (...) {
+                NSError *error = nil;
+                RLMRealmTranslateException(&error);
+                block(nil, nil, nil, nil, error);
+            }
+        }
+    }
+};
+} // anonymous namespace
+
+@interface RLMPropertyChange ()
+@property (nonatomic, readwrite, strong) NSString *name;
+@property (nonatomic, readwrite, strong, nullable) id previousValue;
+@property (nonatomic, readwrite, strong, nullable) id value;
+@end
+
+@implementation RLMPropertyChange
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<RLMPropertyChange: %p> %@ %@ -> %@",
+            (__bridge void *)self, _name, _previousValue, _value];
+}
+@end
+@interface RLMObjectNotificationToken : RLMNotificationToken
+@end
+
+@implementation RLMObjectNotificationToken {
+    std::mutex _mutex;
+    __unsafe_unretained RLMRealm *_realm;
+    realm::Object _object;
+    realm::NotificationToken _token;
+}
+
+- (RLMRealm *)realm {
+    return _realm;
+}
+
+- (void)suppressNextNotification {
+    std::lock_guard<std::mutex> lock(_mutex);
+    if (_object.is_valid()) {
+        _token.suppress_next();
+    }
+}
+
+- (void)invalidate {
+    std::lock_guard<std::mutex> lock(_mutex);
+    _realm = nil;
+    _token = {};
+    _object = {};
+}
+
+- (void)addNotificationBlock:(RLMObjectNotificationCallback)block
+         threadSafeReference:(RLMThreadSafeReference *)tsr
+                      config:(RLMRealmConfiguration *)config
+                       queue:(dispatch_queue_t)queue {
+    std::lock_guard<std::mutex> lock(_mutex);
+    if (!_realm) {
+        // Token was invalidated before we got this far
+        return;
+    }
+
+    NSError *error;
+    RLMRealm *realm = _realm = [RLMRealm realmWithConfiguration:config queue:queue error:&error];
+    if (!realm) {
+        block(nil, nil, nil, nil, error);
+        return;
+    }
+    RLMObjectBase *obj = [realm resolveThreadSafeReference:tsr];
+
+    _object = realm::Object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
+    _token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj});
+}
+
+- (void)addNotificationBlock:(RLMObjectNotificationCallback)block object:(RLMObjectBase *)obj {
+    _object = realm::Object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
+    _realm = obj->_realm;
+    _token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj});
+}
+
+RLMNotificationToken *RLMObjectBaseAddNotificationBlock(RLMObjectBase *obj, dispatch_queue_t queue,
+                                                        RLMObjectNotificationCallback block) {
+    if (!obj->_realm) {
+        @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
+    }
+
+    if (!queue) {
+        [obj->_realm verifyNotificationsAreSupported:true];
+        auto token = [[RLMObjectNotificationToken alloc] init];
+        token->_realm = obj->_realm;
+        [token addNotificationBlock:block object:obj];
+        return token;
+    }
+
+    RLMThreadSafeReference *tsr = [RLMThreadSafeReference referenceWithThreadConfined:(id)obj];
+    auto token = [[RLMObjectNotificationToken alloc] init];
+    token->_realm = obj->_realm;
+    RLMRealmConfiguration *config = obj->_realm.configuration;
+    dispatch_async(queue, ^{
+        @autoreleasepool {
+            [token addNotificationBlock:block threadSafeReference:tsr config:config queue:queue];
+        }
+    });
+    return token;
+}
+
+@end
+
+RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectChangeBlock block, dispatch_queue_t queue) {
+    return RLMObjectBaseAddNotificationBlock(obj, queue, ^(RLMObjectBase *, NSArray<NSString *> *propertyNames,
+                                                           NSArray *oldValues, NSArray *newValues, NSError *error) {
+        if (error) {
+            block(false, nil, error);
+        }
+        else if (!propertyNames) {
+            block(true, nil, nil);
+        }
+        else {
+            auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
+            for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
+                auto prop = [RLMPropertyChange new];
+                prop.name = propertyNames[i];
+                prop.previousValue = RLMCoerceToNil(oldValues[i]);
+                prop.value = RLMCoerceToNil(newValues[i]);
+                [properties addObject:prop];
+            }
+            block(false, properties, nil);
+        }
+    });
+}
+
+uint64_t RLMObjectBaseGetCombineId(__unsafe_unretained RLMObjectBase *const obj) {
+    if (obj.invalidated) {
+        RLMVerifyAttached(obj);
+    }
+    if (obj->_realm) {
+        return obj->_row.get_key().value;
+    }
+    return reinterpret_cast<uint64_t>((__bridge void *)obj);
+}

+ 0 - 6
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectSchema.mm

@@ -343,12 +343,6 @@ using namespace realm;
         return _swiftGenericProperties;
         return _swiftGenericProperties;
     }
     }
 
 
-    // This check isn't semantically required, but avoiding accessing the local
-    // static helps perf in the obj-c case
-    if (!_isSwiftClass) {
-        return _swiftGenericProperties = @[];
-    }
-
     // Check if it's a swift class using the obj-c API
     // Check if it's a swift class using the obj-c API
     static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject");
     static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject");
     if (![_accessorClass isSubclassOfClass:s_swiftObjectClass]) {
     if (![_accessorClass isSubclassOfClass:s_swiftObjectClass]) {

+ 3 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.h

@@ -80,14 +80,13 @@ void RLMInitializeSwiftAccessorGenerics(RLMObjectBase *object);
 
 
 namespace realm {
 namespace realm {
     class Table;
     class Table;
-    template<typename T> class BasicRowExpr;
-    using RowExpr = BasicRowExpr<Table>;
+    class Obj;
 }
 }
 class RLMClassInfo;
 class RLMClassInfo;
 
 
 // Create accessors
 // Create accessors
-RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, NSUInteger index) NS_RETURNS_RETAINED;
-RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, realm::RowExpr row) NS_RETURNS_RETAINED;
+RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, int64_t key) NS_RETURNS_RETAINED;
+RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, realm::Obj&& obj) NS_RETURNS_RETAINED;
 #endif
 #endif
 
 
 NS_ASSUME_NONNULL_END
 NS_ASSUME_NONNULL_END

+ 15 - 10
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.mm

@@ -70,6 +70,11 @@ static inline void RLMVerifyRealmRead(__unsafe_unretained RLMRealm *const realm)
         @throw RLMException(@"Realm must not be nil");
         @throw RLMException(@"Realm must not be nil");
     }
     }
     [realm verifyThread];
     [realm verifyThread];
+    if (realm->_realm->is_closed()) {
+        // This message may seem overly specific, but frozen Realms are currently
+        // the only ones which we outright close.
+        @throw RLMException(@"Cannot read from a frozen Realm which has been invalidated.");
+    }
 }
 }
 
 
 static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm) {
 static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm) {
@@ -144,7 +149,7 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object,
     try {
     try {
         realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object,
         realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object,
                               static_cast<CreatePolicy>(updatePolicy),
                               static_cast<CreatePolicy>(updatePolicy),
-                              -1, &object->_row);
+                              {}, &object->_row);
     }
     }
     catch (std::exception const& e) {
     catch (std::exception const& e) {
         @throw RLMException(e);
         @throw RLMException(e);
@@ -180,7 +185,7 @@ RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *classN
     RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, &info);
     RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, &info);
     try {
     try {
         object->_row = realm::Object::create(c, realm->_realm, *info.objectSchema, (id)value,
         object->_row = realm::Object::create(c, realm->_realm, *info.objectSchema, (id)value,
-                                             static_cast<realm::CreatePolicy>(updatePolicy)).row();
+                                             static_cast<CreatePolicy>(updatePolicy)).obj();
     }
     }
     catch (std::exception const& e) {
     catch (std::exception const& e) {
         @throw RLMException(e);
         @throw RLMException(e);
@@ -198,9 +203,9 @@ void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object,
     RLMVerifyInWriteTransaction(object->_realm);
     RLMVerifyInWriteTransaction(object->_realm);
 
 
     // move last row to row we are deleting
     // move last row to row we are deleting
-    if (object->_row.is_attached()) {
+    if (object->_row.is_valid()) {
         RLMTrackDeletions(realm, ^{
         RLMTrackDeletions(realm, ^{
-            object->_row.move_last_over();
+            object->_row.remove();
         });
         });
     }
     }
 
 
@@ -237,7 +242,7 @@ RLMResults *RLMGetObjects(__unsafe_unretained RLMRealm *const realm,
     }
     }
 
 
     return [RLMResults resultsWithObjectInfo:info
     return [RLMResults resultsWithObjectInfo:info
-                                     results:realm::Results(realm->_realm, *info.table())];
+                                     results:realm::Results(realm->_realm, info.table())];
 }
 }
 
 
 id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) {
 id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) {
@@ -253,21 +258,21 @@ id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) {
                                                       key ?: NSNull.null);
                                                       key ?: NSNull.null);
         if (!obj.is_valid())
         if (!obj.is_valid())
             return nil;
             return nil;
-        return RLMCreateObjectAccessor(info, obj.row());
+        return RLMCreateObjectAccessor(info, obj.obj());
     }
     }
     catch (std::exception const& e) {
     catch (std::exception const& e) {
         @throw RLMException(e);
         @throw RLMException(e);
     }
     }
 }
 }
 
 
-RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, NSUInteger index) {
-    return RLMCreateObjectAccessor(info, (*info.table())[index]);
+RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, int64_t key) {
+    return RLMCreateObjectAccessor(info, info.table()->get_object(realm::ObjKey(key)));
 }
 }
 
 
 // Create accessor and register with realm
 // Create accessor and register with realm
-RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, realm::RowExpr row) {
+RLMObjectBase *RLMCreateObjectAccessor(RLMClassInfo& info, realm::Obj&& obj) {
     RLMObjectBase *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, &info);
     RLMObjectBase *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, &info);
-    accessor->_row = row;
+    accessor->_row = std::move(obj);
     RLMInitializeSwiftAccessorGenerics(accessor);
     RLMInitializeSwiftAccessorGenerics(accessor);
     return accessor;
     return accessor;
 }
 }

+ 13 - 2
Carthage/Checkouts/realm-cocoa/Realm/RLMObject_Private.h

@@ -50,11 +50,16 @@ FOUNDATION_EXTERN id _Nullable RLMValidatedValueForProperty(id object, NSString
 // Compare two RLObjectBases
 // Compare two RLObjectBases
 FOUNDATION_EXTERN BOOL RLMObjectBaseAreEqual(RLMObjectBase * _Nullable o1, RLMObjectBase * _Nullable o2);
 FOUNDATION_EXTERN BOOL RLMObjectBaseAreEqual(RLMObjectBase * _Nullable o1, RLMObjectBase * _Nullable o2);
 
 
-typedef void (^RLMObjectNotificationCallback)(NSArray<NSString *> *_Nullable propertyNames,
+typedef void (^RLMObjectNotificationCallback)(RLMObjectBase *_Nullable object,
+                                              NSArray<NSString *> *_Nullable propertyNames,
                                               NSArray *_Nullable oldValues,
                                               NSArray *_Nullable oldValues,
                                               NSArray *_Nullable newValues,
                                               NSArray *_Nullable newValues,
                                               NSError *_Nullable error);
                                               NSError *_Nullable error);
-FOUNDATION_EXTERN RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block);
+FOUNDATION_EXTERN RLMNotificationToken *RLMObjectBaseAddNotificationBlock(RLMObjectBase *obj,
+                                                                          dispatch_queue_t _Nullable queue,
+                                                                          RLMObjectNotificationCallback block);
+RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectChangeBlock block,
+                                                    dispatch_queue_t _Nullable queue);
 
 
 // Returns whether the class is a descendent of RLMObjectBase
 // Returns whether the class is a descendent of RLMObjectBase
 FOUNDATION_EXTERN BOOL RLMIsObjectOrSubclass(Class klass);
 FOUNDATION_EXTERN BOOL RLMIsObjectOrSubclass(Class klass);
@@ -64,6 +69,12 @@ FOUNDATION_EXTERN BOOL RLMIsObjectSubclass(Class klass);
 
 
 FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth;
 FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth;
 
 
+FOUNDATION_EXTERN id RLMObjectFreeze(RLMObjectBase *obj) NS_RETURNS_RETAINED;
+
+// Gets an object identifier suitable for use with Combine. This value may
+// change when an unmanaged object is added to the Realm.
+FOUNDATION_EXTERN uint64_t RLMObjectBaseGetCombineId(RLMObjectBase *);
+
 @interface RLMManagedPropertyAccessor : NSObject
 @interface RLMManagedPropertyAccessor : NSObject
 + (void)initializeObject:(void *)object parent:(RLMObjectBase *)parent property:(RLMProperty *)property;
 + (void)initializeObject:(void *)object parent:(RLMObjectBase *)parent property:(RLMProperty *)property;
 + (id)get:(void *)pointer;
 + (id)get:(void *)pointer;

Some files were not shown because too many files changed in this diff