From 4996b7ad8b3a12f573c8746208c1f9d6480db975 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 08:03:31 +0100 Subject: [PATCH 01/12] Update C library [4.1.0 -> 4.2.0] --- dev-doc/updating-c-library.md | 6 +++--- flutter_libs/linux/CMakeLists.txt | 2 +- flutter_libs/windows/CMakeLists.txt | 2 +- install.sh | 2 +- objectbox/CHANGELOG.md | 1 + sync_flutter_libs/linux/CMakeLists.txt | 2 +- sync_flutter_libs/windows/CMakeLists.txt | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dev-doc/updating-c-library.md b/dev-doc/updating-c-library.md index f5ae9dccd..3a86f1f7c 100644 --- a/dev-doc/updating-c-library.md +++ b/dev-doc/updating-c-library.md @@ -19,15 +19,15 @@ for the binding update script (see below) and for Flutter (`flutter_libs` and `sync_flutter_libs` plugins) on Linux and Windows: ```bash -./tool/set-c-version.sh 4.1.0 +./tool/set-c-version.sh 4.2.0 ``` ```text -* Flutter for Linux/Windows, Dart Native: update to [objectbox-c 4.1.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.1.0). +* Update database runtime library for Flutter for Linux/Windows, Dart Native to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). ``` ```text -Update C library [4.0.2 -> 4.1.0] +Update C library [4.1.0 -> 4.2.0] ``` ### Android diff --git a/flutter_libs/linux/CMakeLists.txt b/flutter_libs/linux/CMakeLists.txt index aca438e23..cbce9d6c1 100644 --- a/flutter_libs/linux/CMakeLists.txt +++ b/flutter_libs/linux/CMakeLists.txt @@ -44,7 +44,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) # ---------------------------------------------------------------------- # Download and add objectbox-c prebuilt library. -set(OBJECTBOX_VERSION 4.1.0) +set(OBJECTBOX_VERSION 4.2.0) set(OBJECTBOX_ARCH ${CMAKE_SYSTEM_PROCESSOR}) if (${OBJECTBOX_ARCH} MATCHES "x86_64") diff --git a/flutter_libs/windows/CMakeLists.txt b/flutter_libs/windows/CMakeLists.txt index 77595b411..eed019cbf 100644 --- a/flutter_libs/windows/CMakeLists.txt +++ b/flutter_libs/windows/CMakeLists.txt @@ -50,7 +50,7 @@ set(objectbox_flutter_libs_bundled_libraries # ---------------------------------------------------------------------- # Download and add objectbox-c prebuilt library. -set(OBJECTBOX_VERSION 4.1.0) +set(OBJECTBOX_VERSION 4.2.0) set(OBJECTBOX_ARCH ${CMAKE_SYSTEM_PROCESSOR}) if (${OBJECTBOX_ARCH} MATCHES "AMD64") diff --git a/install.sh b/install.sh index 2707c1c6c..bac35583d 100755 --- a/install.sh +++ b/install.sh @@ -5,7 +5,7 @@ set -eu # It's important that the generated dart bindings and the c-api library version match. Dart won't error on C function # signature mismatch, leading to obscure memory bugs. # For how to upgrade the version see dev-doc/updating-c-library.md -cLibVersion=4.1.0 +cLibVersion=4.2.0 os=$(uname) cLibArgs="$*" diff --git a/objectbox/CHANGELOG.md b/objectbox/CHANGELOG.md index f94cf4bac..3787bab0d 100644 --- a/objectbox/CHANGELOG.md +++ b/objectbox/CHANGELOG.md @@ -4,6 +4,7 @@ * Examples: demos are compatible with JDK 21 included with Android Studio Ladybug or later, require Flutter SDK 3.24 (with Dart SDK 3.5) or newer. * Requires at least Dart SDK 3.4 or Flutter SDK 3.22. +* Update database runtime library for Flutter for Linux/Windows, Dart Native to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). ## 4.1.0 (2025-02-04) diff --git a/sync_flutter_libs/linux/CMakeLists.txt b/sync_flutter_libs/linux/CMakeLists.txt index 4958308b2..246d71b49 100644 --- a/sync_flutter_libs/linux/CMakeLists.txt +++ b/sync_flutter_libs/linux/CMakeLists.txt @@ -44,7 +44,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) # ---------------------------------------------------------------------- # Download and add objectbox-c prebuilt library. -set(OBJECTBOX_VERSION 4.1.0) +set(OBJECTBOX_VERSION 4.2.0) set(OBJECTBOX_ARCH ${CMAKE_SYSTEM_PROCESSOR}) if (${OBJECTBOX_ARCH} MATCHES "x86_64") diff --git a/sync_flutter_libs/windows/CMakeLists.txt b/sync_flutter_libs/windows/CMakeLists.txt index 75e929653..4fcd94c55 100644 --- a/sync_flutter_libs/windows/CMakeLists.txt +++ b/sync_flutter_libs/windows/CMakeLists.txt @@ -50,7 +50,7 @@ set(objectbox_sync_flutter_libs_bundled_libraries # ---------------------------------------------------------------------- # Download and add objectbox-c prebuilt library. -set(OBJECTBOX_VERSION 4.1.0) +set(OBJECTBOX_VERSION 4.2.0) set(OBJECTBOX_ARCH ${CMAKE_SYSTEM_PROCESSOR}) if (${OBJECTBOX_ARCH} MATCHES "AMD64") From 5a45835bdec9f7b539d59e26b6d5eb119a7d7633 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 08:50:56 +0100 Subject: [PATCH 02/12] Update objectbox-android [4.1.0 -> 4.2.0] Bundled with C API 4.2.0 and ObjectBox 4.2.0-2025-03-04 --- dev-doc/updating-c-library.md | 12 ++++++------ flutter_libs/android/build.gradle | 2 +- objectbox/CHANGELOG.md | 3 +++ .../android/app/build.gradle | 2 +- sync_flutter_libs/android/build.gradle | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dev-doc/updating-c-library.md b/dev-doc/updating-c-library.md index 3a86f1f7c..c0994f1ec 100644 --- a/dev-doc/updating-c-library.md +++ b/dev-doc/updating-c-library.md @@ -35,19 +35,19 @@ Update C library [4.1.0 -> 4.2.0] For the Flutter plugins on Android ([view releases](https://github.com/objectbox/objectbox-java/releases)): ```bash -./tool/set-android-version.sh 4.1.0 +./tool/set-android-version.sh 4.2.0 ``` ```text -* Flutter for Android: update to [objectbox-android 4.1.0](https://github.com/objectbox/objectbox-java/releases/tag/V4.1.0). - If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make sure to - update to `io.objectbox:objectbox-android-objectbrowser:4.1.0` in `android/app/build.gradle`. +* Update database runtime library for Flutter for Android to 4.2.0. + If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make + sure to update to `io.objectbox:objectbox-android-objectbrowser:4.2.0` in `android/app/build.gradle`. ``` ```text -Update objectbox-android [4.0.3 -> 4.1.0] +Update objectbox-android [4.1.0 -> 4.2.0] -Bundled with C API 4.1.0 and ObjectBox 4.1.0-2025-01-28 +Bundled with C API 4.2.0 and ObjectBox 4.2.0-2025-03-04 ``` Note: the embedded C API and ObjectBox version can be looked up diff --git a/flutter_libs/android/build.gradle b/flutter_libs/android/build.gradle index 79bf4f154..788a1a9ad 100644 --- a/flutter_libs/android/build.gradle +++ b/flutter_libs/android/build.gradle @@ -52,6 +52,6 @@ android { // ObjectBox Android library that includes an ObjectBox C library version compatible with // the C API binding of the ObjectBox Dart package. // https://central.sonatype.com/search?q=g:io.objectbox%20objectbox-android - implementation "io.objectbox:objectbox-android:4.1.0" + implementation "io.objectbox:objectbox-android:4.2.0" } } diff --git a/objectbox/CHANGELOG.md b/objectbox/CHANGELOG.md index 3787bab0d..db970fd07 100644 --- a/objectbox/CHANGELOG.md +++ b/objectbox/CHANGELOG.md @@ -5,6 +5,9 @@ Flutter SDK 3.24 (with Dart SDK 3.5) or newer. * Requires at least Dart SDK 3.4 or Flutter SDK 3.22. * Update database runtime library for Flutter for Linux/Windows, Dart Native to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). +* Update database runtime library for Flutter for Android to 4.2.0. + If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make + sure to update to `io.objectbox:objectbox-android-objectbrowser:4.2.0` in `android/app/build.gradle`. ## 4.1.0 (2025-02-04) diff --git a/objectbox/example/flutter/objectbox_demo_relations/android/app/build.gradle b/objectbox/example/flutter/objectbox_demo_relations/android/app/build.gradle index 56fc22b7e..5da6c3569 100644 --- a/objectbox/example/flutter/objectbox_demo_relations/android/app/build.gradle +++ b/objectbox/example/flutter/objectbox_demo_relations/android/app/build.gradle @@ -83,5 +83,5 @@ dependencies { // Add the Android library with ObjectBox Admin only for debug builds. // Note: when the objectbox package updates, check if the Android // library below needs to be updated as well. - debugImplementation("io.objectbox:objectbox-android-objectbrowser:4.1.0") + debugImplementation("io.objectbox:objectbox-android-objectbrowser:4.2.0") } diff --git a/sync_flutter_libs/android/build.gradle b/sync_flutter_libs/android/build.gradle index 95ae6c0f1..39ec10d25 100644 --- a/sync_flutter_libs/android/build.gradle +++ b/sync_flutter_libs/android/build.gradle @@ -52,6 +52,6 @@ android { // ObjectBox Android library that includes an ObjectBox C library version compatible with // the C API binding of the ObjectBox Dart package. // https://central.sonatype.com/search?q=g:io.objectbox%20objectbox-sync-android - implementation "io.objectbox:objectbox-sync-android:4.1.0" + implementation "io.objectbox:objectbox-sync-android:4.2.0" } } From 38d5f87237a86d53043bb67a7cf1c50371b20194 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 08:03:24 +0100 Subject: [PATCH 03/12] Update C-API [4.1.0 -> 4.2.0] --- dev-doc/updating-c-library.md | 2 +- .../lib/src/native/bindings/objectbox-sync.h | 2 +- objectbox/lib/src/native/bindings/objectbox.h | 108 +++++++- .../lib/src/native/bindings/objectbox_c.dart | 237 ++++++++++++++++-- tool/update-c-binding.sh | 2 +- 5 files changed, 319 insertions(+), 32 deletions(-) diff --git a/dev-doc/updating-c-library.md b/dev-doc/updating-c-library.md index c0994f1ec..507a22626 100644 --- a/dev-doc/updating-c-library.md +++ b/dev-doc/updating-c-library.md @@ -96,5 +96,5 @@ Then manually: - Commit as ```text -Update C-API [4.0.2 -> 4.1.0] +Update C-API [4.1.0 -> 4.2.0] ``` diff --git a/objectbox/lib/src/native/bindings/objectbox-sync.h b/objectbox/lib/src/native/bindings/objectbox-sync.h index 2a154f39d..109bc8a3f 100644 --- a/objectbox/lib/src/native/bindings/objectbox-sync.h +++ b/objectbox/lib/src/native/bindings/objectbox-sync.h @@ -34,7 +34,7 @@ #include "objectbox.h" #if defined(static_assert) || defined(__cplusplus) -static_assert(OBX_VERSION_MAJOR == 4 && OBX_VERSION_MINOR == 1 && OBX_VERSION_PATCH == 0, // NOLINT +static_assert(OBX_VERSION_MAJOR == 4 && OBX_VERSION_MINOR == 2 && OBX_VERSION_PATCH == 0, // NOLINT "Versions of objectbox.h and objectbox-sync.h files do not match, please update"); #endif diff --git a/objectbox/lib/src/native/bindings/objectbox.h b/objectbox/lib/src/native/bindings/objectbox.h index 6973625cd..22a9602f3 100644 --- a/objectbox/lib/src/native/bindings/objectbox.h +++ b/objectbox/lib/src/native/bindings/objectbox.h @@ -52,7 +52,7 @@ extern "C" { /// When using ObjectBox as a dynamic library, you should verify that a compatible version was linked using /// obx_version() or obx_version_is_at_least(). #define OBX_VERSION_MAJOR 4 -#define OBX_VERSION_MINOR 1 +#define OBX_VERSION_MINOR 2 #define OBX_VERSION_PATCH 0 // values >= 100 are reserved for dev releases leading to the next minor/major increase //---------------------------------------------- @@ -615,6 +615,76 @@ typedef enum { OBXPropertyFlags_EXPIRATION_TIME = 65536, } OBXPropertyFlags; +/// A property type of an external system (e.g. another database) that has no default mapping to an ObjectBox type. +/// External property types numeric values start at 100 to avoid overlaps with ObjectBox's PropertyType. +/// (And if we ever support one of these as a primary type, we could share the numeric value?) +typedef enum { + /// Not a real type: represents uninitialized state and can be used for forward compatibility. + OBXExternalPropertyType_Unknown = 0, + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation, little endian (16 bytes) + OBXExternalPropertyType_Int128 = 100, + // OBXExternalPropertyType_Reserved1 = 101, + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + OBXExternalPropertyType_Uuid = 102, + /// IEEE 754 decimal128 type, e.g. supported by MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + OBXExternalPropertyType_Decimal128 = 103, + // OBXExternalPropertyType_Reserved2 = 104, + // OBXExternalPropertyType_Reserved3 = 105, + // OBXExternalPropertyType_Reserved4 = 106, + /// A key/value map; e.g. corresponds to a JSON object or a MongoDB document (although not keeping the key order). + /// Unlike the Flex type, this must contain a map value (e.g. not a vector or a scalar). + /// Representing type: Flex + /// Encoding: Flex + OBXExternalPropertyType_FlexMap = 107, + /// A vector (aka list or array) of flexible elements; e.g. corresponds to a JSON array or a MongoDB array. + /// Unlike the Flex type, this must contain a vector value (e.g. not a map or a scalar). + /// Representing type: Flex + /// Encoding: Flex + OBXExternalPropertyType_FlexVector = 108, + /// Placeholder (not yet used) for a JSON document. + /// Representing type: String + OBXExternalPropertyType_Json = 109, + /// Placeholder (not yet used) for a BSON document. + /// Representing type: ByteVector + OBXExternalPropertyType_Bson = 110, + /// JavaScript source code + /// Representing type: String + OBXExternalPropertyType_JavaScript = 111, + // OBXExternalPropertyType_Reserved5 = 112, + // OBXExternalPropertyType_Reserved6 = 113, + // OBXExternalPropertyType_Reserved7 = 114, + // OBXExternalPropertyType_Reserved8 = 115, + /// A vector (array) of Int128 values + OBXExternalPropertyType_Int128Vector = 116, + // OBXExternalPropertyType_Reserved9 = 117, + /// A vector (array) of Int128 values + OBXExternalPropertyType_UuidVector = 118, + // OBXExternalPropertyType_Reserved10 = 119, + // OBXExternalPropertyType_Reserved11 = 120, + // OBXExternalPropertyType_Reserved12 = 121, + // OBXExternalPropertyType_Reserved13 = 122, + /// The 12-byte ObjectId type in MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (12 bytes) + OBXExternalPropertyType_MongoId = 123, + /// A vector (array) of MongoId values + OBXExternalPropertyType_MongoIdVector = 124, + /// Representing type: Long + /// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer. + OBXExternalPropertyType_MongoTimestamp = 125, + /// Representing type: ByteVector + /// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is the sub-type, + /// followed by the binary data. + OBXExternalPropertyType_MongoBinary = 126, + /// Representing type: string vector with 2 elements (index 0: pattern, index 1: options) + /// Encoding: 1:1 string representation + OBXExternalPropertyType_MongoRegex = 127, +} OBXExternalPropertyType; + struct OBX_model; // doxygen (only) picks up the typedef struct below /// Model represents a database schema and must be provided when opening the store. @@ -657,6 +727,14 @@ OBX_C_API obx_err obx_model_entity(OBX_model* model, const char* name, obx_schem /// @param flags See OBXEntityFlags for values (use bitwise OR to combine multiple flags) OBX_C_API obx_err obx_model_entity_flags(OBX_model* model, uint32_t flags); +/// Set the highest ever known property id in the entity. Should always be equal to or higher than the +/// last property id of the previous version of the entity. +OBX_C_API obx_err obx_model_entity_last_property_id(OBX_model* model, obx_schema_id property_id, obx_uid property_uid); + +/// Refine the definition of the entity declared by the most recent obx_model_entity() call: set the external name. +/// This is an optional name used in an external system, e.g. another database that ObjectBox syncs with. +OBX_C_API obx_err obx_model_entity_external_name(OBX_model* model, const char* external_name); + /// Starts the definition of a new property for the entity type of the last obx_model_entity() call. /// @param name A human readable name for the property. Must be unique within the entity /// @param type The type of property required @@ -682,6 +760,19 @@ OBX_C_API obx_err obx_model_property_relation(OBX_model* model, const char* targ /// @param index_uid Used to identify relations between versions of the model. Must be globally unique. OBX_C_API obx_err obx_model_property_index_id(OBX_model* model, obx_schema_id index_id, obx_uid index_uid); +/// Refine the definition of the property declared by the most recent obx_model_property() call: set the external name. +/// This is an optional name used in an external system, e.g. another database that ObjectBox syncs with. +/// @param index_id Must be unique within this version of the model +/// @param index_uid Used to identify relations between versions of the model. Must be globally unique. +OBX_C_API obx_err obx_model_property_external_name(OBX_model* model, const char* external_name); + +/// Refine the definition of the property declared by the most recent obx_model_property() call: set the external type. +/// This is an optional type used in an external system, e.g. another database that ObjectBox syncs with. +/// Note that the supported mappings from ObjectBox types to external types are limited. +/// @param index_id Must be unique within this version of the model +/// @param index_uid Used to identify relations between versions of the model. Must be globally unique. +OBX_C_API obx_err obx_model_property_external_type(OBX_model* model, OBXExternalPropertyType external_type); + /// Sets the vector dimensionality for the HNSW index of the latest property (must be of a supported vector type). /// This a mandatory option for all HNSW indexes. /// Note 1: vectors with higher dimensions than this value are also indexed (ignoring the higher elements). @@ -702,7 +793,7 @@ OBX_C_API obx_err obx_model_property_index_hnsw_neighbors_per_node(OBX_model* mo /// If indexing time is not a major concern, a value of at least 200 is recommended to improve search quality. OBX_C_API obx_err obx_model_property_index_hnsw_indexing_search_count(OBX_model* model, uint32_t value); -/// Sets flags for the HNSW index of the latest property (). +/// Sets flags for the HNSW index of the latest property. /// For details see OBXHnswFlags and its individual values. /// @param flags See OBXHnswFlags for values (use bitwise OR to combine multiple flags) OBX_C_API obx_err obx_model_property_index_hnsw_flags(OBX_model* model, uint32_t flags); @@ -728,6 +819,15 @@ OBX_C_API obx_err obx_model_property_index_hnsw_vector_cache_hint_size_kb(OBX_mo OBX_C_API obx_err obx_model_relation(OBX_model* model, obx_schema_id relation_id, obx_uid relation_uid, obx_schema_id target_id, obx_uid target_uid); +/// Augments the previously defined relation with a name +OBX_C_API obx_err obx_model_relation_name(OBX_model* model, const char* name); + +/// Augments the previously defined relation with an external name (used outside of ObjectBox) +OBX_C_API obx_err obx_model_relation_external_name(OBX_model* model, const char* external_name); + +/// Augments the previously defined relation with an external type (used outside of ObjectBox) +OBX_C_API obx_err obx_model_relation_external_type(OBX_model* model, OBXExternalPropertyType external_type); + /// Set the highest ever known entity id in the model. Should always be equal to or higher than the /// last entity id of the previous version of the model OBX_C_API void obx_model_last_entity_id(OBX_model*, obx_schema_id entity_id, obx_uid entity_uid); @@ -740,10 +840,6 @@ OBX_C_API void obx_model_last_index_id(OBX_model* model, obx_schema_id index_id, /// last relation id of the previous version of the model. OBX_C_API void obx_model_last_relation_id(OBX_model* model, obx_schema_id relation_id, obx_uid relation_uid); -/// Set the highest ever known property id in the entity. Should always be equal to or higher than the -/// last property id of the previous version of the entity. -OBX_C_API obx_err obx_model_entity_last_property_id(OBX_model* model, obx_schema_id property_id, obx_uid property_uid); - //---------------------------------------------- // Store //---------------------------------------------- diff --git a/objectbox/lib/src/native/bindings/objectbox_c.dart b/objectbox/lib/src/native/bindings/objectbox_c.dart index 702343fda..4a26aa2a5 100644 --- a/objectbox/lib/src/native/bindings/objectbox_c.dart +++ b/objectbox/lib/src/native/bindings/objectbox_c.dart @@ -427,6 +427,47 @@ class ObjectBoxC { late final _model_entity_flags = _model_entity_flagsPtr .asFunction, int)>(); + /// Set the highest ever known property id in the entity. Should always be equal to or higher than the + /// last property id of the previous version of the entity. + int model_entity_last_property_id( + ffi.Pointer model, + int property_id, + int property_uid, + ) { + return _model_entity_last_property_id( + model, + property_id, + property_uid, + ); + } + + late final _model_entity_last_property_idPtr = _lookup< + ffi.NativeFunction< + obx_err Function(ffi.Pointer, obx_schema_id, + obx_uid)>>('obx_model_entity_last_property_id'); + late final _model_entity_last_property_id = _model_entity_last_property_idPtr + .asFunction, int, int)>(); + + /// Refine the definition of the entity declared by the most recent obx_model_entity() call: set the external name. + /// This is an optional name used in an external system, e.g. another database that ObjectBox syncs with. + int model_entity_external_name( + ffi.Pointer model, + ffi.Pointer external_name, + ) { + return _model_entity_external_name( + model, + external_name, + ); + } + + late final _model_entity_external_namePtr = _lookup< + ffi.NativeFunction< + obx_err Function(ffi.Pointer, + ffi.Pointer)>>('obx_model_entity_external_name'); + late final _model_entity_external_name = + _model_entity_external_namePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + /// Starts the definition of a new property for the entity type of the last obx_model_entity() call. /// @param name A human readable name for the property. Must be unique within the entity /// @param type The type of property required @@ -523,6 +564,50 @@ class ObjectBoxC { late final _model_property_index_id = _model_property_index_idPtr .asFunction, int, int)>(); + /// Refine the definition of the property declared by the most recent obx_model_property() call: set the external name. + /// This is an optional name used in an external system, e.g. another database that ObjectBox syncs with. + /// @param index_id Must be unique within this version of the model + /// @param index_uid Used to identify relations between versions of the model. Must be globally unique. + int model_property_external_name( + ffi.Pointer model, + ffi.Pointer external_name, + ) { + return _model_property_external_name( + model, + external_name, + ); + } + + late final _model_property_external_namePtr = _lookup< + ffi.NativeFunction< + obx_err Function(ffi.Pointer, + ffi.Pointer)>>('obx_model_property_external_name'); + late final _model_property_external_name = + _model_property_external_namePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + /// Refine the definition of the property declared by the most recent obx_model_property() call: set the external type. + /// This is an optional type used in an external system, e.g. another database that ObjectBox syncs with. + /// Note that the supported mappings from ObjectBox types to external types are limited. + /// @param index_id Must be unique within this version of the model + /// @param index_uid Used to identify relations between versions of the model. Must be globally unique. + int model_property_external_type( + ffi.Pointer model, + int external_type, + ) { + return _model_property_external_type( + model, + external_type, + ); + } + + late final _model_property_external_typePtr = _lookup< + ffi + .NativeFunction, ffi.Int32)>>( + 'obx_model_property_external_type'); + late final _model_property_external_type = _model_property_external_typePtr + .asFunction, int)>(); + /// Sets the vector dimensionality for the HNSW index of the latest property (must be of a supported vector type). /// This a mandatory option for all HNSW indexes. /// Note 1: vectors with higher dimensions than this value are also indexed (ignoring the higher elements). @@ -591,7 +676,7 @@ class ObjectBoxC { _model_property_index_hnsw_indexing_search_countPtr .asFunction, int)>(); - /// Sets flags for the HNSW index of the latest property (). + /// Sets flags for the HNSW index of the latest property. /// For details see OBXHnswFlags and its individual values. /// @param flags See OBXHnswFlags for values (use bitwise OR to combine multiple flags) int model_property_index_hnsw_flags( @@ -702,6 +787,61 @@ class ObjectBoxC { late final _model_relation = _model_relationPtr .asFunction, int, int, int, int)>(); + /// Augments the previously defined relation with a name + int model_relation_name( + ffi.Pointer model, + ffi.Pointer name, + ) { + return _model_relation_name( + model, + name, + ); + } + + late final _model_relation_namePtr = _lookup< + ffi.NativeFunction< + obx_err Function(ffi.Pointer, + ffi.Pointer)>>('obx_model_relation_name'); + late final _model_relation_name = _model_relation_namePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + /// Augments the previously defined relation with an external name (used outside of ObjectBox) + int model_relation_external_name( + ffi.Pointer model, + ffi.Pointer external_name, + ) { + return _model_relation_external_name( + model, + external_name, + ); + } + + late final _model_relation_external_namePtr = _lookup< + ffi.NativeFunction< + obx_err Function(ffi.Pointer, + ffi.Pointer)>>('obx_model_relation_external_name'); + late final _model_relation_external_name = + _model_relation_external_namePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + /// Augments the previously defined relation with an external type (used outside of ObjectBox) + int model_relation_external_type( + ffi.Pointer model, + int external_type, + ) { + return _model_relation_external_type( + model, + external_type, + ); + } + + late final _model_relation_external_typePtr = _lookup< + ffi + .NativeFunction, ffi.Int32)>>( + 'obx_model_relation_external_type'); + late final _model_relation_external_type = _model_relation_external_typePtr + .asFunction, int)>(); + /// Set the highest ever known entity id in the model. Should always be equal to or higher than the /// last entity id of the previous version of the model void model_last_entity_id( @@ -765,27 +905,6 @@ class ObjectBoxC { late final _model_last_relation_id = _model_last_relation_idPtr .asFunction, int, int)>(); - /// Set the highest ever known property id in the entity. Should always be equal to or higher than the - /// last property id of the previous version of the entity. - int model_entity_last_property_id( - ffi.Pointer model, - int property_id, - int property_uid, - ) { - return _model_entity_last_property_id( - model, - property_id, - property_uid, - ); - } - - late final _model_entity_last_property_idPtr = _lookup< - ffi.NativeFunction< - obx_err Function(ffi.Pointer, obx_schema_id, - obx_uid)>>('obx_model_entity_last_property_id'); - late final _model_entity_last_property_id = _model_entity_last_property_idPtr - .asFunction, int, int)>(); - /// Get the actual bytes from the given OBX_bytes_lazy. /// Because of the potential lazy creation of bytes, the given bytes are not const as it may be resolved internally. /// For the same reason, this function is not thread-safe, at least for the first call on a OBX_bytes_lazy instance. @@ -10510,6 +10629,78 @@ abstract class OBXPropertyFlags { static const int EXPIRATION_TIME = 65536; } +/// A property type of an external system (e.g. another database) that has no default mapping to an ObjectBox type. +/// External property types numeric values start at 100 to avoid overlaps with ObjectBox's PropertyType. +/// (And if we ever support one of these as a primary type, we could share the numeric value?) +abstract class OBXExternalPropertyType { + /// Not a real type: represents uninitialized state and can be used for forward compatibility. + static const int Unknown = 0; + + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation, little endian (16 bytes) + static const int Int128 = 100; + + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + static const int Uuid = 102; + + /// IEEE 754 decimal128 type, e.g. supported by MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + static const int Decimal128 = 103; + + /// A key/value map; e.g. corresponds to a JSON object or a MongoDB document (although not keeping the key order). + /// Unlike the Flex type, this must contain a map value (e.g. not a vector or a scalar). + /// Representing type: Flex + /// Encoding: Flex + static const int FlexMap = 107; + + /// A vector (aka list or array) of flexible elements; e.g. corresponds to a JSON array or a MongoDB array. + /// Unlike the Flex type, this must contain a vector value (e.g. not a map or a scalar). + /// Representing type: Flex + /// Encoding: Flex + static const int FlexVector = 108; + + /// Placeholder (not yet used) for a JSON document. + /// Representing type: String + static const int Json = 109; + + /// Placeholder (not yet used) for a BSON document. + /// Representing type: ByteVector + static const int Bson = 110; + + /// JavaScript source code + /// Representing type: String + static const int JavaScript = 111; + + /// A vector (array) of Int128 values + static const int Int128Vector = 116; + + /// A vector (array) of Int128 values + static const int UuidVector = 118; + + /// The 12-byte ObjectId type in MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (12 bytes) + static const int MongoId = 123; + + /// A vector (array) of MongoId values + static const int MongoIdVector = 124; + + /// Representing type: Long + /// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer. + static const int MongoTimestamp = 125; + + /// Representing type: ByteVector + /// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is the sub-type, + /// followed by the binary data. + static const int MongoBinary = 126; + + /// Representing type: string vector with 2 elements (index 0: pattern, index 1: options) + /// Encoding: 1:1 string representation + static const int MongoRegex = 127; +} + class OBX_model extends ffi.Opaque {} /// Schema entity & property identifiers @@ -11459,7 +11650,7 @@ typedef obx_dart_closer const int OBX_VERSION_MAJOR = 4; -const int OBX_VERSION_MINOR = 1; +const int OBX_VERSION_MINOR = 2; const int OBX_VERSION_PATCH = 0; diff --git a/tool/update-c-binding.sh b/tool/update-c-binding.sh index c8b3a2bdf..87789a244 100755 --- a/tool/update-c-binding.sh +++ b/tool/update-c-binding.sh @@ -5,7 +5,7 @@ # copies the header files, makes some required modifications # and runs the ffigen binding generator on them. -cLibVersion=4.1.0 +cLibVersion=4.2.0 echo "Downloading C library source files from GitHub..." # Note: the release archives do not contain objectbox-dart.h, so get the full sources. From 930ae01d50b11c8e7b75003e0b1de1d3275ca0c2 Mon Sep 17 00:00:00 2001 From: Shubham Date: Wed, 19 Mar 2025 08:06:19 +0100 Subject: [PATCH 04/12] external types: add @ExternalType annotation #139 --- generator/lib/src/code_builder.dart | 2 + generator/lib/src/code_chunks.dart | 8 ++ generator/lib/src/entity_resolver.dart | 18 +++ generator/test/code_builder_test.dart | 32 +++++ objectbox/lib/src/annotations.dart | 106 +++++++++++++++++ objectbox/lib/src/modelinfo/enums.dart | 109 ++++++++++++++++++ .../lib/src/modelinfo/modelproperty.dart | 16 ++- objectbox/lib/src/native/model.dart | 10 ++ objectbox_test/test/box_test.dart | 10 ++ objectbox_test/test/entity.dart | 5 + objectbox_test/test/objectbox-model.json | 7 +- 11 files changed, 320 insertions(+), 3 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index f735df13e..0213facce 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -213,6 +213,8 @@ class CodeBuilder extends Builder { propInModel.dartFieldType = prop.dartFieldType; propInModel.relationTarget = prop.relationTarget; propInModel.hnswParams = prop.hnswParams; + propInModel.externalPropertyType = prop.externalPropertyType; + propInModel.externalPropertyName = prop.externalPropertyName; if (!prop.hasIndexFlag()) { propInModel.removeIndex(); diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 336b4bdf5..4f2747217 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -154,6 +154,14 @@ class CodeChunks { additionalArgs += ", hnswParams: ${property.hnswParams!.toCodeString(obxInt)}"; } + if (property.externalPropertyType != null) { + additionalArgs += + ", externalPropertyType: ${property.externalPropertyType!}"; + } + if (property.externalPropertyName != null) { + additionalArgs += + ", externalPropertyName: '${property.externalPropertyName!}'"; + } return ''' $obxInt.ModelProperty( id: ${createIdUid(property.id)}, diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index eb688718f..af354becc 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -28,6 +28,7 @@ class EntityResolver extends Builder { final _indexChecker = const TypeChecker.fromRuntime(Index); final _backlinkChecker = const TypeChecker.fromRuntime(Backlink); final _hnswChecker = const TypeChecker.fromRuntime(HnswIndex); + final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalProperty); @override FutureOr build(BuildStep buildStep) async { @@ -226,6 +227,10 @@ class EntityResolver extends Builder { _readHnswIndexParams(annotation, prop); }); + _externalTypeChecker.runIfMatches(annotated, (annotation) { + _readExternalTypeParams(annotation, prop); + }); + // for code generation prop.dartFieldType = f.type.element!.name! + (isNullable(f.type) ? '?' : ''); @@ -555,6 +560,19 @@ class EntityResolver extends Builder { annotation.getField('vectorCacheHintSizeKB')!.toIntValue()); property.hnswParams = ModelHnswParams.fromAnnotation(hnswRestored); } + + void _readExternalTypeParams(DartObject annotation, ModelProperty property) { + final typeIndex = + _enumValueIndex(annotation.getField('type')!, "ExternalProperty.type"); + final type = typeIndex != null ? ExternalType.values[typeIndex] : null; + if (type == null) { + throw InvalidGenerationSourceError( + "'type' attribute not specified in @ExternalProperty annotation"); + } + final name = annotation.getField('name')!.toStringValue(); + property.externalPropertyType = externalTypeToOBXExternalType(type); + property.externalPropertyName = name; + } } extension _TypeCheckerExtensions on TypeChecker { diff --git a/generator/test/code_builder_test.dart b/generator/test/code_builder_test.dart index 8bfd7b0e6..05d67482d 100644 --- a/generator/test/code_builder_test.dart +++ b/generator/test/code_builder_test.dart @@ -378,6 +378,38 @@ void main() { expect(idProperty.flags & OBXPropertyFlags.ID_SELF_ASSIGNABLE != 0, true); }); }); + + test('ExternalType annotation', () async { + final source = r''' + library example; + import 'package:objectbox/objectbox.dart'; + + @Entity() + class Example { + @Id() + int id = 0; + + @Property(type: PropertyType.byteVector) + @ExternalProperty(type: ExternalType.bson, name: 'my-mongo-bson') + List? mongoBson; + + @ExternalProperty(type: ExternalType.javaScript, name: 'my-mongo-js') + String? mongoJS; + } + '''; + + final testEnv = GeneratorTestEnv(); + await testEnv.run(source); + + final property1 = testEnv.model.entities[0].properties + .firstWhere((element) => element.name == "mongoBson"); + expect(property1.externalPropertyType, OBXExternalPropertyType.Bson); + expect(property1.externalPropertyName, "my-mongo-bson"); + final property2 = testEnv.model.entities[0].properties + .firstWhere((element) => element.name == "mongoJS"); + expect(property2.externalPropertyType, OBXExternalPropertyType.JavaScript); + expect(property2.externalPropertyName, "my-mongo-js"); + }); } Future _unsupported() { diff --git a/objectbox/lib/src/annotations.dart b/objectbox/lib/src/annotations.dart index 44c20582d..442ab53ed 100644 --- a/objectbox/lib/src/annotations.dart +++ b/objectbox/lib/src/annotations.dart @@ -487,3 +487,109 @@ class HnswIndex { this.reparationBacklinkProbability, this.vectorCacheHintSizeKB}); } + +/// A property type of an external system (e.g. another database) that has no +/// default mapping to an ObjectBox type. +/// +/// Use with [ExternalProperty]. +enum ExternalType { + /// Representing type: ByteVector + /// + /// Encoding: 1:1 binary representation, little endian (16 bytes) + int128, + + /// Representing type: ByteVector + /// + /// Encoding: 1:1 binary representation (16 bytes) + uuid, + + /// IEEE 754 decimal128 type, e.g. supported by MongoDB. + /// + /// Representing type: ByteVector + /// + /// Encoding: 1:1 binary representation (16 bytes) + decimal128, + + /// A key/value map; e.g. corresponds to a JSON object or a MongoDB document + /// (although not keeping the key order). + /// + /// Unlike the Flex type, this must contain a map value (e.g. not a vector or + /// a scalar). + /// + /// Representing type: Flex + /// + /// Encoding: Flex + flexMap, + + /// A vector (aka list or array) of flexible elements; e.g. corresponds to a + /// JSON array or a MongoDB array. + /// + /// Unlike the Flex type, this must contain a vector value (e.g. not a map or + /// a scalar). + /// + /// Representing type: Flex + /// + /// Encoding: Flex + flexVector, + + /// Placeholder (not yet used) for a JSON document. + /// + /// Representing type: String + json, + + /// Placeholder (not yet used) for a BSON document. + /// + /// Representing type: ByteVector + bson, + + /// JavaScript source code. + /// + /// Representing type: String + javaScript, + + /// A vector (array) of Int128 values. + int128Vector, + + /// A vector (array) of Int128 values + uuidVector, + + /// The 12-byte ObjectId type in MongoDB. + /// + /// Representing type: ByteVector + /// + /// Encoding: 1:1 binary representation (12 bytes) + mongoId, + + /// A vector (array) of MongoId values. + mongoIdVector, + + /// Representing type: Long + /// + /// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer. + mongoTimestamp, + + /// Representing type: ByteVector + /// + /// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is + /// the sub-type, followed by the binary data. + mongoBinary, + + /// Representing type: string vector with 2 elements (index 0: pattern, + /// index 1: options) + /// + /// Encoding: 1:1 string representation + mongoRegex +} + +/// Annotates a property to be stored in an external system. +class ExternalProperty { + /// The name assigned to the property in the external system. + final String? name; + + /// The type of the property in the external system. + /// See [ExternalType] for possible values. + final ExternalType type; + + /// Create an ExternalProperty annotation. + const ExternalProperty({required this.type, this.name}); +} diff --git a/objectbox/lib/src/modelinfo/enums.dart b/objectbox/lib/src/modelinfo/enums.dart index 2ebb7067d..8f1712280 100644 --- a/objectbox/lib/src/modelinfo/enums.dart +++ b/objectbox/lib/src/modelinfo/enums.dart @@ -239,3 +239,112 @@ abstract class OBXPropertyType { /// < Variable sized vector of Date values (high precision 64-bit timestamp). static const int DateNanoVector = 32; } + +int externalTypeToOBXExternalType(ExternalType type) { + switch (type) { + case ExternalType.int128: + return OBXExternalPropertyType.Int128; + case ExternalType.uuid: + return OBXExternalPropertyType.Uuid; + case ExternalType.decimal128: + return OBXExternalPropertyType.Decimal128; + case ExternalType.flexMap: + return OBXExternalPropertyType.FlexMap; + case ExternalType.flexVector: + return OBXExternalPropertyType.FlexVector; + case ExternalType.json: + return OBXExternalPropertyType.Json; + case ExternalType.bson: + return OBXExternalPropertyType.Bson; + case ExternalType.javaScript: + return OBXExternalPropertyType.JavaScript; + case ExternalType.int128Vector: + return OBXExternalPropertyType.Int128Vector; + case ExternalType.uuidVector: + return OBXExternalPropertyType.UuidVector; + case ExternalType.mongoId: + return OBXExternalPropertyType.MongoId; + case ExternalType.mongoIdVector: + return OBXExternalPropertyType.MongoIdVector; + case ExternalType.mongoTimestamp: + return OBXExternalPropertyType.MongoTimestamp; + case ExternalType.mongoBinary: + return OBXExternalPropertyType.MongoBinary; + case ExternalType.mongoRegex: + return OBXExternalPropertyType.MongoRegex; + default: + throw ArgumentError.value(type, 'type', 'Invalid ExternalType'); + } +} + +/// A property type of an external system (e.g. another database) that has no default mapping to an ObjectBox type. +/// External property types numeric values start at 100 to avoid overlaps with ObjectBox's PropertyType. +/// (And if we ever support one of these as a primary type, we could share the numeric value?) +abstract class OBXExternalPropertyType { + /// Not a real type: represents uninitialized state and can be used for forward compatibility. + static const int Unknown = 0; + + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation, little endian (16 bytes) + static const int Int128 = 100; + + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + static const int Uuid = 102; + + /// IEEE 754 decimal128 type, e.g. supported by MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (16 bytes) + static const int Decimal128 = 103; + + /// A key/value map; e.g. corresponds to a JSON object or a MongoDB document (although not keeping the key order). + /// Unlike the Flex type, this must contain a map value (e.g. not a vector or a scalar). + /// Representing type: Flex + /// Encoding: Flex + static const int FlexMap = 107; + + /// A vector (aka list or array) of flexible elements; e.g. corresponds to a JSON array or a MongoDB array. + /// Unlike the Flex type, this must contain a vector value (e.g. not a map or a scalar). + /// Representing type: Flex + /// Encoding: Flex + static const int FlexVector = 108; + + /// Placeholder (not yet used) for a JSON document. + /// Representing type: String + static const int Json = 109; + + /// Placeholder (not yet used) for a BSON document. + /// Representing type: ByteVector + static const int Bson = 110; + + /// JavaScript source code + /// Representing type: String + static const int JavaScript = 111; + + /// A vector (array) of Int128 values + static const int Int128Vector = 116; + + /// A vector (array) of Int128 values + static const int UuidVector = 118; + + /// The 12-byte ObjectId type in MongoDB + /// Representing type: ByteVector + /// Encoding: 1:1 binary representation (12 bytes) + static const int MongoId = 123; + + /// A vector (array) of MongoId values + static const int MongoIdVector = 124; + + /// Representing type: Long + /// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer. + static const int MongoTimestamp = 125; + + /// Representing type: ByteVector + /// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is the sub-type, + /// followed by the binary data. + static const int MongoBinary = 126; + + /// Representing type: string vector with 2 elements (index 0: pattern, index 1: options) + /// Encoding: 1:1 string representation + static const int MongoRegex = 127; +} diff --git a/objectbox/lib/src/modelinfo/modelproperty.dart b/objectbox/lib/src/modelinfo/modelproperty.dart index 2ff57c6cc..1e8658ede 100644 --- a/objectbox/lib/src/modelinfo/modelproperty.dart +++ b/objectbox/lib/src/modelinfo/modelproperty.dart @@ -16,6 +16,8 @@ class ModelProperty { ModelEntity? entity; String? relationTarget; ModelHnswParams? hnswParams; + int? externalPropertyType; + String? externalPropertyName; /// Type used in the source dart code - used by the code generator. /// Starts with [_fieldReadOnlyPrefix] if the field (currently IDs only) is @@ -114,7 +116,9 @@ class ModelProperty { required int flags, IdUid? indexId, this.relationTarget, - this.hnswParams}) + this.hnswParams, + this.externalPropertyName, + this.externalPropertyType}) : _name = name, _type = type, _flags = flags, @@ -127,7 +131,9 @@ class ModelProperty { _dartFieldType = data['dartFieldType'] as String?, uidRequest = data['uidRequest'] as bool? ?? false, hnswParams = ModelHnswParams.fromMap( - data['hnswParams'] as Map?) { + data['hnswParams'] as Map?), + externalPropertyType = data['externalType'] as int?, + externalPropertyName = data['externalName'] as String? { name = data['name'] as String?; type = data['type'] as int?; flags = data['flags'] as int? ?? 0; @@ -151,6 +157,12 @@ class ModelProperty { if (hnswParams != null) { ret['hnswParams'] = hnswParams!.toMap(); } + if (externalPropertyType != null) { + ret['externalType'] = externalPropertyType; + } + if (externalPropertyName != null) { + ret['externalName'] = externalPropertyName; + } } return ret; } diff --git a/objectbox/lib/src/native/model.dart b/objectbox/lib/src/native/model.dart index e6071a28a..df2e2789c 100644 --- a/objectbox/lib/src/native/model.dart +++ b/objectbox/lib/src/native/model.dart @@ -115,6 +115,16 @@ class Model { _cModel, hnswParams.vectorCacheHintSizeKB!)); } } + + final externalPropertyType = prop.externalPropertyType; + if (externalPropertyType != null) { + _check(C.model_property_external_type(_cModel, externalPropertyType)); + } + final externalPropertyName = prop.externalPropertyName; + if (externalPropertyName != null) { + _check(C.model_property_external_name( + _cModel, externalPropertyName.toNativeUtf8().cast())); + } } void addRelation(ModelRelation rel) { diff --git a/objectbox_test/test/box_test.dart b/objectbox_test/test/box_test.dart index f56b41ad7..30e7988d2 100644 --- a/objectbox_test/test/box_test.dart +++ b/objectbox_test/test/box_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: deprecated_member_use import 'dart:io'; +import 'dart:typed_data'; import 'package:objectbox/objectbox.dart'; import 'package:test/test.dart'; @@ -1031,6 +1032,15 @@ void main() { expect(items[2].tDateNano, isNull); }); + test('external-types', () { + final object = TestEntity(); + object.tMongoId = Uint8List.fromList([2, 14, 5]); + box.put(object); + + final items = box.getAll(); + expect(items[0].tMongoId, [2, 14, 5]); + }); + test('large-data', () { final numBytes = 1024 * 1024; final str = List.generate(numBytes, (i) => 'A').join(); diff --git a/objectbox_test/test/entity.dart b/objectbox_test/test/entity.dart index 5712476d6..7f07905df 100644 --- a/objectbox_test/test/entity.dart +++ b/objectbox_test/test/entity.dart @@ -74,6 +74,10 @@ class TestEntity { // OBXPropertyType.ByteVector Uint8List? tUint8List; + @Property(type: PropertyType.byteVector) + @ExternalProperty(type: ExternalType.mongoId, name: 'tMongoId') + Uint8List? tMongoId; + TestEntity( {this.tString, this.tLong, @@ -108,6 +112,7 @@ class TestEntity { tByteList = [1, 2, 3]; tInt8List = Int8List.fromList([-4, 5, 6]); tUint8List = Uint8List.fromList([7, 8, 9]); + tMongoId = Uint8List.fromList([-3, 4, 5]); } TestEntity.ignoredExcept(this.tInt) : tString = '' { diff --git a/objectbox_test/test/objectbox-model.json b/objectbox_test/test/objectbox-model.json index 99635f567..b3928e2e2 100644 --- a/objectbox_test/test/objectbox-model.json +++ b/objectbox_test/test/objectbox-model.json @@ -5,7 +5,7 @@ "entities": [ { "id": "1:4630700155272683157", - "lastPropertyId": "35:1724663621433823504", + "lastPropertyId": "36:5399124829543441597", "name": "TestEntity", "properties": [ { @@ -196,6 +196,11 @@ "type": 6, "flags": 32808, "indexId": "20:4846837430056399798" + }, + { + "id": "36:5399124829543441597", + "name": "tMongoId", + "type": 23 } ], "relations": [ From 4af4d686fd20eac31869b7e18b77e01a2a619a14 Mon Sep 17 00:00:00 2001 From: Shubham Date: Wed, 19 Mar 2025 18:35:47 +0530 Subject: [PATCH 05/12] external types: create two separate annotations, @ExternalType and @ExternalName --- generator/lib/src/code_builder.dart | 12 ++- generator/lib/src/code_chunks.dart | 8 ++ generator/lib/src/entity_resolver.dart | 63 +++++++++++-- generator/test/code_builder_test.dart | 91 +++++++++++++++---- objectbox/lib/src/annotations.dart | 37 +++++--- objectbox/lib/src/modelinfo/enums.dart | 32 +++---- .../lib/src/modelinfo/modelrelation.dart | 29 +++++- objectbox/lib/src/native/model.dart | 23 ++++- objectbox_test/test/box_test.dart | 19 ++-- objectbox_test/test/entity.dart | 21 ++++- objectbox_test/test/objectbox-model.json | 33 +++++-- 11 files changed, 283 insertions(+), 85 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 0213facce..264c99c1d 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -1,21 +1,21 @@ import 'dart:async'; -import 'dart:io'; import 'dart:convert'; +import 'dart:io'; import 'package:build/build.dart'; import 'package:collection/collection.dart'; +import 'package:dart_style/dart_style.dart'; import 'package:glob/glob.dart'; +import 'package:objectbox/internal.dart'; import 'package:objectbox_generator/src/analysis/analysis.dart'; import 'package:objectbox_generator/src/builder_dirs.dart'; import 'package:path/path.dart' as path; -import 'package:objectbox/internal.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:source_gen/source_gen.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:source_gen/source_gen.dart'; +import 'code_chunks.dart'; import 'config.dart'; import 'entity_resolver.dart'; -import 'code_chunks.dart'; /// CodeBuilder collects all '.objectbox.info' files created by EntityResolver and generates objectbox-model.json and /// objectbox_model.dart @@ -244,6 +244,8 @@ class CodeBuilder extends Builder { relInModel.name = rel.name; relInModel.targetName = rel.targetName; + relInModel.externalPropertyType = rel.externalPropertyType; + relInModel.externalPropertyName = rel.externalPropertyName; } IdUid mergeEntity(ModelInfo modelInfo, ModelEntity entity) { diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 4f2747217..19da063ad 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -174,11 +174,19 @@ class CodeChunks { } static String createModelRelation(ModelRelation relation) { + var additionalArgs = ''; + if (relation.externalPropertyType != null) { + additionalArgs += ", externalType: ${relation.externalPropertyType!}"; + } + if (relation.externalPropertyName != null) { + additionalArgs += ", externalName: '${relation.externalPropertyName!}'"; + } return ''' $obxInt.ModelRelation( id: ${createIdUid(relation.id)}, name: '${relation.name}', targetId: ${createIdUid(relation.targetId)} + $additionalArgs ) '''; } diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index af354becc..d964079ba 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -28,7 +28,8 @@ class EntityResolver extends Builder { final _indexChecker = const TypeChecker.fromRuntime(Index); final _backlinkChecker = const TypeChecker.fromRuntime(Backlink); final _hnswChecker = const TypeChecker.fromRuntime(HnswIndex); - final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalProperty); + final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalType); + final _externalNameChecker = const TypeChecker.fromRuntime(ExternalName); @override FutureOr build(BuildStep buildStep) async { @@ -172,6 +173,7 @@ class EntityResolver extends Builder { final backlinkAnnotations = _backlinkChecker.annotationsOfExact(annotated); if (backlinkAnnotations.isNotEmpty) { + // Handles ToMany based on other ToOne or ToMany relation (backlink) if (!isToManyRel) { log.severe( " Skipping property '${f.name}': @Backlink() may only be used with ToMany."); @@ -184,14 +186,37 @@ class EntityResolver extends Builder { entity.backlinks.add(backlink); log.info(' $backlink'); } else if (isToManyRel) { + // Handles standalone (non backlink) ToMany relation + + // @ExternalType + int? externalPropertyType; + _externalTypeChecker.runIfMatches(annotated, (annotation) { + final externalTypeId = _readExternalTypeParams(annotation); + externalPropertyType = externalTypeId; + }); + + // @ExternalName + String? externalPropertyName; + _externalNameChecker.runIfMatches(annotated, (annotation) { + if (externalPropertyType == null) { + throw InvalidGenerationSourceError( + "@ExternalName annotation requires @ExternalType annotation to be present"); + } + externalPropertyName = _readExternalNameParams(annotation); + }); + // create relation final rel = ModelRelation.create(IdUid(0, propUid ?? 0), f.name, targetName: relTargetName, - uidRequest: propUid != null && propUid == 0); + uidRequest: propUid != null && propUid == 0, + externalPropertyName: externalPropertyName, + externalPropertyType: externalPropertyType); + entity.relations.add(rel); log.info(' $rel'); } else { + // Handles regular properties // create property (do not use readEntity.createProperty in order to avoid generating new ids) final prop = ModelProperty.create( IdUid(0, propUid ?? 0), f.name, fieldType, @@ -227,8 +252,20 @@ class EntityResolver extends Builder { _readHnswIndexParams(annotation, prop); }); + // @ExternalType _externalTypeChecker.runIfMatches(annotated, (annotation) { - _readExternalTypeParams(annotation, prop); + final externalTypeId = _readExternalTypeParams(annotation); + prop.externalPropertyType = externalTypeId; + }); + + // @ExternalName + _externalNameChecker.runIfMatches(annotated, (annotation) { + if (prop.externalPropertyType == null) { + throw InvalidGenerationSourceError( + "@ExternalName annotation requires @ExternalType annotation to be present"); + } + final externalName = _readExternalNameParams(annotation); + prop.externalPropertyName = externalName; }); // for code generation @@ -561,17 +598,25 @@ class EntityResolver extends Builder { property.hnswParams = ModelHnswParams.fromAnnotation(hnswRestored); } - void _readExternalTypeParams(DartObject annotation, ModelProperty property) { + int _readExternalTypeParams(DartObject annotation) { final typeIndex = - _enumValueIndex(annotation.getField('type')!, "ExternalProperty.type"); - final type = typeIndex != null ? ExternalType.values[typeIndex] : null; + _enumValueIndex(annotation.getField('type')!, "ExternalType.type"); + final type = + typeIndex != null ? ExternalPropertyType.values[typeIndex] : null; if (type == null) { throw InvalidGenerationSourceError( - "'type' attribute not specified in @ExternalProperty annotation"); + "'type' attribute not specified in @ExternalType annotation"); } + return externalTypeToOBXExternalType(type); + } + + String _readExternalNameParams(DartObject annotation) { final name = annotation.getField('name')!.toStringValue(); - property.externalPropertyType = externalTypeToOBXExternalType(type); - property.externalPropertyName = name; + if (name == null) { + throw InvalidGenerationSourceError( + "'name' attribute not specified in @ExternalName annotation"); + } + return name; } } diff --git a/generator/test/code_builder_test.dart b/generator/test/code_builder_test.dart index 05d67482d..55a1a4a60 100644 --- a/generator/test/code_builder_test.dart +++ b/generator/test/code_builder_test.dart @@ -379,8 +379,9 @@ void main() { }); }); - test('ExternalType annotation', () async { - final source = r''' + group("ExternalType and ExternalName annotations", () { + test('annotations work on properties', () async { + final source = r''' library example; import 'package:objectbox/objectbox.dart'; @@ -390,25 +391,83 @@ void main() { int id = 0; @Property(type: PropertyType.byteVector) - @ExternalProperty(type: ExternalType.bson, name: 'my-mongo-bson') - List? mongoBson; + @ExternalType(type: ExternalPropertyType.mongoId) + List? mongoId; + + @ExternalType(type: ExternalPropertyType.uuid) + @ExternalName(name: 'my-mongo-uuid') + List? mongoUuid; + } + '''; + + final testEnv = GeneratorTestEnv(); + await testEnv.run(source); + + final property1 = testEnv.model.entities[0].properties + .firstWhere((element) => element.name == "mongoId"); + expect(property1.externalPropertyType, OBXExternalPropertyType.MongoId); + + final property2 = testEnv.model.entities[0].properties + .firstWhere((element) => element.name == "mongoUuid"); + expect(property2.externalPropertyType, OBXExternalPropertyType.Uuid); + expect(property2.externalPropertyName, "my-mongo-uuid"); + }); + + test('annotations work on ToMany (standalone) relations', () async { + final source = r''' + library example; + import 'package:objectbox/objectbox.dart'; + + @Entity() + class Student{ + int id; + + @ExternalType(type: ExternalPropertyType.mongoId) + final rel1 = ToMany(); + + @ExternalType(type: ExternalPropertyType.uuid) + @ExternalName(name: 'my-courses-rel') + final rel2 = ToMany(); + } + '''; + + final testEnv = GeneratorTestEnv(); + await testEnv.run(source); + + final relation1 = testEnv.model.entities[0].relations + .firstWhere((element) => element.name == "rel1"); + expect(relation1.externalPropertyType, OBXExternalPropertyType.MongoId); + + final relation2 = testEnv.model.entities[0].relations + .firstWhere((element) => element.name == "rel2"); + expect(relation2.externalPropertyType, OBXExternalPropertyType.Uuid); + expect(relation2.externalPropertyName, "my-courses-rel"); + }); + }); + + test('Only ExternalName annotation fails', () async { + final source = r''' + library example; + import 'package:objectbox/objectbox.dart'; + + @Entity() + class Example { + @Id() + int id = 0; - @ExternalProperty(type: ExternalType.javaScript, name: 'my-mongo-js') - String? mongoJS; + @ExternalName(name: 'my-mongo-uuid') + List? mongoUuid; } '''; final testEnv = GeneratorTestEnv(); - await testEnv.run(source); - - final property1 = testEnv.model.entities[0].properties - .firstWhere((element) => element.name == "mongoBson"); - expect(property1.externalPropertyType, OBXExternalPropertyType.Bson); - expect(property1.externalPropertyName, "my-mongo-bson"); - final property2 = testEnv.model.entities[0].properties - .firstWhere((element) => element.name == "mongoJS"); - expect(property2.externalPropertyType, OBXExternalPropertyType.JavaScript); - expect(property2.externalPropertyName, "my-mongo-js"); + await expectLater( + () async => await testEnv.run(source), + throwsA(isA().having( + (e) => e.message, + 'message', + contains( + "@ExternalName annotation requires @ExternalType annotation to be present")))); }); } diff --git a/objectbox/lib/src/annotations.dart b/objectbox/lib/src/annotations.dart index 442ab53ed..690e58105 100644 --- a/objectbox/lib/src/annotations.dart +++ b/objectbox/lib/src/annotations.dart @@ -491,8 +491,8 @@ class HnswIndex { /// A property type of an external system (e.g. another database) that has no /// default mapping to an ObjectBox type. /// -/// Use with [ExternalProperty]. -enum ExternalType { +/// Use with [ExternalType]. +enum ExternalPropertyType { /// Representing type: ByteVector /// /// Encoding: 1:1 binary representation, little endian (16 bytes) @@ -581,15 +581,30 @@ enum ExternalType { mongoRegex } -/// Annotates a property to be stored in an external system. -class ExternalProperty { - /// The name assigned to the property in the external system. - final String? name; - +/// See the constructor documentation. +class ExternalType { /// The type of the property in the external system. - /// See [ExternalType] for possible values. - final ExternalType type; + /// + /// See [ExternalPropertyType] for possible values. + final ExternalPropertyType type; + + /// Sets the type of a property or the type of object IDs of a [ToMany] in an + /// external system (like another database). + /// + /// This is useful if there is no default mapping of the ObjectBox type to the + /// type in the external system. + /// + /// Carefully look at the documentation of the external type to ensure it is + /// compatible with the ObjectBox type. + const ExternalType({required this.type}); +} + +/// See the constructor documentation. +class ExternalName { + /// The name assigned to the property in the external system. + final String name; - /// Create an ExternalProperty annotation. - const ExternalProperty({required this.type, this.name}); + /// Sets the name of an @Entity, a property or a [ToMany] in an external + /// system (like another database). + const ExternalName({required this.name}); } diff --git a/objectbox/lib/src/modelinfo/enums.dart b/objectbox/lib/src/modelinfo/enums.dart index 8f1712280..d422dcebb 100644 --- a/objectbox/lib/src/modelinfo/enums.dart +++ b/objectbox/lib/src/modelinfo/enums.dart @@ -240,37 +240,37 @@ abstract class OBXPropertyType { static const int DateNanoVector = 32; } -int externalTypeToOBXExternalType(ExternalType type) { +int externalTypeToOBXExternalType(ExternalPropertyType type) { switch (type) { - case ExternalType.int128: + case ExternalPropertyType.int128: return OBXExternalPropertyType.Int128; - case ExternalType.uuid: + case ExternalPropertyType.uuid: return OBXExternalPropertyType.Uuid; - case ExternalType.decimal128: + case ExternalPropertyType.decimal128: return OBXExternalPropertyType.Decimal128; - case ExternalType.flexMap: + case ExternalPropertyType.flexMap: return OBXExternalPropertyType.FlexMap; - case ExternalType.flexVector: + case ExternalPropertyType.flexVector: return OBXExternalPropertyType.FlexVector; - case ExternalType.json: + case ExternalPropertyType.json: return OBXExternalPropertyType.Json; - case ExternalType.bson: + case ExternalPropertyType.bson: return OBXExternalPropertyType.Bson; - case ExternalType.javaScript: + case ExternalPropertyType.javaScript: return OBXExternalPropertyType.JavaScript; - case ExternalType.int128Vector: + case ExternalPropertyType.int128Vector: return OBXExternalPropertyType.Int128Vector; - case ExternalType.uuidVector: + case ExternalPropertyType.uuidVector: return OBXExternalPropertyType.UuidVector; - case ExternalType.mongoId: + case ExternalPropertyType.mongoId: return OBXExternalPropertyType.MongoId; - case ExternalType.mongoIdVector: + case ExternalPropertyType.mongoIdVector: return OBXExternalPropertyType.MongoIdVector; - case ExternalType.mongoTimestamp: + case ExternalPropertyType.mongoTimestamp: return OBXExternalPropertyType.MongoTimestamp; - case ExternalType.mongoBinary: + case ExternalPropertyType.mongoBinary: return OBXExternalPropertyType.MongoBinary; - case ExternalType.mongoRegex: + case ExternalPropertyType.mongoRegex: return OBXExternalPropertyType.MongoRegex; default: throw ArgumentError.value(type, 'type', 'Invalid ExternalType'); diff --git a/objectbox/lib/src/modelinfo/modelrelation.dart b/objectbox/lib/src/modelinfo/modelrelation.dart index f8c275004..f5d64ca46 100644 --- a/objectbox/lib/src/modelinfo/modelrelation.dart +++ b/objectbox/lib/src/modelinfo/modelrelation.dart @@ -12,6 +12,9 @@ class ModelRelation { String? _targetName; + int? externalPropertyType; + String? externalPropertyName; + // whether the user requested UID information (started a rename process) final bool uidRequest; @@ -44,15 +47,29 @@ class ModelRelation { // used in code generator ModelRelation.create(this.id, String? name, - {String? targetId, String? targetName, this.uidRequest = false}) { + {String? targetId, + String? targetName, + int? externalPropertyType, + String? externalPropertyName, + this.uidRequest = false}) { this.name = name; if (targetId != null) this.targetId = IdUid.fromString(targetId); if (targetName != null) this.targetName = targetName; + if (externalPropertyType != null) { + this.externalPropertyType = externalPropertyType; + } + if (externalPropertyName != null) { + this.externalPropertyName = externalPropertyName; + } } // used in generated code ModelRelation( - {required this.id, required String name, required IdUid targetId}) + {required this.id, + required String name, + required IdUid targetId, + this.externalName, + this.externalType}) : _name = name, _targetId = targetId, uidRequest = false; @@ -62,13 +79,19 @@ class ModelRelation { IdUid.fromString(data['id'] as String?), data['name'] as String?, targetId: data['targetId'] as String?, targetName: data['targetName'] as String?, - uidRequest: data['uidRequest'] as bool? ?? false); + uidRequest: data['uidRequest'] as bool? ?? false, + externalPropertyType: data['externalType'] as int?, + externalPropertyName: data['externalName'] as String?); Map toMap({bool forModelJson = false}) { final ret = {}; ret['id'] = id.toString(); ret['name'] = name; if (_targetId != null) ret['targetId'] = _targetId.toString(); + if (externalPropertyType != null) + ret['externalType'] = externalPropertyType; + if (externalPropertyName != null) + ret['externalName'] = externalPropertyName; if (!forModelJson) { ret['targetName'] = _targetName; ret['uidRequest'] = uidRequest; diff --git a/objectbox/lib/src/native/model.dart b/objectbox/lib/src/native/model.dart index df2e2789c..bea32f47c 100644 --- a/objectbox/lib/src/native/model.dart +++ b/objectbox/lib/src/native/model.dart @@ -120,15 +120,32 @@ class Model { if (externalPropertyType != null) { _check(C.model_property_external_type(_cModel, externalPropertyType)); } - final externalPropertyName = prop.externalPropertyName; + final externalPropertyName = prop.externalPropertyName?.toNativeUtf8(); if (externalPropertyName != null) { - _check(C.model_property_external_name( - _cModel, externalPropertyName.toNativeUtf8().cast())); + try { + _check(C.model_property_external_name( + _cModel, externalPropertyName.cast())); + } finally { + calloc.free(externalPropertyName); + } } } void addRelation(ModelRelation rel) { _check(C.model_relation( _cModel, rel.id.id, rel.id.uid, rel.targetId.id, rel.targetId.uid)); + if (rel.externalPropertyType != null) { + _check( + C.model_relation_external_type(_cModel, rel.externalPropertyType!)); + if (rel.externalPropertyName != null) { + final externalPropertyName = rel.externalPropertyName!.toNativeUtf8(); + try { + _check(C.model_relation_external_name( + _cModel, externalPropertyName.cast())); + } finally { + calloc.free(externalPropertyName); + } + } + } } } diff --git a/objectbox_test/test/box_test.dart b/objectbox_test/test/box_test.dart index 30e7988d2..fa62a5818 100644 --- a/objectbox_test/test/box_test.dart +++ b/objectbox_test/test/box_test.dart @@ -1,7 +1,6 @@ // ignore_for_file: deprecated_member_use import 'dart:io'; -import 'dart:typed_data'; import 'package:objectbox/objectbox.dart'; import 'package:test/test.dart'; @@ -725,6 +724,15 @@ void main() { expect(item2.tFloat64List, [-2000.00001, 2000.00001]); }); + test('@ExternalType and @ExternalName annotations work correctly', () { + final box = store.box(); + final id = box.put(EntityWithExternalType([90, 100, 110], [1, 2, 3])); + final item = box.get(id)!; + expect(item.id, id); + expect(item.mongoId, [90, 100, 110]); + expect(item.mongoUuid, [1, 2, 3]); + }); + test('.count() works', () { expect(box.count(), equals(0)); box.putMany(simpleItems()); @@ -1032,15 +1040,6 @@ void main() { expect(items[2].tDateNano, isNull); }); - test('external-types', () { - final object = TestEntity(); - object.tMongoId = Uint8List.fromList([2, 14, 5]); - box.put(object); - - final items = box.getAll(); - expect(items[0].tMongoId, [2, 14, 5]); - }); - test('large-data', () { final numBytes = 1024 * 1024; final str = List.generate(numBytes, (i) => 'A').join(); diff --git a/objectbox_test/test/entity.dart b/objectbox_test/test/entity.dart index 7f07905df..0c27e7e14 100644 --- a/objectbox_test/test/entity.dart +++ b/objectbox_test/test/entity.dart @@ -74,10 +74,6 @@ class TestEntity { // OBXPropertyType.ByteVector Uint8List? tUint8List; - @Property(type: PropertyType.byteVector) - @ExternalProperty(type: ExternalType.mongoId, name: 'tMongoId') - Uint8List? tMongoId; - TestEntity( {this.tString, this.tLong, @@ -112,7 +108,6 @@ class TestEntity { tByteList = [1, 2, 3]; tInt8List = Int8List.fromList([-4, 5, 6]); tUint8List = Uint8List.fromList([7, 8, 9]); - tMongoId = Uint8List.fromList([-3, 4, 5]); } TestEntity.ignoredExcept(this.tInt) : tString = '' { @@ -477,6 +472,22 @@ class HnswObject { final rel = ToOne(); } +@Entity() +class EntityWithExternalType { + @Id() + int id = 0; + + @Property(type: PropertyType.byteVector) + @ExternalType(type: ExternalPropertyType.mongoId) + List? mongoId; + + @ExternalType(type: ExternalPropertyType.uuid) + @ExternalName(name: 'my-mongo-uuid') + List? mongoUuid; + + EntityWithExternalType(this.mongoId, this.mongoUuid); +} + @Entity() class RelatedNamedEntity { @Id() diff --git a/objectbox_test/test/objectbox-model.json b/objectbox_test/test/objectbox-model.json index b3928e2e2..adcc3fd29 100644 --- a/objectbox_test/test/objectbox-model.json +++ b/objectbox_test/test/objectbox-model.json @@ -5,7 +5,7 @@ "entities": [ { "id": "1:4630700155272683157", - "lastPropertyId": "36:5399124829543441597", + "lastPropertyId": "35:1724663621433823504", "name": "TestEntity", "properties": [ { @@ -196,11 +196,6 @@ "type": 6, "flags": 32808, "indexId": "20:4846837430056399798" - }, - { - "id": "36:5399124829543441597", - "name": "tMongoId", - "type": 23 } ], "relations": [ @@ -729,9 +724,33 @@ } ], "relations": [] + }, + { + "id": "16:5931645853908059165", + "lastPropertyId": "3:7670802129899081197", + "name": "EntityWithExternalType", + "properties": [ + { + "id": "1:3044824951282217899", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:8057333288729755334", + "name": "mongoId", + "type": 23 + }, + { + "id": "3:7670802129899081197", + "name": "mongoUuid", + "type": 27 + } + ], + "relations": [] } ], - "lastEntityId": "15:4803284427984871569", + "lastEntityId": "16:5931645853908059165", "lastIndexId": "23:6649884639373473085", "lastRelationId": "1:2155747579134420981", "lastSequenceId": "0:0", From 04fa1ca934324a5a06a7fbf10774f11ba84b1c18 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 07:33:42 +0100 Subject: [PATCH 06/12] external types: drop property prefix from external field names #139 --- generator/lib/src/code_builder.dart | 8 ++--- generator/lib/src/code_chunks.dart | 18 +++++------ generator/lib/src/entity_resolver.dart | 20 ++++++------- generator/test/code_builder_test.dart | 12 ++++---- .../lib/src/modelinfo/modelproperty.dart | 20 ++++++------- .../lib/src/modelinfo/modelrelation.dart | 30 ++++++++++--------- objectbox/lib/src/native/model.dart | 29 ++++++++---------- 7 files changed, 67 insertions(+), 70 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 264c99c1d..1a37eca75 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -213,8 +213,8 @@ class CodeBuilder extends Builder { propInModel.dartFieldType = prop.dartFieldType; propInModel.relationTarget = prop.relationTarget; propInModel.hnswParams = prop.hnswParams; - propInModel.externalPropertyType = prop.externalPropertyType; - propInModel.externalPropertyName = prop.externalPropertyName; + propInModel.externalType = prop.externalType; + propInModel.externalName = prop.externalName; if (!prop.hasIndexFlag()) { propInModel.removeIndex(); @@ -244,8 +244,8 @@ class CodeBuilder extends Builder { relInModel.name = rel.name; relInModel.targetName = rel.targetName; - relInModel.externalPropertyType = rel.externalPropertyType; - relInModel.externalPropertyName = rel.externalPropertyName; + relInModel.externalType = rel.externalType; + relInModel.externalName = rel.externalName; } IdUid mergeEntity(ModelInfo modelInfo, ModelEntity entity) { diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 19da063ad..938a05988 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -154,13 +154,11 @@ class CodeChunks { additionalArgs += ", hnswParams: ${property.hnswParams!.toCodeString(obxInt)}"; } - if (property.externalPropertyType != null) { - additionalArgs += - ", externalPropertyType: ${property.externalPropertyType!}"; + if (property.externalType != null) { + additionalArgs += ", externalType: ${property.externalType!}"; } - if (property.externalPropertyName != null) { - additionalArgs += - ", externalPropertyName: '${property.externalPropertyName!}'"; + if (property.externalName != null) { + additionalArgs += ", externalName: '${property.externalName!}'"; } return ''' $obxInt.ModelProperty( @@ -175,11 +173,11 @@ class CodeChunks { static String createModelRelation(ModelRelation relation) { var additionalArgs = ''; - if (relation.externalPropertyType != null) { - additionalArgs += ", externalType: ${relation.externalPropertyType!}"; + if (relation.externalType != null) { + additionalArgs += ", externalType: ${relation.externalType!}"; } - if (relation.externalPropertyName != null) { - additionalArgs += ", externalName: '${relation.externalPropertyName!}'"; + if (relation.externalName != null) { + additionalArgs += ", externalName: '${relation.externalName!}'"; } return ''' $obxInt.ModelRelation( diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index d964079ba..f1958a623 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -189,28 +189,28 @@ class EntityResolver extends Builder { // Handles standalone (non backlink) ToMany relation // @ExternalType - int? externalPropertyType; + int? externalType; _externalTypeChecker.runIfMatches(annotated, (annotation) { final externalTypeId = _readExternalTypeParams(annotation); - externalPropertyType = externalTypeId; + externalType = externalTypeId; }); // @ExternalName - String? externalPropertyName; + String? externalName; _externalNameChecker.runIfMatches(annotated, (annotation) { - if (externalPropertyType == null) { + if (externalType == null) { throw InvalidGenerationSourceError( "@ExternalName annotation requires @ExternalType annotation to be present"); } - externalPropertyName = _readExternalNameParams(annotation); + externalName = _readExternalNameParams(annotation); }); // create relation final rel = ModelRelation.create(IdUid(0, propUid ?? 0), f.name, targetName: relTargetName, uidRequest: propUid != null && propUid == 0, - externalPropertyName: externalPropertyName, - externalPropertyType: externalPropertyType); + externalName: externalName, + externalType: externalType); entity.relations.add(rel); @@ -255,17 +255,17 @@ class EntityResolver extends Builder { // @ExternalType _externalTypeChecker.runIfMatches(annotated, (annotation) { final externalTypeId = _readExternalTypeParams(annotation); - prop.externalPropertyType = externalTypeId; + prop.externalType = externalTypeId; }); // @ExternalName _externalNameChecker.runIfMatches(annotated, (annotation) { - if (prop.externalPropertyType == null) { + if (prop.externalType == null) { throw InvalidGenerationSourceError( "@ExternalName annotation requires @ExternalType annotation to be present"); } final externalName = _readExternalNameParams(annotation); - prop.externalPropertyName = externalName; + prop.externalName = externalName; }); // for code generation diff --git a/generator/test/code_builder_test.dart b/generator/test/code_builder_test.dart index 55a1a4a60..6b35de459 100644 --- a/generator/test/code_builder_test.dart +++ b/generator/test/code_builder_test.dart @@ -405,12 +405,12 @@ void main() { final property1 = testEnv.model.entities[0].properties .firstWhere((element) => element.name == "mongoId"); - expect(property1.externalPropertyType, OBXExternalPropertyType.MongoId); + expect(property1.externalType, OBXExternalPropertyType.MongoId); final property2 = testEnv.model.entities[0].properties .firstWhere((element) => element.name == "mongoUuid"); - expect(property2.externalPropertyType, OBXExternalPropertyType.Uuid); - expect(property2.externalPropertyName, "my-mongo-uuid"); + expect(property2.externalType, OBXExternalPropertyType.Uuid); + expect(property2.externalName, "my-mongo-uuid"); }); test('annotations work on ToMany (standalone) relations', () async { @@ -436,12 +436,12 @@ void main() { final relation1 = testEnv.model.entities[0].relations .firstWhere((element) => element.name == "rel1"); - expect(relation1.externalPropertyType, OBXExternalPropertyType.MongoId); + expect(relation1.externalType, OBXExternalPropertyType.MongoId); final relation2 = testEnv.model.entities[0].relations .firstWhere((element) => element.name == "rel2"); - expect(relation2.externalPropertyType, OBXExternalPropertyType.Uuid); - expect(relation2.externalPropertyName, "my-courses-rel"); + expect(relation2.externalType, OBXExternalPropertyType.Uuid); + expect(relation2.externalName, "my-courses-rel"); }); }); diff --git a/objectbox/lib/src/modelinfo/modelproperty.dart b/objectbox/lib/src/modelinfo/modelproperty.dart index 1e8658ede..863e5ed08 100644 --- a/objectbox/lib/src/modelinfo/modelproperty.dart +++ b/objectbox/lib/src/modelinfo/modelproperty.dart @@ -16,8 +16,8 @@ class ModelProperty { ModelEntity? entity; String? relationTarget; ModelHnswParams? hnswParams; - int? externalPropertyType; - String? externalPropertyName; + int? externalType; + String? externalName; /// Type used in the source dart code - used by the code generator. /// Starts with [_fieldReadOnlyPrefix] if the field (currently IDs only) is @@ -117,8 +117,8 @@ class ModelProperty { IdUid? indexId, this.relationTarget, this.hnswParams, - this.externalPropertyName, - this.externalPropertyType}) + this.externalName, + this.externalType}) : _name = name, _type = type, _flags = flags, @@ -132,8 +132,8 @@ class ModelProperty { uidRequest = data['uidRequest'] as bool? ?? false, hnswParams = ModelHnswParams.fromMap( data['hnswParams'] as Map?), - externalPropertyType = data['externalType'] as int?, - externalPropertyName = data['externalName'] as String? { + externalType = data['externalType'] as int?, + externalName = data['externalName'] as String? { name = data['name'] as String?; type = data['type'] as int?; flags = data['flags'] as int? ?? 0; @@ -157,11 +157,11 @@ class ModelProperty { if (hnswParams != null) { ret['hnswParams'] = hnswParams!.toMap(); } - if (externalPropertyType != null) { - ret['externalType'] = externalPropertyType; + if (externalType != null) { + ret['externalType'] = externalType; } - if (externalPropertyName != null) { - ret['externalName'] = externalPropertyName; + if (externalName != null) { + ret['externalName'] = externalName; } } return ret; diff --git a/objectbox/lib/src/modelinfo/modelrelation.dart b/objectbox/lib/src/modelinfo/modelrelation.dart index f5d64ca46..cad53f851 100644 --- a/objectbox/lib/src/modelinfo/modelrelation.dart +++ b/objectbox/lib/src/modelinfo/modelrelation.dart @@ -12,8 +12,8 @@ class ModelRelation { String? _targetName; - int? externalPropertyType; - String? externalPropertyName; + int? externalType; + String? externalName; // whether the user requested UID information (started a rename process) final bool uidRequest; @@ -49,17 +49,17 @@ class ModelRelation { ModelRelation.create(this.id, String? name, {String? targetId, String? targetName, - int? externalPropertyType, - String? externalPropertyName, + int? externalType, + String? externalName, this.uidRequest = false}) { this.name = name; if (targetId != null) this.targetId = IdUid.fromString(targetId); if (targetName != null) this.targetName = targetName; - if (externalPropertyType != null) { - this.externalPropertyType = externalPropertyType; + if (externalType != null) { + this.externalType = externalType; } - if (externalPropertyName != null) { - this.externalPropertyName = externalPropertyName; + if (externalName != null) { + this.externalName = externalName; } } @@ -80,18 +80,20 @@ class ModelRelation { targetId: data['targetId'] as String?, targetName: data['targetName'] as String?, uidRequest: data['uidRequest'] as bool? ?? false, - externalPropertyType: data['externalType'] as int?, - externalPropertyName: data['externalName'] as String?); + externalType: data['externalType'] as int?, + externalName: data['externalName'] as String?); Map toMap({bool forModelJson = false}) { final ret = {}; ret['id'] = id.toString(); ret['name'] = name; if (_targetId != null) ret['targetId'] = _targetId.toString(); - if (externalPropertyType != null) - ret['externalType'] = externalPropertyType; - if (externalPropertyName != null) - ret['externalName'] = externalPropertyName; + if (externalType != null) { + ret['externalType'] = externalType; + } + if (externalName != null) { + ret['externalName'] = externalName; + } if (!forModelJson) { ret['targetName'] = _targetName; ret['uidRequest'] = uidRequest; diff --git a/objectbox/lib/src/native/model.dart b/objectbox/lib/src/native/model.dart index bea32f47c..1983e7186 100644 --- a/objectbox/lib/src/native/model.dart +++ b/objectbox/lib/src/native/model.dart @@ -116,17 +116,16 @@ class Model { } } - final externalPropertyType = prop.externalPropertyType; - if (externalPropertyType != null) { - _check(C.model_property_external_type(_cModel, externalPropertyType)); + final externalType = prop.externalType; + if (externalType != null) { + _check(C.model_property_external_type(_cModel, externalType)); } - final externalPropertyName = prop.externalPropertyName?.toNativeUtf8(); - if (externalPropertyName != null) { + final externalName = prop.externalName?.toNativeUtf8(); + if (externalName != null) { try { - _check(C.model_property_external_name( - _cModel, externalPropertyName.cast())); + _check(C.model_property_external_name(_cModel, externalName.cast())); } finally { - calloc.free(externalPropertyName); + calloc.free(externalName); } } } @@ -134,16 +133,14 @@ class Model { void addRelation(ModelRelation rel) { _check(C.model_relation( _cModel, rel.id.id, rel.id.uid, rel.targetId.id, rel.targetId.uid)); - if (rel.externalPropertyType != null) { - _check( - C.model_relation_external_type(_cModel, rel.externalPropertyType!)); - if (rel.externalPropertyName != null) { - final externalPropertyName = rel.externalPropertyName!.toNativeUtf8(); + if (rel.externalType != null) { + _check(C.model_relation_external_type(_cModel, rel.externalType!)); + if (rel.externalName != null) { + final externalName = rel.externalName!.toNativeUtf8(); try { - _check(C.model_relation_external_name( - _cModel, externalPropertyName.cast())); + _check(C.model_relation_external_name(_cModel, externalName.cast())); } finally { - calloc.free(externalPropertyName); + calloc.free(externalName); } } } From 68cd1c9b7aeaac54518a13dedd7bf6a6149f5d84 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 07:45:10 +0100 Subject: [PATCH 07/12] external types: add ToMany and external name to test entity #139 --- objectbox_test/test/box_test.dart | 9 ++++++++- objectbox_test/test/entity.dart | 5 +++++ objectbox_test/test/objectbox-model.json | 12 ++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/objectbox_test/test/box_test.dart b/objectbox_test/test/box_test.dart index fa62a5818..941b6acd0 100644 --- a/objectbox_test/test/box_test.dart +++ b/objectbox_test/test/box_test.dart @@ -725,12 +725,19 @@ void main() { }); test('@ExternalType and @ExternalName annotations work correctly', () { + // That the database accepts a model with external types and names is tested + // implicitly by creating a Store. This test is only a smoke test to verify + // the annotated properties and ToMany work without issue. final box = store.box(); - final id = box.put(EntityWithExternalType([90, 100, 110], [1, 2, 3])); + var testObject = EntityWithExternalType([90, 100, 110], [1, 2, 3]); + testObject.mongoIdEntities + .add(EntityWithExternalType([120, 130, 140], null)); + final id = box.put(testObject); final item = box.get(id)!; expect(item.id, id); expect(item.mongoId, [90, 100, 110]); expect(item.mongoUuid, [1, 2, 3]); + expect(item.mongoIdEntities.first.mongoId, [120, 130, 140]); }); test('.count() works', () { diff --git a/objectbox_test/test/entity.dart b/objectbox_test/test/entity.dart index 0c27e7e14..1bfcf3297 100644 --- a/objectbox_test/test/entity.dart +++ b/objectbox_test/test/entity.dart @@ -473,6 +473,7 @@ class HnswObject { } @Entity() +@ExternalName(name: 'my-mongo-entity') class EntityWithExternalType { @Id() int id = 0; @@ -485,6 +486,10 @@ class EntityWithExternalType { @ExternalName(name: 'my-mongo-uuid') List? mongoUuid; + @ExternalType(type: ExternalPropertyType.mongoIdVector) + @ExternalName(name: 'my-mongo-rel') + final mongoIdEntities = ToMany(); + EntityWithExternalType(this.mongoId, this.mongoUuid); } diff --git a/objectbox_test/test/objectbox-model.json b/objectbox_test/test/objectbox-model.json index adcc3fd29..e1c4def21 100644 --- a/objectbox_test/test/objectbox-model.json +++ b/objectbox_test/test/objectbox-model.json @@ -747,12 +747,20 @@ "type": 27 } ], - "relations": [] + "relations": [ + { + "id": "2:313640065593441165", + "name": "mongoIdEntities", + "targetId": "16:5931645853908059165", + "externalType": 124, + "externalName": "my-mongo-rel" + } + ] } ], "lastEntityId": "16:5931645853908059165", "lastIndexId": "23:6649884639373473085", - "lastRelationId": "1:2155747579134420981", + "lastRelationId": "2:313640065593441165", "lastSequenceId": "0:0", "modelVersion": 5, "modelVersionParserMinimum": 5, From ec18417d3943535383ba4bc909e51e532b1f4cd2 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 07:49:10 +0100 Subject: [PATCH 08/12] external types: add external type, name for properties to model JSON #139 --- objectbox/lib/src/modelinfo/modelproperty.dart | 8 ++------ objectbox_test/test/objectbox-model.json | 7 +++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/objectbox/lib/src/modelinfo/modelproperty.dart b/objectbox/lib/src/modelinfo/modelproperty.dart index 863e5ed08..30c720d8f 100644 --- a/objectbox/lib/src/modelinfo/modelproperty.dart +++ b/objectbox/lib/src/modelinfo/modelproperty.dart @@ -146,6 +146,8 @@ class ModelProperty { ret['id'] = id.toString(); ret['name'] = name; ret['type'] = type; + if (externalType != null) ret['externalType'] = externalType; + if (externalName != null) ret['externalName'] = externalName; if (flags != 0) ret['flags'] = flags; if (indexId != null) ret['indexId'] = indexId!.toString(); if (relationTarget != null) ret['relationTarget'] = relationTarget; @@ -157,12 +159,6 @@ class ModelProperty { if (hnswParams != null) { ret['hnswParams'] = hnswParams!.toMap(); } - if (externalType != null) { - ret['externalType'] = externalType; - } - if (externalName != null) { - ret['externalName'] = externalName; - } } return ret; } diff --git a/objectbox_test/test/objectbox-model.json b/objectbox_test/test/objectbox-model.json index e1c4def21..f5a1b131e 100644 --- a/objectbox_test/test/objectbox-model.json +++ b/objectbox_test/test/objectbox-model.json @@ -739,12 +739,15 @@ { "id": "2:8057333288729755334", "name": "mongoId", - "type": 23 + "type": 23, + "externalType": 123 }, { "id": "3:7670802129899081197", "name": "mongoUuid", - "type": 27 + "type": 27, + "externalType": 102, + "externalName": "my-mongo-uuid" } ], "relations": [ From 4d3164c90e0c10a5f3da1560991d9fa85e5f65be Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 09:27:26 +0100 Subject: [PATCH 09/12] external types: support external name for @Entity #139 --- generator/lib/src/code_builder.dart | 1 + generator/lib/src/code_chunks.dart | 6 +++++- generator/lib/src/entity_resolver.dart | 5 +++++ generator/test/code_builder_test.dart | 20 ++++++++++++++++++++ objectbox/lib/src/modelinfo/modelentity.dart | 4 ++++ objectbox/lib/src/native/model.dart | 9 +++++++++ objectbox_test/test/box_test.dart | 4 ++-- objectbox_test/test/objectbox-model.json | 1 + 8 files changed, 47 insertions(+), 3 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 1a37eca75..f447004b9 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -271,6 +271,7 @@ class CodeBuilder extends Builder { entityInModel.name = entity.name; entityInModel.flags = entity.flags; + entityInModel.externalName = entity.externalName; entityInModel.constructorParams = entity.constructorParams; // here, the entity was found already and entityInModel and entity might differ, i.e. conflicts need to be resolved, so merge all properties first diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 938a05988..2fbce137e 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -122,12 +122,16 @@ class CodeChunks { } static String createModelEntity(ModelEntity entity) { + var additionalArgs = ''; + if (entity.externalName != null) { + additionalArgs += " externalName: '${entity.externalName}',"; + } return ''' $obxInt.ModelEntity( id: ${createIdUid(entity.id)}, name: '${entity.name}', lastPropertyId: ${createIdUid(entity.lastPropertyId)}, - flags: ${entity.flags}, + flags: ${entity.flags},$additionalArgs properties: <$obxInt.ModelProperty>[ ${entity.properties.map(createModelProperty).join(',')} ], diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index f1958a623..e3b3e04e7 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -70,6 +70,11 @@ class EntityResolver extends Builder { null, uidRequest: !entityUid.isNull && entityUid.intValue == 0); + // @ExternalName + _externalNameChecker.runIfMatches(classElement, (annotation) { + entity.externalName = _readExternalNameParams(annotation); + }); + // Sync: check if enabled and options _syncChecker.runIfMatches(classElement, (annotation) { entity.flags |= OBXEntityFlags.SYNC_ENABLED; diff --git a/generator/test/code_builder_test.dart b/generator/test/code_builder_test.dart index 6b35de459..d95e66f62 100644 --- a/generator/test/code_builder_test.dart +++ b/generator/test/code_builder_test.dart @@ -380,6 +380,26 @@ void main() { }); group("ExternalType and ExternalName annotations", () { + test('annotations work on @Entity', () async { + final source = r''' + library example; + import 'package:objectbox/objectbox.dart'; + + @Entity() + @ExternalName(name: 'my-mongo-entity') + class Example { + @Id() + int id = 0; + } + '''; + + final testEnv = GeneratorTestEnv(); + await testEnv.run(source); + + final entity = testEnv.model.entities[0]; + expect(entity.externalName, "my-mongo-entity"); + }); + test('annotations work on properties', () async { final source = r''' library example; diff --git a/objectbox/lib/src/modelinfo/modelentity.dart b/objectbox/lib/src/modelinfo/modelentity.dart index e15ac526d..e5c0f0346 100644 --- a/objectbox/lib/src/modelinfo/modelentity.dart +++ b/objectbox/lib/src/modelinfo/modelentity.dart @@ -14,6 +14,7 @@ class ModelEntity { late String _name; IdUid lastPropertyId = const IdUid.empty(); int _flags = 0; + String? externalName; final List _properties; final List _relations; final List _backlinks; @@ -72,6 +73,7 @@ class ModelEntity { required String name, required this.lastPropertyId, required int flags, + this.externalName, required List properties, required List relations, required List backlinks}) @@ -94,6 +96,7 @@ class ModelEntity { _backlinks = [] { name = data['name'] as String?; flags = data['flags'] as int? ?? 0; + externalName = data['externalName'] as String?; final properties = data['properties'] as List; for (final p in properties) { @@ -179,6 +182,7 @@ class ModelEntity { ret['lastPropertyId'] = lastPropertyId.toString(); ret['name'] = name; if (flags != 0) ret['flags'] = flags; + if (externalName != null) ret['externalName'] = externalName; ret['properties'] = properties.map((p) => p.toMap(forModelJson: forModelJson)).toList(); ret['relations'] = diff --git a/objectbox/lib/src/native/model.dart b/objectbox/lib/src/native/model.dart index 1983e7186..9cb1f46c6 100644 --- a/objectbox/lib/src/native/model.dart +++ b/objectbox/lib/src/native/model.dart @@ -52,6 +52,15 @@ class Model { if (entity.flags != 0) _check(C.model_entity_flags(_cModel, entity.flags)); + final externalName = entity.externalName?.toNativeUtf8(); + if (externalName != null) { + try { + _check(C.model_entity_external_name(_cModel, externalName.cast())); + } finally { + calloc.free(externalName); + } + } + // add all properties entity.properties.forEach(addProperty); diff --git a/objectbox_test/test/box_test.dart b/objectbox_test/test/box_test.dart index 941b6acd0..90c1f9237 100644 --- a/objectbox_test/test/box_test.dart +++ b/objectbox_test/test/box_test.dart @@ -731,13 +731,13 @@ void main() { final box = store.box(); var testObject = EntityWithExternalType([90, 100, 110], [1, 2, 3]); testObject.mongoIdEntities - .add(EntityWithExternalType([120, 130, 140], null)); + .add(EntityWithExternalType([120, 121, 122], null)); final id = box.put(testObject); final item = box.get(id)!; expect(item.id, id); expect(item.mongoId, [90, 100, 110]); expect(item.mongoUuid, [1, 2, 3]); - expect(item.mongoIdEntities.first.mongoId, [120, 130, 140]); + expect(item.mongoIdEntities.first.mongoId, [120, 121, 122]); }); test('.count() works', () { diff --git a/objectbox_test/test/objectbox-model.json b/objectbox_test/test/objectbox-model.json index f5a1b131e..ebb6adf65 100644 --- a/objectbox_test/test/objectbox-model.json +++ b/objectbox_test/test/objectbox-model.json @@ -729,6 +729,7 @@ "id": "16:5931645853908059165", "lastPropertyId": "3:7670802129899081197", "name": "EntityWithExternalType", + "externalName": "my-mongo-entity", "properties": [ { "id": "1:3044824951282217899", From 79c8ae134996df30511af0dbf1e13f2604ddb9b0 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 26 Mar 2025 08:58:47 +0100 Subject: [PATCH 10/12] Update ObjectBox Swift [4.1.0 -> 4.2.0] Bundled with C API 4.2.0 and ObjectBox 4.2.0-2025-03-04 --- dev-doc/updating-c-library.md | 12 ++++++------ flutter_libs/ios/objectbox_flutter_libs.podspec | 2 +- flutter_libs/macos/objectbox_flutter_libs.podspec | 2 +- objectbox/CHANGELOG.md | 6 ++++-- .../ios/objectbox_sync_flutter_libs.podspec | 2 +- .../macos/objectbox_sync_flutter_libs.podspec | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dev-doc/updating-c-library.md b/dev-doc/updating-c-library.md index 507a22626..b54c2d7f6 100644 --- a/dev-doc/updating-c-library.md +++ b/dev-doc/updating-c-library.md @@ -23,7 +23,7 @@ for Flutter (`flutter_libs` and `sync_flutter_libs` plugins) on Linux and Window ``` ```text -* Update database runtime library for Flutter for Linux/Windows, Dart Native to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). +* Update ObjectBox database for Flutter Linux/Windows, Dart Native apps to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). ``` ```text @@ -39,7 +39,7 @@ For the Flutter plugins on Android ([view releases](https://github.com/objectbox ``` ```text -* Update database runtime library for Flutter for Android to 4.2.0. +* Update ObjectBox database for Flutter Android apps to 4.2.0. If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make sure to update to `io.objectbox:objectbox-android-objectbrowser:4.2.0` in `android/app/build.gradle`. ``` @@ -58,18 +58,18 @@ from the relevant objectbox repository release tag (like `java-4.1.0`). For the Flutter plugins on iOS/macOS ([view releases](https://github.com/objectbox/objectbox-swift/releases)) ```bash -./tool/set-swift-version.sh 4.1.0 +./tool/set-swift-version.sh 4.2.0 ``` ```text -* Flutter for iOS/macOS: update to [objectbox-swift 4.1.0](https://github.com/objectbox/objectbox-swift/releases/tag/v4.1.0). +* Update ObjectBox database for Flutter iOS/macOS apps to 4.2.0. For existing projects, run `pod repo update` and `pod update ObjectBox` in the `ios` or `macos` directories. ``` ```text -Update ObjectBox Swift [4.0.0 -> 4.0.1] +Update ObjectBox Swift [4.1.0 -> 4.2.0] -Bundled with C API 4.1.0 and ObjectBox 4.1.0-2025-01-30 +Bundled with C API 4.2.0 and ObjectBox 4.2.0-2025-03-04 ``` Note: the embedded C API and ObjectBox version can be looked up diff --git a/flutter_libs/ios/objectbox_flutter_libs.podspec b/flutter_libs/ios/objectbox_flutter_libs.podspec index 3a24c744b..a26696e9d 100644 --- a/flutter_libs/ios/objectbox_flutter_libs.podspec +++ b/flutter_libs/ios/objectbox_flutter_libs.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'ObjectBox', '4.1.0' + s.dependency 'ObjectBox', '4.2.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/flutter_libs/macos/objectbox_flutter_libs.podspec b/flutter_libs/macos/objectbox_flutter_libs.podspec index 5dcbcf81d..e6ef1d9af 100644 --- a/flutter_libs/macos/objectbox_flutter_libs.podspec +++ b/flutter_libs/macos/objectbox_flutter_libs.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' - s.dependency 'ObjectBox', '4.1.0' + s.dependency 'ObjectBox', '4.2.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.swift_version = '5.3' diff --git a/objectbox/CHANGELOG.md b/objectbox/CHANGELOG.md index db970fd07..cc1de67c7 100644 --- a/objectbox/CHANGELOG.md +++ b/objectbox/CHANGELOG.md @@ -4,10 +4,12 @@ * Examples: demos are compatible with JDK 21 included with Android Studio Ladybug or later, require Flutter SDK 3.24 (with Dart SDK 3.5) or newer. * Requires at least Dart SDK 3.4 or Flutter SDK 3.22. -* Update database runtime library for Flutter for Linux/Windows, Dart Native to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). -* Update database runtime library for Flutter for Android to 4.2.0. +* Update ObjectBox database for Flutter Linux/Windows, Dart Native apps to [4.2.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.2.0). +* Update ObjectBox database for Flutter Android apps to 4.2.0. If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make sure to update to `io.objectbox:objectbox-android-objectbrowser:4.2.0` in `android/app/build.gradle`. +* Update ObjectBox database for Flutter iOS/macOS apps to 4.2.0. + For existing projects, run `pod repo update` and `pod update ObjectBox` in the `ios` or `macos` directories. ## 4.1.0 (2025-02-04) diff --git a/sync_flutter_libs/ios/objectbox_sync_flutter_libs.podspec b/sync_flutter_libs/ios/objectbox_sync_flutter_libs.podspec index d452b7b91..3267a9d1a 100644 --- a/sync_flutter_libs/ios/objectbox_sync_flutter_libs.podspec +++ b/sync_flutter_libs/ios/objectbox_sync_flutter_libs.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'ObjectBox', '4.1.0-sync' + s.dependency 'ObjectBox', '4.2.0-sync' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/sync_flutter_libs/macos/objectbox_sync_flutter_libs.podspec b/sync_flutter_libs/macos/objectbox_sync_flutter_libs.podspec index 7c39f43e0..eab6f3d23 100644 --- a/sync_flutter_libs/macos/objectbox_sync_flutter_libs.podspec +++ b/sync_flutter_libs/macos/objectbox_sync_flutter_libs.podspec @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' - s.dependency 'ObjectBox', '4.1.0-sync' + s.dependency 'ObjectBox', '4.2.0-sync' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.swift_version = '5.3' From be14b89c812d495c9cf79bc36e73c9429db9d7ca Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 14 Apr 2025 13:53:29 +0200 Subject: [PATCH 11/12] Scripts: fix set version on macOS #126 --- tool/common.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tool/common.sh b/tool/common.sh index e8f41ba0c..171f16379 100755 --- a/tool/common.sh +++ b/tool/common.sh @@ -9,11 +9,17 @@ root=$( ) echo "Repo root dir: $root" -# align GNU vs BSD `sed` version handling -i argument +# macOS includes the BSD version of sed, which uses a different syntax than the +# GNU version, which these scripts expect. So require users of this script to +# install gsed. if [[ "$OSTYPE" == "darwin"* ]]; then - sed="sed -i ''" + if ! command -v gsed &>/dev/null; then + echo "Error: gsed is required but not installed. Install it, for example using 'brew install gsed'." + exit 1 + fi + sed="gsed" else - sed="sed -i" + sed="sed" fi function update() { @@ -26,5 +32,5 @@ function update() { expr=${2} echo "Updating ${file} - \"${expr}\"" - $sed "${expr}" "$root/$file" + $sed -i "${expr}" "$root/$file" } From 2d34f9b16b420233373c7ef7fef03766ea8cfd79 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 14 Apr 2025 14:26:08 +0200 Subject: [PATCH 12/12] external types: allow only external name #139 --- generator/lib/src/entity_resolver.dart | 11 +---------- generator/test/code_builder_test.dart | 25 ------------------------- objectbox/lib/src/native/model.dart | 14 +++++++------- 3 files changed, 8 insertions(+), 42 deletions(-) diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index e3b3e04e7..03355b962 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -203,10 +203,6 @@ class EntityResolver extends Builder { // @ExternalName String? externalName; _externalNameChecker.runIfMatches(annotated, (annotation) { - if (externalType == null) { - throw InvalidGenerationSourceError( - "@ExternalName annotation requires @ExternalType annotation to be present"); - } externalName = _readExternalNameParams(annotation); }); @@ -265,12 +261,7 @@ class EntityResolver extends Builder { // @ExternalName _externalNameChecker.runIfMatches(annotated, (annotation) { - if (prop.externalType == null) { - throw InvalidGenerationSourceError( - "@ExternalName annotation requires @ExternalType annotation to be present"); - } - final externalName = _readExternalNameParams(annotation); - prop.externalName = externalName; + prop.externalName = _readExternalNameParams(annotation); }); // for code generation diff --git a/generator/test/code_builder_test.dart b/generator/test/code_builder_test.dart index d95e66f62..29e5cb44d 100644 --- a/generator/test/code_builder_test.dart +++ b/generator/test/code_builder_test.dart @@ -464,31 +464,6 @@ void main() { expect(relation2.externalName, "my-courses-rel"); }); }); - - test('Only ExternalName annotation fails', () async { - final source = r''' - library example; - import 'package:objectbox/objectbox.dart'; - - @Entity() - class Example { - @Id() - int id = 0; - - @ExternalName(name: 'my-mongo-uuid') - List? mongoUuid; - } - '''; - - final testEnv = GeneratorTestEnv(); - await expectLater( - () async => await testEnv.run(source), - throwsA(isA().having( - (e) => e.message, - 'message', - contains( - "@ExternalName annotation requires @ExternalType annotation to be present")))); - }); } Future _unsupported() { diff --git a/objectbox/lib/src/native/model.dart b/objectbox/lib/src/native/model.dart index 9cb1f46c6..35753f9b9 100644 --- a/objectbox/lib/src/native/model.dart +++ b/objectbox/lib/src/native/model.dart @@ -144,13 +144,13 @@ class Model { _cModel, rel.id.id, rel.id.uid, rel.targetId.id, rel.targetId.uid)); if (rel.externalType != null) { _check(C.model_relation_external_type(_cModel, rel.externalType!)); - if (rel.externalName != null) { - final externalName = rel.externalName!.toNativeUtf8(); - try { - _check(C.model_relation_external_name(_cModel, externalName.cast())); - } finally { - calloc.free(externalName); - } + } + final externalName = rel.externalName?.toNativeUtf8(); + if (externalName != null) { + try { + _check(C.model_relation_external_name(_cModel, externalName.cast())); + } finally { + calloc.free(externalName); } } }