Skip to content

Commit

Permalink
Upgrade to core 14 (#8496)
Browse files Browse the repository at this point in the history
  • Loading branch information
tgoyne committed Mar 21, 2024
1 parent 33a1eee commit 49c5912
Show file tree
Hide file tree
Showing 18 changed files with 326 additions and 187 deletions.
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
x.y.z Release notes (yyyy-MM-dd)
=============================================================

This version introduces a new Realm file format version (v24). Opening existing
Realm files will automatically upgrade the files, making them unable to be
opened by older versions. This upgrade process should typically be very fast
unless you have large Sets of AnyRealmValue, String, or Data, which have to be rewritten.

A backup will automatically be created next to the Realm before performing the
upgrade. Downgrading to older versions of Realm will attempt to automatically
restore the backup, or it will be deleted after three months.

### Enhancements
* Storage of Decimal128 properties has been optimised similarly to Int
properties so that the individual values will take up 0 bits (if all nulls),
32 bits, 64 bits or 128 bits depending on what is needed.
([Core #6111](https://github.com/realm/realm-core/pull/6111))
* Improve file compaction performance on arm64 platforms for encrypted files
between 16kB and 4MB in size. ([PR #7492](https://github.com/realm/realm-core/pull/7492)).

### Fixed
* Sorting on binary Data was done by comparing bytes as signed char rather than
unsigned char, resulting in very strange orders (since sorting on Data was
enabled in v6.0.4)
* Sorting on AnyRealmValue did not use a valid total ordering, and certain
combinations of values could result in values not being sorted or potentially
even crashes. The resolution for this will result in some previously-valid
combinations of values of different types being sorted in different orders
than previously (since the introduction of AnyRealmValue in 10.8.0).
* RLMSet/MutableSet was inconsistent about if it considered a String and a Data
containing the utf-8 encoded bytes of that String to be equivalent. They are
now always considered distinct. (since the introduction of sets in v10.8.0).
* Equality queries on a Mixed property with an index could sometimes return
incorrect results if values of different types happened to have the same hash
code. ([Core 6407](https://github.com/realm/realm-core/issues/6407) since v10.8.0).
* Creating more than 8388606 links pointing to a single object would crash.
([Core #6577](https://github.com/realm/realm-core/issues/6577), since v5.0.0)
* A Realm generated on a non-apple ARM 64 device and copied to another platform
(and vice-versa) were non-portable due to a sorting order difference. This
impacts strings or binaries that have their first difference at a non-ascii
character. These items may not be found in a set, or in an indexed column if
the strings had a long common prefix (> 200 characters).
([Core #6670](https://github.com/realm/realm-core/pull/6670), since 2.0.0 for indexes, and since since the introduction of sets in v10.8.0)
* Fix a spurious crash related to opening a Realm on background thread while
the process was in the middle of exiting ([Core #7420](https://github.com/realm/realm-core/pull/7420)).
* Opening a Realm with a cached user while offline would fail to retry some
steps of the connection process and instead report a fatal error.
([#7349](https://github.com/realm/realm-core/issues/7349), since v10.46.0)

### Breaking Changes
* Drop support for opening pre-v5.0.0 Realm files.

### Compatibility
* Realm Studio: 14.0.1 or later.
* APIs are backwards compatible with all previous releases in the 10.x.y series.
* Carthage release for Swift is built with Xcode 15.3.0.
* CocoaPods: 1.10 or later.
* Xcode: 14.2-15.3.0. Note that we will be dropping support for Xcode 14 when
Apple begins requiring Xcode 15 for app store submissions on April 29.

### Internal
* Upgraded realm-core from 13.26.0 to 14.3.0

10.48.1 Release notes (2024-03-15)
=============================================================

Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import PackageDescription
import Foundation

let coreVersion = Version("13.26.0")
let coreVersion = Version("14.3.0")
let cocoaVersion = Version("10.48.1")

let cxxSettings: [CXXSetting] = [
Expand Down
2 changes: 1 addition & 1 deletion Realm/ObjectServerTests/RLMSyncTestCase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ - (RLMMongoCollection *)collectionForType:(Class)type app:(RLMApp *)app {
@end

int64_t RLMGetClientFileIdent(RLMRealm *realm) {
return realm::SyncSession::OnlyForTesting::get_file_ident(*realm->_realm->sync_session()).ident;
return realm->_realm->sync_session()->get_file_ident().ident;
}

#endif // TARGET_OS_OSX
3 changes: 1 addition & 2 deletions Realm/RLMBSON_Private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@

namespace realm::bson {
class Bson;
template <typename> class IndexedMap;
using BsonDocument = IndexedMap<Bson>;
class BsonDocument;
}

realm::bson::Bson RLMConvertRLMBSONToBson(id<RLMBSON> b);
Expand Down
4 changes: 2 additions & 2 deletions Realm/RLMLogger.mm
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ static RLMLogLevel logLevelForLevel(Level logLevel) {
}

struct CocoaLogger : public Logger {
void do_log(Level level, const std::string& message) override {
void do_log(const realm::util::LogCategory&, Level level, const std::string& message) override {
NSLog(@"%@: %@", levelPrefix(level), RLMStringDataToNSString(message));
}
};

class CustomLogger : public Logger {
public:
RLMLoggerFunction function;
void do_log(Level level, const std::string& message) override {
void do_log(const realm::util::LogCategory&, Level level, const std::string& message) override {
@autoreleasepool {
if (function) {
function(logLevelForLevel(level), RLMStringDataToNSString(message));
Expand Down
2 changes: 1 addition & 1 deletion Realm/RLMMongoCollection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ - (void)insertOneDocument:(NSDictionary<NSString *, id<RLMBSON>> *)document
- (void)insertManyDocuments:(NSArray<NSDictionary<NSString *, id<RLMBSON>> *> *)documents
completion:(RLMMongoInsertManyBlock)completion {
self.collection.insert_many(toBsonArray(documents),
[completion](std::vector<realm::bson::Bson> insertedIds,
[completion](realm::bson::BsonArray insertedIds,
std::optional<realm::app::AppError> error) {
if (error) {
return completion(nil, makeError(*error));
Expand Down
2 changes: 1 addition & 1 deletion Realm/RLMObservation.mm
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ void RLMClearTable(RLMClassInfo &objectSchema) {
}

NSString *name = observer->columnName(link.origin_col_key);
if (observer->getRow().get_table()->get_column_type(link.origin_col_key) != type_LinkList) {
if (!link.origin_col_key.is_list()) {
_changes.push_back({observer, name});
continue;
}
Expand Down
88 changes: 64 additions & 24 deletions Realm/RLMQueryUtil.mm
Original file line number Diff line number Diff line change
Expand Up @@ -684,32 +684,30 @@ Query make_diacritic_insensitive_constraint(NSPredicateOperatorType operatorType
}
}

// static_assert is always evaluated even if it's inside a if constexpr
// unless the value is derived from the template argument, in which case it's
// only evaluated if that branch is active
template <typename> struct AlwaysFalse : std::false_type {};

template <typename C, typename T>
void QueryBuilder::do_add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
NSComparisonPredicateOptions predicateOptions,
C&& column, T&& value) {
bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
Query make_diacritic_sensitive_constraint(NSPredicateOperatorType operatorType,
bool caseSensitive, C& column, T const& value)
{
switch (operatorType) {
case NSBeginsWithPredicateOperatorType:
add_substring_constraint(value, column.begins_with(value, caseSensitive));
break;
return column.begins_with(value, caseSensitive);
case NSEndsWithPredicateOperatorType:
add_substring_constraint(value, column.ends_with(value, caseSensitive));
break;
return column.ends_with(value, caseSensitive);
case NSContainsPredicateOperatorType:
add_substring_constraint(value, column.contains(value, caseSensitive));
break;
return column.contains(value, caseSensitive);
case NSEqualToPredicateOperatorType:
m_query.and_query(column.equal(value, caseSensitive));
break;
return column.equal(value, caseSensitive);
case NSNotEqualToPredicateOperatorType:
m_query.and_query(column.not_equal(value, caseSensitive));
break;
return column.not_equal(value, caseSensitive);
case NSLikePredicateOperatorType:
m_query.and_query(column.like(value, caseSensitive));
break;
return column.like(value, caseSensitive);
default: {
if constexpr (is_any_v<C, Columns<String>, Columns<Lst<String>>, Columns<Set<String>>>) {
if constexpr (is_any_v<C, Columns<String>, Columns<Lst<String>>, Columns<Set<String>>, ColumnDictionaryKeys>) {
unsupportedOperator(RLMPropertyTypeString, operatorType);
}
else if constexpr (is_any_v<C, Columns<Binary>, Columns<Lst<Binary>>, Columns<Set<Binary>>>) {
Expand All @@ -726,10 +724,56 @@ Query make_diacritic_insensitive_constraint(NSPredicateOperatorType operatorType
@"Operator '%@' not supported for string queries on Dictionary.",
operatorName(operatorType));
}
else {
static_assert(AlwaysFalse<C>::value, "unsupported column type");
}
}
}
}

template <typename C, typename T>
void QueryBuilder::do_add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
NSComparisonPredicateOptions predicateOptions,
C&& column, T&& value) {
bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
Query condition = make_diacritic_sensitive_constraint(operatorType, caseSensitive, column, value);

// Queries on Mixed used to coerce Strings to Binary and vice-versa. Core
// no longer does this, but we can maintain compatibility by doing the
// coercion and checking both
// NEXT-MAJOR: we should remove this and realign with core's behavior
if constexpr (is_any_v<C, Columns<Mixed>, Columns<Lst<Mixed>>, Columns<Set<Mixed>>, Columns<Dictionary>>) {
Mixed m = value;
if (!m.is_null()) {
if (m.get_type() == type_String) {
m = m.export_to_type<BinaryData>();
}
else {
m = m.export_to_type<StringData>();
}

// Equality and substring operations need (col == strValue OR col == binValue),
// but not equals needs (col != strValue AND col != binValue)
if (operatorType != NSNotEqualToPredicateOperatorType) {
condition.Or();
}

condition.and_query(make_diacritic_sensitive_constraint(operatorType, caseSensitive, column, m));
}
}
switch (operatorType) {
case NSBeginsWithPredicateOperatorType:
case NSEndsWithPredicateOperatorType:
case NSContainsPredicateOperatorType:
add_substring_constraint(value, std::move(condition));
break;

default:
m_query.and_query(std::move(condition));
break;
}
}

template <typename C, typename T>
void QueryBuilder::add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
NSComparisonPredicateOptions predicateOptions,
Expand Down Expand Up @@ -1297,11 +1341,6 @@ KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, N

#pragma mark Collection Operations

// static_assert is always evaluated even if it's inside a if constexpr
// unless the value is derived from the template argument, in which case it's
// only evaluated if that branch is active
template <CollectionOperation::Type> struct AlwaysFalse : std::false_type {};

template <CollectionOperation::Type OperationType, typename Column>
auto collection_operation_expr_2(Column&& column) {
if constexpr (OperationType == CollectionOperation::Minimum) {
Expand All @@ -1317,7 +1356,8 @@ auto collection_operation_expr_2(Column&& column) {
return column.average();
}
else {
static_assert(AlwaysFalse<OperationType>::value, "invalid operation type");
static_assert(AlwaysFalse<std::integral_constant<CollectionOperation::Type, OperationType>>::value,
"invalid operation type");
}
}

Expand Down Expand Up @@ -1660,7 +1700,7 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression)
ColumnReference collectionColumn = column_reference_from_key_path(key_path_from_string(m_schema, objectSchema, keyPath), true);
RLMPrecondition(collectionColumn.property().dictionary, @"Invalid predicate",
@"Invalid keypath '%@': only dictionaries support subscript predicates.", functionExpression);
add_mixed_constraint(operatorType, options, collectionColumn.resolve<Dictionary>().key(mapKey.UTF8String), right.constantValue);
add_mixed_constraint(operatorType, options, std::move(collectionColumn.resolve<Dictionary>().key(mapKey.UTF8String)), right.constantValue);
}

void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
Expand Down
2 changes: 1 addition & 1 deletion Realm/RLMSyncConfiguration.mm
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ void RLMSetConfigInfoForClientResetCallbacks(realm::SyncConfig& syncConfig, RLMR

- (id<RLMBSON>)partitionValue {
if (!_config->partition_value.empty()) {
return RLMConvertBsonToRLMBSON(realm::bson::parse(_config->partition_value.c_str()));
return RLMConvertBsonToRLMBSON(realm::bson::parse(_config->partition_value));
}
return nil;
}
Expand Down
6 changes: 3 additions & 3 deletions Realm/RLMSyncManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ RLMSyncLogLevel logLevelForLevel(Level logLevel) {
#pragma mark - Loggers

struct CocoaSyncLogger : public realm::util::Logger {
void do_log(Level, const std::string& message) override {
void do_log(const realm::util::LogCategory&, Level, const std::string& message) override {
NSLog(@"Sync: %@", RLMStringDataToNSString(message));
}
};
Expand All @@ -86,7 +86,7 @@ void do_log(Level, const std::string& message) override {

struct CallbackLogger : public realm::util::Logger {
RLMSyncLogFunction logFn;
void do_log(Level level, const std::string& message) override {
void do_log(const realm::util::LogCategory&, Level level, const std::string& message) override {
@autoreleasepool {
logFn(logLevelForLevel(level), RLMStringDataToNSString(message));
}
Expand Down Expand Up @@ -202,7 +202,7 @@ - (void)resetForTesting {
_logger = nil;
_authorizationHeaderName = nil;
_customRequestHeaders = nil;
_syncManager->reset_for_testing();
_syncManager->tear_down_for_testing();
}

- (std::shared_ptr<realm::SyncManager>)syncManager {
Expand Down
2 changes: 0 additions & 2 deletions Realm/RLMUtil.mm
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,6 @@ id RLMMixedToObjc(realm::Mixed const& mixed,
}
case realm::type_UUID:
return [[NSUUID alloc] initWithRealmUUID:mixed.get<realm::UUID>()];
case realm::type_LinkList:
REALM_UNREACHABLE();
default:
@throw RLMException(@"Invalid data type for RLMPropertyTypeAny property.");
}
Expand Down
6 changes: 2 additions & 4 deletions Realm/TestUtils/TestUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,8 @@ bool RLMHasCachedRealmForPath(NSString *path) {
std::string encoded_prefix, encoded_body;
encoded_prefix.resize(realm::util::base64_encoded_size(unencoded_prefix.size()));
encoded_body.resize(realm::util::base64_encoded_size(unencoded_body.size()));
realm::util::base64_encode(unencoded_prefix.data(), unencoded_prefix.size(),
&encoded_prefix[0], encoded_prefix.size());
realm::util::base64_encode(unencoded_body.data(), unencoded_body.size(),
&encoded_body[0], encoded_body.size());
realm::util::base64_encode(unencoded_prefix, encoded_prefix);
realm::util::base64_encode(unencoded_body, encoded_body);
std::string suffix = "Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U";
return encoded_prefix + "." + encoded_body + "." + suffix;
}
Expand Down

0 comments on commit 49c5912

Please sign in to comment.