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

RCORE-2198: Support for additional properties (#7519) #7886

Merged
merged 8 commits into from
Oct 25, 2024
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Enhancements
* <New feature description> (PR [#????](https://github.com/realm/realm-core/pull/????))
* Storage of integers changed so that they take up less space in the file. This can cause commits and some queries to take a bit longer (PR [#7668](https://github.com/realm/realm-core/pull/7668))
* We now allow synchronizing, getting and setting properties that are not defined in the object schema (PR [#7886](https://github.com/realm/realm-core/pull/7886))

### Fixed
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
Expand Down
21 changes: 19 additions & 2 deletions bindgen/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ records:
schema_mode:
type: SchemaMode
default: SchemaMode::Automatic
flexible_schema:
type: bool
default: false
disable_format_upgrade:
type: bool
default: false
Expand Down Expand Up @@ -785,6 +788,7 @@ classes:
remove_object: '(key: ObjKey)'
get_link_target: '(column: ColKey) -> TableRef'
clear: ()
get_column_key: '(column: StringData) -> ColKey'
get_primary_key_column: '() -> ColKey'

Obj:
Expand All @@ -807,17 +811,28 @@ classes:
- '(column: ColKey, value: Mixed)'
- sig: '(column: ColKey, value: Mixed, is_default: bool)'
suffix: with_default
set_collection: '(column: ColKey, type: CollectionType) -> Obj'
- sig: '(column: StringData, value: Mixed)'
suffix: by_name
set_collection:
- '(column: ColKey, type: CollectionType) -> Obj'
- sig: '(prop_name: StringData, type: CollectionType)'
suffix: by_name
add_int: '(column: ColKey, value: int64_t) -> Obj'
get_linked_object: '(column: ColKey) const -> Nullable<Obj>'
to_string: () const -> std::string
get_backlink_count: '() const -> count_t'
erase_additional_prop: '(prop_name: StringData) const -> Obj'
get_additional_properties: '() -> std::vector<StringData>'
get_backlink_view: '(src_table: TableRef, src_col_key: ColKey) -> TableView'
create_and_set_linked_object: '(column: ColKey) -> Obj'

has_schema_property: '(column: StringData) -> bool'
get_collection_ptr: '(prop_name: StringData) -> CollectionPointer'
Transaction:
sharedPtrWrapped: TransactionRef

CollectionPointer:
cppName: CollectionBasePtr

ObjectStore:
staticMethods:
get_schema_version: '(group: Group) -> SchemaVersion'
Expand Down Expand Up @@ -1035,6 +1050,7 @@ classes:

Collection:
cppName: object_store::Collection
sharedPtrWrapped: SharedCollection
abstract: true
properties:
get_type: PropertyType
Expand Down Expand Up @@ -1063,6 +1079,7 @@ classes:
base: Collection
constructors:
make: '(r: SharedRealm, parent: const Obj&, col: ColKey)'
make_other: '(r: SharedRealm, parent: const Obj&, prop_name: StringData)'
methods:
get:
- sig: '(ndx: count_t) -> Obj'
Expand Down
67 changes: 67 additions & 0 deletions src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,11 @@ RLM_API bool realm_config_get_cached(realm_config_t*) RLM_API_NOEXCEPT;
*/
RLM_API void realm_config_set_automatic_backlink_handling(realm_config_t*, bool) RLM_API_NOEXCEPT;

/**
* Allow realm objects in the realm to have additional properties that are not defined in the schema.
*/
RLM_API void realm_config_set_flexible_schema(realm_config_t*, bool) RLM_API_NOEXCEPT;

/**
* Create a custom scheduler object from callback functions.
*
Expand Down Expand Up @@ -1645,6 +1650,13 @@ RLM_API realm_object_t* realm_object_from_thread_safe_reference(const realm_t*,
*/
RLM_API bool realm_get_value(const realm_object_t*, realm_property_key_t, realm_value_t* out_value);

/**
* Get the value for a property.
*
* @return True if no exception occurred.
*/
RLM_API bool realm_get_value_by_name(const realm_object_t*, const char* property_name, realm_value_t* out_value);

/**
* Get the values for several properties.
*
Expand Down Expand Up @@ -1680,6 +1692,41 @@ RLM_API bool realm_get_values(const realm_object_t*, size_t num_values, const re
*/
RLM_API bool realm_set_value(realm_object_t*, realm_property_key_t, realm_value_t new_value, bool is_default);

/**
* Set the value for a property. Property need not be defined in schema if flexible
* schema is enabled in configuration
*
* @param property_name The name of the property.
* @param new_value The new value for the property.
* @return True if no exception occurred.
*/
RLM_API bool realm_set_value_by_name(realm_object_t*, const char* property_name, realm_value_t new_value);

/**
* Examines if the object has a property with the given name.
* @param out_has_property will be true if the property exists.
* @return True if no exception occurred.
*/
RLM_API bool realm_has_property(realm_object_t*, const char* property_name, bool* out_has_property);

/**
* Get a list of properties set on the object that are not defined in the schema.
*
* @param out_prop_names A pointer to an array of const char* of size @a max. If the pointer is NULL,
* no names will be copied, but @a out_n will be set to the required size.
* @param max size of @a out_prop_names
* @param out_n number of names actually returned.
*/
RLM_API void realm_get_additional_properties(realm_object_t*, const char** out_prop_names, size_t max, size_t* out_n);

/**
* Erases a property from an object. You can't erase a property that is defined in the current schema.
*
* @param property_name The name of the property.
* @return True if the property was removed.
*/
RLM_API bool realm_erase_additional_property(realm_object_t*, const char* property_name);

/**
* Assign a JSON formatted string to a Mixed property. Underlying structures will be created as needed
*
Expand All @@ -1701,6 +1748,8 @@ RLM_API realm_object_t* realm_set_embedded(realm_object_t*, realm_property_key_t
*/
RLM_API realm_list_t* realm_set_list(realm_object_t*, realm_property_key_t);
RLM_API realm_dictionary_t* realm_set_dictionary(realm_object_t*, realm_property_key_t);
RLM_API realm_list_t* realm_set_list_by_name(realm_object_t*, const char* property_name);
RLM_API realm_dictionary_t* realm_set_dictionary_by_name(realm_object_t*, const char* property_name);

/** Return the object linked by the given property
*
Expand Down Expand Up @@ -1753,6 +1802,15 @@ RLM_API bool realm_set_values(realm_object_t*, size_t num_values, const realm_pr
*/
RLM_API realm_list_t* realm_get_list(realm_object_t*, realm_property_key_t);

/**
* Get a list instance for the property of an object by name.
*
* Note: It is up to the caller to call `realm_release()` on the returned list.
*
* @return A non-null pointer if no exception occurred.
*/
RLM_API realm_list_t* realm_get_list_by_name(realm_object_t*, const char*);

/**
* Create a `realm_list_t` from a pointer to a `realm::List`, copy-constructing
* the internal representation.
Expand Down Expand Up @@ -2258,6 +2316,15 @@ RLM_API realm_set_t* realm_set_from_thread_safe_reference(const realm_t*, realm_
*/
RLM_API realm_dictionary_t* realm_get_dictionary(realm_object_t*, realm_property_key_t);

/**
* Get a dictionary instance for the property of an object by name.
*
* Note: It is up to the caller to call `realm_release()` on the returned dictionary.
*
* @return A non-null pointer if no exception occurred.
*/
RLM_API realm_dictionary_t* realm_get_dictionary_by_name(realm_object_t*, const char*);

/**
* Create a `realm_dictionary_t` from a pointer to a `realm::object_store::Dictionary`,
* copy-constructing the internal representation.
Expand Down
3 changes: 2 additions & 1 deletion src/realm/db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2806,7 +2806,8 @@ void DB::async_request_write_mutex(TransactionRef& tr, util::UniqueFunction<void
}

inline DB::DB(Private, const DBOptions& options)
: m_upgrade_callback(std::move(options.upgrade_callback))
: m_allow_flexible_schema(options.allow_flexible_schema)
, m_upgrade_callback(std::move(options.upgrade_callback))
, m_log_id(util::gen_log_id(this))
{
if (options.enable_async_writes) {
Expand Down
1 change: 1 addition & 0 deletions src/realm/db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ class DB : public std::enable_shared_from_this<DB> {
SharedInfo* m_info = nullptr;
bool m_wait_for_change_enabled = true; // Initially wait_for_change is enabled
bool m_write_transaction_open GUARDED_BY(m_mutex) = false;
bool m_allow_flexible_schema;
std::string m_db_path;
int m_file_format_version = 0;
util::InterprocessMutex m_writemutex;
Expand Down
3 changes: 3 additions & 0 deletions src/realm/db_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ struct DBOptions {
/// will clear and reinitialize the file.
bool clear_on_invalid_file = false;

/// Allow setting properties not supported by a specific column on an object
bool allow_flexible_schema = false;

/// sys_tmp_dir will be used if the temp_dir is empty when creating DBOptions.
/// It must be writable and allowed to create pipe/fifo file on it.
/// set_sys_tmp_dir is not a thread-safe call and it is only supposed to be called once
Expand Down
7 changes: 6 additions & 1 deletion src/realm/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ Group::Group()
}


Group::Group(const std::string& file_path, const char* encryption_key)
Group::Group(const std::string& file_path, const char* encryption_key, bool allow_additional_properties)
: m_local_alloc(new SlabAlloc) // Throws
, m_alloc(*m_local_alloc)
, m_top(m_alloc)
, m_tables(m_alloc)
, m_table_names(m_alloc)
, m_allow_additional_properties(allow_additional_properties)
{
init_array_parents();

Expand Down Expand Up @@ -761,6 +762,10 @@ Table* Group::do_add_table(StringData name, Table::Type table_type, bool do_repl
Table* table = create_table_accessor(j);
table->do_set_table_type(table_type);

if (m_allow_additional_properties && name.begins_with(g_class_name_prefix)) {
table->do_add_additional_prop_column();
}

return table;
}

Expand Down
4 changes: 3 additions & 1 deletion src/realm/group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ class Group : public ArrayParent {
/// types that are derived from FileAccessError, the
/// derived exception type is thrown. Note that InvalidDatabase is
/// among these derived exception types.
explicit Group(const std::string& file, const char* encryption_key = nullptr);
explicit Group(const std::string& file, const char* encryption_key = nullptr,
bool allow_additional_properties = false);

/// Attach this Group instance to the specified memory buffer.
///
Expand Down Expand Up @@ -599,6 +600,7 @@ class Group : public ArrayParent {
mutable int m_num_tables = 0;
bool m_attached = false;
bool m_is_writable = true;
bool m_allow_additional_properties = false;
static std::optional<int> fake_target_file_format;

util::UniqueFunction<void(const CascadeNotification&)> m_notify_handler;
Expand Down
4 changes: 1 addition & 3 deletions src/realm/impl/array_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ class ArrayWriterBase {
bool only_modified = true;
bool compress = true;
const Table* table;
virtual ~ArrayWriterBase()
{
}
virtual ~ArrayWriterBase() {}

/// Write the specified array data and its checksum into free
/// space.
Expand Down
Loading
Loading