Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spec validator and fix discovered issues with spec #6702

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
# 13.13.0 Release notes

### Enhancements
* None.

### Fixed
* Allow numeric substitutions into a geospatial query. Example `location GEOWITHIN geoCircle([$0, $1], $2)`. ([#6662](https://github.com/realm/realm-core/issues/6662))
* Access token refresh for websockets was not updating the location metadata ([#6630](https://github.com/realm/realm-core/issues/6630), since v13.9.3)
* Fix several UBSan failures which did not appear to result in functional bugs ([#6649](https://github.com/realm/realm-core/pull/6649)).
* Fix an out-of-bounds read in sectioned results when sectioned are removed by modifying all objects in that section to no longer appear in that section ([#6649](https://github.com/realm/realm-core/pull/6649), since v13.12.0)
* Using both synchronous and asynchronous transactions on the same thread or scheduler could hit the assertion failure "!realm.is_in_transaction()" if one of the callbacks for an asynchronous transaction happened to be scheduled during a synchronous transaction ([#6659](https://github.com/realm/realm-core/issues/6659), since v11.8.0)

### Breaking changes
* None.

### Compatibility
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.

-----------

### Internals
* Simplify the implementation of query expression nodes which have a btree leaf cache.
* Fix a lock order inversion hit by object store tests running on linux. The cycle required test-specific code and so is not applicable to non-tests.
* Remove catch() clause to prevent truncating stack trace in AsyncOper::do_recycle_and_execute() ([PR #6667](https://github.com/realm/realm-core/pull/6667))

----------------------------------------------

# 13.12.0 Release notes

### Enhancements
* Improve performance of SectionedResults. With a single section it is now ~10% faster, and the runtime of sectioning no longer scales significantly with section count, giving >100% speedups when there are large numbers of sections ([PR #6606](https://github.com/realm/realm-core/pull/6606)).
* Very slightly improve performance of runtime thread checking on the main thread on Apple platforms. ([PR #6606](https://github.com/realm/realm-core/pull/6606))

### Fixed
* We could crash when removing backlinks in cases where forward links did not have a corresponding backlink due to corruption. We now silently ignore this inconsistency in release builds, allowing the app to continue. ([#6585](https://github.com/realm/realm-core/issues/6585), v6.0.0)
* If you freeze a Results based on a collection of objects, the result would be invalid if you delete the collection ([#6635](https://github.com/realm/realm-core/issues/6635), since V13.11.0)
* Geospatial polygons now have built in normalization and validation in line with the MongoDB server side behaviour and the geoJSON standard. ([#6607](https://github.com/realm/realm-core/pull/6607), since v13.11.0)
* Dictionary::get_any() would expose unresolved links rather than mapping them to null. In addition to allowing invalid objects to be read from Dictionaries, this resulted in queries on Dictionaries sometimes having incorrect results ([#6644](https://github.com/realm/realm-core/pull/6644)).

### Breaking changes
* `platform` and `cpu_arch` fields in the `device_info` structure in App::Config can no longer be provided by the SDK's, they are inferred by the library ([PR #6612](https://github.com/realm/realm-core/pull/6612))
* `bundle_id` is now a required field in the `device_info` structure in App::Config ([PR #6612](https://github.com/realm/realm-core/pull/6612))
* The API for sectioned results change notifications has changed. Changes are now reported in a vector rather than a sparse map.
* Renamed `GeoCenterSphere` to `GeoCircle` and in RQL `geoSphere` to `geoCircle`. The GeoPoints of query shapes are now validated before use and an exception will be thrown if invalid. Geospatial queries are no longer allowed on top-level tables. Fixed query results using ANY/ALL/NONE and matching on lists ([PR #6645](https://github.com/realm/realm-core/issues/6645))

### Compatibility
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.

-----------

### Internals
* Upgraded to Catch from v3.0.1 to v3.3.2. ([#6623](https://github.com/realm/realm-core/issues/6623))
* Added some geospatial benchmarks. ([#6622](https://github.com/realm/realm-core/issues/6622))

----------------------------------------------

# 13.11.0 Release notes

### Enhancements
Expand All @@ -8,6 +64,7 @@
* Fixed a fatal error (reported to the sync error handler) during client reset (or automatic PBS to FLX migration) if the reset has been triggered during an async open and the schema being applied has added new classes. ([#6601](https://github.com/realm/realm-core/issues/6601), since automatic client resets were introduced in v11.5.0)
* Full text search would sometimes find words where the word only matches the beginning of the search token ([#6591](https://github.com/realm/realm-core/issues/6591), since v13.0.0)
* Added missing includes of `<cstdint>` surfaced by gcc13 ([#6616](https://github.com/realm/realm-core/pull/6616))
* Prevent crashing on Results.freeze if underlying object or table were removed by making Results.is_valid correctly report its state. ([#6401](https://github.com/realm/realm-core/issues/6401))

### Compatibility
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.
Expand All @@ -18,6 +75,7 @@
* Add initial support for targeting WebAssembly with Emscripten ([PR #6263](https://github.com/realm/realm-core/pull/6263)).
* Sync session multiplexing is now enabled by default. The method `SyncManager::enable_session_multiplexing()` has been renamed `SyncManager::set_session_multiplexing()`. (PR [#6557](https://github.com/realm/realm-core/pull/6557))
* Bump protocol to v9 to indicate client has fix for client reset error during async open ([#6609](https://github.com/realm/realm-core/issues/6609))
* Fixed `Results::is_valid()` in order to return `false` if the results is bound to a deleted object or table. (PR [#6445](https://github.com/realm/realm-core/pull/6445))

----------------------------------------------

Expand All @@ -30,7 +88,6 @@

### Fixed
* Exclusion of words in a full text search does not work ([#6512](https://github.com/realm/realm-core/issues/6512), since v13.0.0 );
* Prevent crashing on Results.freeze if underlying object or table were removed by making Results.is_valid correctly report its state. ([#6401](https://github.com/realm/realm-core/issues/6401))

### Breaking changes
* Add `service_name` parameter to `realm_app_call_function` (PR [#6394](https://github.com/realm/realm-core/pull/6394)).
Expand All @@ -44,7 +101,6 @@
* Reduce the memory footprint of an automatic (discard or recover) client reset when there are large incoming changes from the server. ([#6567](https://github.com/realm/realm-core/issues/6567))
* `get_committed_file_format_version()` safe access to mappings vector from multiple threads.
* Add CI tests for Clang 16/Ubuntu 22.04 and update lint task ([PR #6563](https://github.com/realm/realm-core/pull/6563))
* Fixed `Results::is_valid()` in order to return `false` if the results is bound to a deleted object or table. (PR [#6445](https://github.com/realm/realm-core/pull/6445))

----------------------------------------------

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 versionStr = "13.11.0"
let versionStr = "13.13.0"
let versionPieces = versionStr.split(separator: "-")
let versionCompontents = versionPieces[0].split(separator: ".")
let versionExtra = versionPieces.count > 1 ? versionPieces[1] : ""
Expand Down
15 changes: 15 additions & 0 deletions bindgen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ file(GLOB_RECURSE BINDGEN_LIB_TS_FILES
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/src/*.ts
)
list(FILTER BINDGEN_LIB_TS_FILES EXCLUDE REGEX "templates/[^/]*\.ts$")
list(FILTER BINDGEN_LIB_TS_FILES EXCLUDE REGEX "tests/[^/]*\.ts$")

# Ninja buffers output so we need to tell tools to use colors even though stdout isn't a tty.
Expand Down Expand Up @@ -84,3 +85,17 @@ function(bindgen)
set_property(SOURCE ${FILE} PROPERTY INCLUDE_DIRECTORIES "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_FUNCTION_LIST_DIR}/src>")
endforeach()
endfunction()

bindgen(
TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/src/templates/validate_spec.ts
OUTPUTS ${CMAKE_CURRENT_BINARY_DIR}/validate_spec.cpp
OUTDIR ${CMAKE_CURRENT_BINARY_DIR}
)

add_library(ValidateBindgenSpec MODULE ${CMAKE_CURRENT_BINARY_DIR}/validate_spec.cpp)
set_property(TARGET ValidateBindgenSpec PROPERTY CXX_STANDARD 20)
target_link_libraries(ValidateBindgenSpec Realm::ObjectStore)
if (NOT MSVC)
# Ensure that linking to Realm::ObjectStore doesn't leave undefined symbols.
target_link_options(ValidateBindgenSpec PRIVATE LINKER:-z,defs)
endif()
57 changes: 16 additions & 41 deletions bindgen/spec.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=generated/spec.schema.json

headers:
- "realm_helpers.h"
- "realm/sync/config.hpp"
- "realm/sync/subscriptions.hpp"
- "realm/object-store/shared_realm.hpp"
Expand Down Expand Up @@ -455,6 +456,7 @@ records:
query_string: std::string

ObjectChangeSet:
cppName: Helpers::ObjectChangeSet
fields:
is_deleted: bool
changed_columns: std::vector<ColKey>
Expand Down Expand Up @@ -539,9 +541,9 @@ records:
type: std::error_code
cppName: get_system_error()
is_fatal: bool
simple_message: StringData
logURL: StringData
user_info: std::unordered_map<StringData, StringData>
simple_message: std::string_view
logURL: std::string_view
user_info: std::unordered_map<std::string, std::string>
is_unrecognized_by_client:
type: bool
default: false
Expand Down Expand Up @@ -577,11 +579,9 @@ records:
DeviceInfo:
cppName: app::App::Config::DeviceInfo
fields:
platform: std::string
platform_version: std::string
sdk_version: std::string
sdk: std::string
cpu_arch: std::string
device_name: std::string
device_version: std::string
framework_name: std::string
Expand All @@ -607,7 +607,6 @@ records:

opaqueTypes:
- Schema
- Group
- AuditInterface

classes:
Expand All @@ -631,15 +630,15 @@ classes:
make_network_transport: '(runRequest: (request: const Request&, callback: util::UniqueFunction<(response: Response&&)>&&) off_thread) -> SharedGenericNetworkTransport'
delete_data_for_object: '(realm: SharedRealm, object_type: StringData)'
is_empty_realm: '(realm: SharedRealm) -> bool'
base64_decode: '(input: StringData) -> BinaryData'
base64_decode: '(input: StringData) -> OwnedBinaryData'
make_logger_factory: '(log: (level: LoggerLevel, message: const std::string&) off_thread) -> LoggerFactory'
make_logger: '(log: (level: LoggerLevel, message: const std::string&) off_thread) -> SharedLogger'
simulate_sync_error: '(session: SyncSession&, code: const int&, message: const std::string&, type: const std::string&, is_fatal: bool)'
consume_thread_safe_reference_to_shared_realm: '(tsr: ThreadSafeReference) -> SharedRealm'
consume_thread_safe_reference_to_shared_realm: '(tsr: ThreadSafeReference&) -> SharedRealm'
file_exists: '(path: StringData) -> bool'
erase_subscription: '(subs: MutableSyncSubscriptionSet&, sub_to_remove: const SyncSubscription&) -> bool'
# This is added due to DescriptorOrdering not being exposed
get_results_description: '(results: const Results&) -> StringData'
get_results_description: '(results: const Results&) -> std::string'
feed_buffer: '(ws: WatchStream&, buffer: BinaryData)'
# Converts char* and length to a combined string_view argument to callback.
# TODO: Consider making preverify_ok a bool.
Expand Down Expand Up @@ -710,29 +709,6 @@ classes:
Transaction:
sharedPtrWrapped: TransactionRef

ObjectStore:
staticMethods:
get_schema_version: '(group: Group) -> SchemaVersion'
set_schema_version: '(group: Group, version: SchemaVersion)'
# verify_no_migration_required: '(changes: std::vector<SchemaChange>)'
#needs_migration: '(changes: std::vector<SchemaChange>) -> bool'
#verify_valid_additive_changes:
# - '(changes: std::vector<SchemaChange>) -> bool'
# - suffix: and_update_indexes
# sig: '(changes: std::vector<SchemaChange>, update_indexes: bool) -> bool'
#verify_valid_external_changes: '(changes: std::vector<SchemaChange>)'
#verify_compatible_for_immutable_and_readonly: '(changes: std::vector<SchemaChange>)'
#verify_no_changes_required: '(changes: std::vector<SchemaChange>)'

# apply_schema_changes: '(group: Transaction&, schema_version: SchemaVersion, target_schema: Schema&, target_schema_version: SchemaVersion, mode: SchemaMode, changes: std::vector<SchemaChange>, migration_function: () -> void)'
# apply_additive_changes: '(group: Group&, changes: std::vector<SchemaChanges>, update_indexes: bool)'
# table_for_object_type
# table_for_Schema_type

schema_from_group: '(group: Group) -> std::vector<ObjectSchema>'

# some more...

# TODO should this be nullable or always non-null?
Timestamp:
constructors:
Expand All @@ -754,7 +730,7 @@ classes:
Query:
properties:
get_table: ConstTableRef
get_description: StringData
get_description: std::string
methods:
count: () const -> count_t

Expand Down Expand Up @@ -784,9 +760,9 @@ classes:
get_type: PropertyType
methods:
size: () -> count_t
index_of:
- sig: '(value: Mixed) const -> count_t'
- sig: '(obj: Obj) const -> count_t'
index_of: # TODO should these be const?
- sig: '(value: Mixed) -> count_t'
- sig: '(obj: Obj) -> count_t'
suffix: 'obj'
get: # NOTE: this is actually a template with a defaulted template param.
- sig: '(index: count_t) -> Obj'
Expand All @@ -800,7 +776,7 @@ classes:
suffix: by_names
limit: '(max_count: count_t) const -> Results'
snapshot: '() const -> Results'
freeze: '(frozen_realm: SharedRealm) const -> Results'
freeze: '(frozen_realm: SharedRealm) -> Results' # TODO const
max: '(column: ColKey) -> util::Optional<Mixed>'
min: '(column: ColKey) -> util::Optional<Mixed>'
average: '(column: ColKey) -> util::Optional<Mixed>'
Expand Down Expand Up @@ -855,7 +831,6 @@ classes:
cancel_transaction: ()
freeze: () -> SharedRealm
last_seen_transaction_version: () -> uint64_t
read_group: () -> Group&
duplicate: () -> TransactionRef

update_schema: '(schema: std::vector<ObjectSchema>, version: SchemaVersion, migration_function: Nullable<std::function<(old_realm: SharedRealm, new_realm: SharedRealm, new_schema_handle: IgnoreArgument<Schema&>) -> void>>, initialization_function: Nullable<std::function<(realm: SharedRealm) -> void>>, in_transaction: bool) -> void'
Expand Down Expand Up @@ -1080,7 +1055,6 @@ classes:
SyncUser:
sharedPtrWrapped: SharedSyncUser
properties:
all_sessions: std::vector<SharedSyncSession>
is_logged_in: bool
identity: const std::string&
provider_type: const std::string&
Expand All @@ -1097,6 +1071,7 @@ classes:
subscribers_count: count_t
methods:
log_out: ()
all_sessions: () -> std::vector<SharedSyncSession>
session_for_on_disk_path: '(path: StringData) -> Nullable<SharedSyncSession>'
subscribe: '(observer: (user: IgnoreArgument<const SyncUser&>)) -> SyncUserSubscriptionToken'
unsubscribe: '(token: SyncUserSubscriptionToken)'
Expand Down Expand Up @@ -1219,7 +1194,7 @@ classes:
set_timeouts: '(timeouts: SyncClientTimeouts)'
reconnect: ()
wait_for_sessions_to_terminate: ()
path_for_realm: '(config: SyncConfig, custom_file_name: util::Optional<std::string>) -> StringData'
path_for_realm: '(config: SyncConfig, custom_file_name: util::Optional<std::string>) -> std::string'
get_existing_active_session: '(path: const std::string&) -> SharedSyncSession'

ThreadSafeReference: {}
Expand All @@ -1236,7 +1211,7 @@ classes:
properties:
state: SyncSessionState
connection_state: SyncSessionConnectionState
path: std::string
path: const std::string&
user: SharedSyncUser
config: SyncConfig
full_realm_url: util::Optional<std::string>
Expand Down
27 changes: 14 additions & 13 deletions bindgen/src/realm_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
// Equivalent to std::forward<decltype(x)>(x), but faster to compile and less impact on debug builds
#define FWD(x) ((decltype(x)&&)(x))

namespace realm::js {
namespace realm {
namespace {

// These types are exposed to JS in the spec.
Expand Down Expand Up @@ -281,20 +281,21 @@ struct Helpers {
return callback(server_address, server_port, std::string_view(pem_data, pem_size), preverify_ok, depth);
};
}
};

struct ObjectChangeSet {
ObjectChangeSet() = default;
/*implicit*/ ObjectChangeSet(const CollectionChangeSet& changes)
{
is_deleted = !changes.deletions.empty();
for (const auto& [col_key_val, index_set] : changes.columns) {
changed_columns.push_back(ColKey(col_key_val));
// TODO There is already a realm::ObjectChangeSet. Find a better name for this that doesn't collide.
struct ObjectChangeSet {
ObjectChangeSet() = default;
/*implicit*/ ObjectChangeSet(const CollectionChangeSet& changes)
{
is_deleted = !changes.deletions.empty();
for (const auto& [col_key_val, index_set] : changes.columns) {
changed_columns.push_back(ColKey(col_key_val));
}
}
}

bool is_deleted;
std::vector<ColKey> changed_columns;
bool is_deleted;
std::vector<ColKey> changed_columns;
};
};

////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -383,4 +384,4 @@ auto asSigned(T num)
}

} // namespace
} // namespace realm::js
} // namespace realm
Loading