diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 055627a..e258ad3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,7 +31,7 @@ test:linux:x64: extends: .test:linux:x64 parallel: matrix: - - GOVERSION: [ '1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20' ] + - GOVERSION: [ '1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20', '1.21', '1.22' ] # TODO Not working on shell runner (e.g. with default (old) version): investigate and find a working setup .test:linux:ARMv7hf: @@ -42,13 +42,16 @@ test:linux:x64: test:linux:aarch64: extends: .test - tags: [ aarch64, linux, shell ] + tags: [ arm64, linux, docker ] + image: golang:$GOVERSION variables: + GOVERSION: '1.22' BUILD_ARGS: -test.short test:mac:x64: allow_failure: true # TODO Investigate and fix extends: .test + tags: [ x64, mac, go ] test:win:x64: diff --git a/README.md b/README.md index 4935a4e..543c688 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ id, err := box.Put(&Person{ FirstName: "Joe", LastName: "Green" }) Want details? **[Read the docs](https://golang.objectbox.io/)** or **[check out the API reference](https://godoc.org/github.com/objectbox/objectbox-go/objectbox)**. -Latest release: [v1.7.0 (2023-06-23)](https://golang.objectbox.io/) +Latest release: [v1.8.0 (2024-02-16)](https://golang.objectbox.io/) ## Table of Contents: - [High-performance Golang database](#high-performance-golang-database) @@ -162,7 +162,7 @@ ObjectBox supports multiple platforms and languages: License ------- - Copyright 2018-2022 ObjectBox Ltd. All rights reserved. + Copyright 2018-2024 ObjectBox Ltd. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/build/ci.sh b/build/ci.sh index 57a4760..1be39f3 100755 --- a/build/ci.sh +++ b/build/ci.sh @@ -7,7 +7,7 @@ cd "${script_dir}/.." # move to project root dir args="$@" -bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-c/main/download.sh) --quiet --sync 0.18.1 +bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-c/main/download.sh) --quiet --sync 0.21.0 export CGO_LDFLAGS="-L$(pwd -P)/lib -Wl,-rpath -Wl,$(pwd -P)/lib" if [[ "$(uname)" == MINGW* ]]; then diff --git a/build/test.sh b/build/test.sh index 02f2b7f..d2eb2d7 100755 --- a/build/test.sh +++ b/build/test.sh @@ -23,14 +23,16 @@ fi echo "******** Testing: go vet ********" set +e -go_vet_result=$(go 2>&1 vet ./...) +# ignore '# ' comments to stablize checking false positive below; +# newer go version outputs a second-line with bracket ('# [&1 vet ./... | grep -v ^#) go_vet_rc=$? set -e echo "$go_vet_result" go_vet_result_lines=$(echo "$go_vet_result" | wc -l) if [ $go_vet_rc -ne 0 ]; then - if [[ $go_vet_result_lines -eq 2 && $go_vet_result == *objectbox[/\\]c-callbacks.go*possible\ misuse\ of\ unsafe.Pointer* ]]; then + if [[ $go_vet_result_lines -eq 1 && $go_vet_result == *objectbox[/\\]c-callbacks.go*possible\ misuse\ of\ unsafe.Pointer* ]]; then echo "Ignoring known false positive of go vet" go_vet_rc=0 else diff --git a/install.ps1 b/install.ps1 index 99410b7..4305da1 100644 --- a/install.ps1 +++ b/install.ps1 @@ -7,7 +7,7 @@ $ErrorActionPreference = "Stop" # Configure supported HTTPS protocols [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls -$libVersion = '0.18.1' +$libVersion = '0.21.0' $libVariant = 'objectbox' # or 'objectbox-sync' $downloadDir = 'download' $extractedLibDir = "$downloadDir\objectbox-$libVersion" diff --git a/install.sh b/install.sh index a3647c6..22ebf74 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -cLibVersion=0.18.1 +cLibVersion=0.21.0 os=$(uname) cLibArgs="$*" diff --git a/objectbox/model.go b/objectbox/model.go index 3f18adb..87374d4 100644 --- a/objectbox/model.go +++ b/objectbox/model.go @@ -165,7 +165,7 @@ func (model *Model) EntityFlags(entityFlags int) { return } model.Error = cCall(func() C.obx_err { - return C.obx_model_entity_flags(model.cModel, C.OBXEntityFlags(entityFlags)) + return C.obx_model_entity_flags(model.cModel, C.uint32_t(entityFlags)) }) } @@ -216,7 +216,7 @@ func (model *Model) PropertyFlags(propertyFlags int) { return } model.Error = cCall(func() C.obx_err { - return C.obx_model_property_flags(model.cModel, C.OBXPropertyFlags(propertyFlags)) + return C.obx_model_property_flags(model.cModel, C.uint32_t(propertyFlags)) }) } diff --git a/objectbox/objectbox-sync.h b/objectbox/objectbox-sync.h index 4b4c9c4..e9e10b8 100644 --- a/objectbox/objectbox-sync.h +++ b/objectbox/objectbox-sync.h @@ -34,7 +34,7 @@ #include "objectbox.h" #if defined(static_assert) || defined(__cplusplus) -static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 18 && OBX_VERSION_PATCH == 1, // NOLINT +static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 21 && OBX_VERSION_PATCH == 0, // NOLINT "Versions of objectbox.h and objectbox-sync.h files do not match, please update"); #endif @@ -51,10 +51,16 @@ extern "C" { struct OBX_sync; typedef struct OBX_sync OBX_sync; +/// Specifies user-side credential types as well as server-side authenticator types. +/// Some credentail types do not make sense as authenticators such as OBXSyncCredentialsType_USER_PASSWORD which +/// specifies a generic client-side credential type. typedef enum { OBXSyncCredentialsType_NONE = 1, OBXSyncCredentialsType_SHARED_SECRET = 2, OBXSyncCredentialsType_GOOGLE_AUTH = 3, + OBXSyncCredentialsType_SHARED_SECRET_SIPPED = 4, + OBXSyncCredentialsType_OBX_ADMIN_USER = 5, + OBXSyncCredentialsType_USER_PASSWORD = 6, } OBXSyncCredentialsType; // TODO sync prefix @@ -91,6 +97,13 @@ typedef enum { OBXSyncCode_TX_VIOLATED_UNIQUE = 71, } OBXSyncCode; +/// Sync-level error reporting codes, passed via obx_sync_listener_error(). +typedef enum { + /// Sync client received rejection of transaction writes due to missing permissions. + /// Until reconnecting with new credentials client will run in receive-only mode. + OBXSyncError_REJECT_TX_NO_PERMISSION = 1 +} OBXSyncError; + typedef enum { OBXSyncObjectType_FlatBuffers = 1, OBXSyncObjectType_String = 2, @@ -150,6 +163,11 @@ typedef void OBX_sync_listener_login_failure(void* arg, OBXSyncCode code); /// @param arg is a pass-through argument passed to the called API typedef void OBX_sync_listener_complete(void* arg); +/// Callend when sync-level errors occur +/// @param arg is a pass-through argument passed to the called API +/// @param error error code indicating sync-level error events +typedef void OBX_sync_listener_error(void* arg, OBXSyncError error); + /// Called with fine grained sync changes (IDs of put and removed entities) /// @param arg is a pass-through argument passed to the called API typedef void OBX_sync_listener_change(void* arg, const OBX_sync_change_array* changes); @@ -168,6 +186,10 @@ typedef void OBX_sync_listener_msg_objects(void* arg, const OBX_sync_msg_objects /// To configure this differently, call obx_sync_request_updates_mode() with the wanted mode. OBX_C_API OBX_sync* obx_sync(OBX_store* store, const char* server_url); +/// Creates a sync client associated with the given store and a list of sync server URL. +/// For details, see obx_sync() +OBX_C_API OBX_sync* obx_sync_urls(OBX_store* store, const char* server_urls[], size_t server_urls_count); + /// Stops and closes (deletes) the sync client, freeing its resources. OBX_C_API obx_err obx_sync_close(OBX_sync* sync); @@ -177,6 +199,14 @@ OBX_C_API obx_err obx_sync_close(OBX_sync* sync); /// @param data may be NULL in combination with OBXSyncCredentialsType_NONE OBX_C_API obx_err obx_sync_credentials(OBX_sync* sync, OBXSyncCredentialsType type, const void* data, size_t size); +/// Set username/password credentials to authenticate the client with the server. +/// This is suitable for OBXSyncCredentialsType_OBX_ADMIN_USER and OBXSyncCredentialsType_USER_PASSWORD credential +/// types. Use obx_sync_credentials() for other credential types. +/// @param type should be OBXSyncCredentialsType_OBX_ADMIN_USER or OBXSyncCredentialsType_USER_PASSWORD +/// @returns OBX_ERROR_ILLEGAL_ARGUMENT if credential type does not support username/password authentication. +OBX_C_API obx_err obx_sync_credentials_user_password(OBX_sync* sync, OBXSyncCredentialsType type, const char* username, + const char* password); + /// Configures the maximum number of outgoing TX messages that can be sent without an ACK from the server. /// @returns OBX_ERROR_ILLEGAL_ARGUMENT if value is not in the range 1-20 OBX_C_API obx_err obx_sync_max_messages_in_flight(OBX_sync* sync, int value); @@ -343,6 +373,50 @@ OBX_C_API void obx_sync_listener_server_time(OBX_sync* sync, OBX_sync_listener_s OBX_C_API void obx_sync_listener_msg_objects(OBX_sync* sync, OBX_sync_listener_msg_objects* listener, void* listener_arg); +/// Set or overwrite a previously set 'error' listener - provides information about occurred sync-level errors. +/// @param listener set NULL to reset +/// @param listener_arg is a pass-through argument passed to the listener +OBX_C_API void obx_sync_listener_error(OBX_sync* sync, OBX_sync_listener_error* error, void* listener_arg); + +//---------------------------------------------- +// Sync Stats +//---------------------------------------------- + +/// Stats counter type IDs as passed to obx_sync_stats_u64(). +typedef enum { + /// Total number of connects (u64) + OBXSyncStats_connects = 1, + + /// Total number of succesful logins (u64) + OBXSyncStats_logins = 2, + + /// Total number of messages received (u64) + OBXSyncStats_messagesReceived = 3, + + /// Total number of messages sent (u64) + OBXSyncStats_messagesSent = 4, + + /// Total number of errors during message sending (u64) + OBXSyncStats_messageSendFailures = 5, + + /// Total number of bytes received via messages. + /// Note: this is measured on the application level and thus may not match e.g. the network level. (u64) + OBXSyncStats_messageBytesReceived = 6, + + /// Total number of bytes sent via messages. + /// Note: this is measured on the application level and thus may not match e.g. the network level. + /// E.g. messages may be still enqueued so at least the timing will differ. (u64) + OBXSyncStats_messageBytesSent = 7, + +} OBXSyncStats; + +/// Get u64 value for sync statistics. +/// @param counter_type the counter value to be read. +/// @param out_count receives the counter value. +/// @return OBX_SUCCESS if the counter has been successfully retrieved. +/// @return OBX_ERROR_ILLEGAL_ARGUMENT if counter_type is undefined. +OBX_C_API obx_err obx_sync_stats_u64(OBX_sync* sync, OBXSyncStats counter_type, uint64_t* out_count); + struct OBX_sync_server; typedef struct OBX_sync_server OBX_sync_server; @@ -385,6 +459,12 @@ OBX_C_API obx_err obx_sync_server_certificate_path(OBX_sync_server* server, cons OBX_C_API obx_err obx_sync_server_credentials(OBX_sync_server* server, OBXSyncCredentialsType type, const void* data, size_t size); +/// Enables authenticator for server. Can be called multiple times. Use before obx_sync_server_start(). +/// Use obx_sync_server_credentials() for authenticators which requires additional credentials data (i.e. Google Auth +/// and shared secrets authenticators). +/// @param type should be one of the available authentications, it should not be OBXSyncCredentialsType_USER_PASSWORD. +OBX_C_API obx_err obx_sync_server_enable_auth(OBX_sync_server* server, OBXSyncCredentialsType type); + /// Sets the number of worker threads. Use before obx_sync_server_start(). /// @param thread_count The default is "0" which is hardware dependent, e.g. a multiple of CPU "cores". OBX_C_API obx_err obx_sync_server_worker_threads(OBX_sync_server* server, int thread_count); @@ -432,6 +512,192 @@ OBX_C_API uint16_t obx_sync_server_port(OBX_sync_server* server); /// Returns the number of clients connected to this server. OBX_C_API uint64_t obx_sync_server_connections(OBX_sync_server* server); +//---------------------------------------------- +// Sync Server Stats +//---------------------------------------------- + +/// Stats counter type IDs as passed to obx_sync_server_stats_u64() (for u64 values) and obx_sync_server_stats_f64() +/// (for double (f64) values). +typedef enum { + /// Total number of client connections established (u64) + OBXSyncServerStats_connects = 1, + + /// Total number of messages received from clients (u64) + OBXSyncServerStats_messagesReceived = 2, + + /// Total number of messages sent to clients (u64) + OBXSyncServerStats_messagesSent = 3, + + /// Total number of bytes received from clients via messages. (u64) + /// Note: this is measured on the application level and thus may not match e.g. the network level. + OBXSyncServerStats_messageBytesReceived = 4, + + /// Total number of bytes sent to clients via messages. (u64) + /// Note: this is measured on the application level and thus may not match e.g. the network level. + /// E.g. messages may be still enqueued so at least the timing will differ. + OBXSyncServerStats_messageBytesSent = 5, + + /// Full syncs performed (u64) + OBXSyncServerStats_fullSyncs = 6, + + /// Processing was aborted due to clients disconnected (u64) + OBXSyncServerStats_disconnectAborts = 7, + + /// Total number of client transactions applied (u64) + OBXSyncServerStats_clientTxsApplied = 8, + + /// Total size in bytes of client transactions applied (u64) + OBXSyncServerStats_clientTxBytesApplied = 9, + + /// Total size in number of operations of transactions applied (u64) + OBXSyncServerStats_clientTxOpsApplied = 10, + + /// Total number of local (server initiated) transactions applied (u64) + OBXSyncServerStats_localTxsApplied = 11, + + /// AsyncQ committed TXs (u64) + OBXSyncServerStats_asyncDbCommits = 12, + + /// Total number of skipped transactions duplicates (have been already applied before) (u64) + OBXSyncServerStats_skippedTxDups = 13, + + /// Total number of login successes (u64) + OBXSyncServerStats_loginSuccesses = 14, + + /// Total number of login failures (u64) + OBXSyncServerStats_loginFailures = 15, + + /// Total number of login failures due to bad user credentials (u64) + OBXSyncServerStats_loginFailuresUserBadCredentials = 16, + + /// Total number of login failures due to authenticator not available (u64) + OBXSyncServerStats_loginFailuresAuthUnavailable = 17, + + /// Total number of login failures due to user has no permissions (u64) + OBXSyncServerStats_loginFailuresUserNoPermission = 18, + + /// Total number of errors during message sending (u64) + OBXSyncServerStats_messageSendFailures = 19, + + /// Total number of protocol errors; e.g. offending clients (u64) + OBXSyncServerStats_errorsProtocol = 20, + + /// Total number of errors in message handlers (u64) + OBXSyncServerStats_errorsInHandlers = 21, + + /// Total number of times a client has been disconnected due to heart failure (u64) + OBXSyncServerStats_heartbeatFailures = 22, + + /// Total number of received client heartbeats (u64) + OBXSyncServerStats_heartbeatsReceived = 23, + + /// Total APPLY_TX messages HistoryPusher has sent out (u64) + OBXSyncServerStats_historicUpdateTxsSent = 24, + + /// Total APPLY_TX messages newDataPusher has sent out (u64) + OBXSyncServerStats_newUpdateTxsSent = 25, + + /// Total number of messages received from clients (u64) + OBXSyncServerStats_forwardedMessagesReceived = 26, + + /// Total number of messages sent to clients (u64) + OBXSyncServerStats_forwardedMessagesSent = 27, + + /// Total number of global-to-local cache hits (u64) + OBXSyncServerStats_cacheGlobalToLocalHits = 28, + + /// Total number of global-to-local cache misses (u64) + OBXSyncServerStats_cacheGlobalToLocalMisses = 29, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cacheGlobalToLocalSize = 30, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cachePeerToLocalHits = 31, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cachePeerToLocalMisses = 32, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cacheLocalToPeerHits = 33, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cacheLocalToPeerMisses = 34, + + /// Internal dev stat for ID Map caching (u64) + OBXSyncServerStats_cachePeerSize = 35, + + /// Current cluster peer state (0 = unknown, 1 = leader, 2 = follower, 3 = candidate) (u64) + OBXSyncServerStats_clusterPeerState = 36, + + /// Number of transactions between the current Tx and the oldest Tx currently ACKed on any client (current) + /// (f64) + OBXSyncServerStats_clientTxsBehind = 37, + + /// Number of transactions between the current Tx and the oldest Tx currently ACKed on any client (minimum) + /// (u64) + OBXSyncServerStats_clientTxsBehind_min = 38, + + /// Number of transactions between the current Tx and the oldest Tx currently ACKed on any client (maximum) + /// (u64) + OBXSyncServerStats_clientTxsBehind_max = 39, + + /// Number of connected clients (current) (f64) + OBXSyncServerStats_connectedClients = 40, + + /// Number of connected clients (minimum) (u64) + OBXSyncServerStats_connectedClients_min = 41, + + /// Number of connected clients (maximum) (u64) + OBXSyncServerStats_connectedClients_max = 42, + + /// Length of the queue for regular Tasks (current) (f64) + OBXSyncServerStats_queueLength = 43, + + /// Length of the queue for regular Tasks (minimum) (u64) + OBXSyncServerStats_queueLength_min = 44, + + /// Length of the queue for regular Tasks (maximum) (u64) + OBXSyncServerStats_queueLength_max = 45, + + /// Length of the async queue (current) (f64) + OBXSyncServerStats_queueLengthAsync = 46, + + /// Length of the async queue (minimum) (u64) + OBXSyncServerStats_queueLengthAsync_min = 47, + + /// Length of the async queue (maximum) (u64) + OBXSyncServerStats_queueLengthAsync_max = 48, + + /// Sequence number of TX log history (current) (f64) + OBXSyncServerStats_txHistorySequence = 49, + + /// Sequence number of TX log history (minimum) (u64) + OBXSyncServerStats_txHistorySequence_min = 50, + + /// Sequence number of TX log history (maximum) (u64) + OBXSyncServerStats_txHistorySequence_max = 51, + +} OBXSyncServerStats; + +/// Get u64 value for sync server statistics. +/// @param counter_type the counter value to be read (make sure to choose a uint64_t (u64) metric value type). +/// @param out_count receives the counter value. +/// @return OBX_SUCCESS if the counter has been successfully retrieved. +/// @return OBX_ERROR_ILLEGAL_ARGUMENT if counter_type is undefined (this also happens if the wrong type is requested) +/// @return OBX_ERROR_ILLEGAL_STATE if the server is not started. +OBX_C_API obx_err obx_sync_server_stats_u64(OBX_sync_server* server, OBXSyncServerStats counter_type, + uint64_t* out_value); + +/// Get double value for sync server statistics. +/// @param counter_type the counter value to be read (make sure to use a double (f64) metric value type). +/// @param out_count receives the counter value. +/// @return OBX_SUCCESS if the counter has been successfully retrieved. +/// @return OBX_ERROR_ILLEGAL_ARGUMENT if counter_type is undefined (this also happens if the wrong type is requested) +/// @return OBX_ERROR_ILLEGAL_STATE if the server is not started. +OBX_C_API obx_err obx_sync_server_stats_f64(OBX_sync_server* server, OBXSyncServerStats counter_type, + double* out_value); + /// Get server runtime statistics. /// The returned char* is valid until another call to obx_sync_server_stats_string() or the server is closed. OBX_C_API const char* obx_sync_server_stats_string(OBX_sync_server* server, bool include_zero_values); diff --git a/objectbox/objectbox.go b/objectbox/objectbox.go index 950e65c..36b2ee2 100644 --- a/objectbox/objectbox.go +++ b/objectbox/objectbox.go @@ -187,7 +187,7 @@ func (ob *ObjectBox) getEntityByName(name string) *entity { // See DebugFlags* constants func (ob *ObjectBox) SetDebugFlags(flags uint) error { return cCall(func() C.obx_err { - return C.obx_store_debug_flags(ob.store, C.OBXDebugFlags(flags)) + return C.obx_store_debug_flags(ob.store, C.uint32_t(flags)) }) } diff --git a/objectbox/objectbox.h b/objectbox/objectbox.h index 41b6830..970ab40 100644 --- a/objectbox/objectbox.h +++ b/objectbox/objectbox.h @@ -52,8 +52,8 @@ 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 0 -#define OBX_VERSION_MINOR 18 -#define OBX_VERSION_PATCH 1 // values >= 100 are reserved for dev releases leading to the next minor/major increase +#define OBX_VERSION_MINOR 21 +#define OBX_VERSION_PATCH 0 // values >= 100 are reserved for dev releases leading to the next minor/major increase //---------------------------------------------- // Common types @@ -117,14 +117,39 @@ typedef enum { /// Check whether debug log can be enabled during runtime. OBXFeature_DebugLog = 4, - /// Administration interface (HTTP server) with a database browser. + /// Admin UI including a database browser, user management, and more. + /// Depends on HttpServer (if Admin is available HttpServer is too). OBXFeature_Admin = 5, - /// Tree & GraphQL support + /// Tree with special GraphQL support OBXFeature_Tree = 6, /// Sync server availability. Visit https://objectbox.io/sync for more details. OBXFeature_SyncServer = 7, + + /// Implicitly added by Sync or SyncServer; disable via NoWebSockets + OBXFeature_WebSockets = 8, + + /// Sync Server has cluster functionality. + /// Implicitly added by SyncServer; disable via NoCluster + OBXFeature_Cluster = 9, + + /// Embedded HTTP server. + OBXFeature_HttpServer = 10, + + /// Embedded GraphQL server (via HTTP). + /// Depends on HttpServer (if GraphQL is available HttpServer is too). + OBXFeature_GraphQL = 11, + + /// Database Backup functionality; typically only enabled in Sync Server builds. + OBXFeature_Backup = 12, + + /// The default database "provider"; writes data persistently to disk (ACID). + OBXFeature_Lmdb = 13, + + /// Vector search functionality; enables indexing for nearest neighbor search. + OBXFeature_VectorSearch = 14, + } OBXFeature; /// Checks whether the given feature is available in the currently loaded library. @@ -219,6 +244,12 @@ OBX_C_API int obx_thread_number(); /// be fulfilled. Typically occurs in asynchronous operations. #define OBX_ERROR_SHUTTING_DOWN 10006 +/// An I/O operation failed, e.g. a file was not found, could not be written or read. +#define OBX_ERROR_IO 10007 + +/// A given backup file has invalid content and thus cannot be restored. +#define OBX_ERROR_BACKUP_FILE_INVALID 10008 + /// An error occurred but error information could not be extracted. /// This falls in the "should not happen" category and should be very rare; /// please report if you ever encounter this in a reproducible fashion. @@ -376,22 +407,32 @@ OBX_C_API bool obx_last_error_set(obx_err code, obx_err secondary, const char* m //---------------------------------------------- typedef enum { - OBXPropertyType_Bool = 1, ///< 1 byte - OBXPropertyType_Byte = 2, ///< 1 byte - OBXPropertyType_Short = 3, ///< 2 bytes - OBXPropertyType_Char = 4, ///< 1 byte - OBXPropertyType_Int = 5, ///< 4 bytes - OBXPropertyType_Long = 6, ///< 8 bytes - OBXPropertyType_Float = 7, ///< 4 bytes - OBXPropertyType_Double = 8, ///< 8 bytes - OBXPropertyType_String = 9, - OBXPropertyType_Date = 10, ///< Unix timestamp (milliseconds since 1970) in 8 bytes - OBXPropertyType_Relation = 11, - OBXPropertyType_DateNano = 12, ///< Unix timestamp (nanoseconds since 1970) in 8 bytes + OBXPropertyType_Unknown = 0, ///< Not a actual type; represents an uninitialized or invalid type + OBXPropertyType_Bool = 1, ///< A boolean (flag) + OBXPropertyType_Byte = 2, ///< 8-bit integer + OBXPropertyType_Short = 3, ///< 16-bit integer + OBXPropertyType_Char = 4, ///< 16-bit character + OBXPropertyType_Int = 5, ///< 32-bit integer + OBXPropertyType_Long = 6, ///< 64-bit integer + OBXPropertyType_Float = 7, ///< 32-bit floating point number + OBXPropertyType_Double = 8, ///< 64-bit floating point number + OBXPropertyType_String = 9, ///< UTF-8 encoded string (variable length) + OBXPropertyType_Date = 10, ///< 64-bit (integer) timestamp; milliseconds since 1970-01-01 (unix epoch) + OBXPropertyType_Relation = 11, ///< Relation to another entity + OBXPropertyType_DateNano = 12, ///< High precision 64-bit timestamp; nanoseconds since 1970-01-01 (unix epoch) OBXPropertyType_Flex = 13, ///< Flexible" type, which may contain scalars (integers, floating points), strings or ///< containers (lists and maps). Note: a flex map must use string keys. - OBXPropertyType_ByteVector = 23, - OBXPropertyType_StringVector = 30, + OBXPropertyType_BoolVector = 22, ///< Variable sized vector of Bool values (note: each value is one byte) + OBXPropertyType_ByteVector = 23, ///< Variable sized vector of Byte values (8-bit integers) + OBXPropertyType_ShortVector = 24, ///< Variable sized vector of Short values (16-bit integers) + OBXPropertyType_CharVector = 25, ///< Variable sized vector of Char values (16-bit characters) + OBXPropertyType_IntVector = 26, ///< Variable sized vector of Int values (32-bit integers) + OBXPropertyType_LongVector = 27, ///< Variable sized vector of Long values (64-bit integers) + OBXPropertyType_FloatVector = 28, ///< Variable sized vector of Float values (32-bit floating point numbers) + OBXPropertyType_DoubleVector = 29, ///< Variable sized vector of Double values (64-bit floating point numbers) + OBXPropertyType_StringVector = 30, ///< Variable sized vector of String values (UTF-8 encoded strings). + OBXPropertyType_DateVector = 31, ///< Variable sized vector of Date values (64-bit timestamp). + OBXPropertyType_DateNanoVector = 32, ///< Variable sized vector of Date values (high precision 64-bit timestamp). } OBXPropertyType; /// Bit-flags defining the behavior of entities. @@ -512,7 +553,8 @@ OBX_C_API const char* obx_model_error_message(OBX_model* model); OBX_C_API obx_err obx_model_entity(OBX_model* model, const char* name, obx_schema_id entity_id, obx_uid entity_uid); /// Refine the definition of the entity declared by the most recent obx_model_entity() call, specifying flags. -OBX_C_API obx_err obx_model_entity_flags(OBX_model* model, OBXEntityFlags flags); +/// @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); /// 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 @@ -523,7 +565,8 @@ OBX_C_API obx_err obx_model_property(OBX_model* model, const char* name, OBXProp obx_schema_id property_id, obx_uid property_uid); /// Refine the definition of the property declared by the most recent obx_model_property() call, specifying flags. -OBX_C_API obx_err obx_model_property_flags(OBX_model* model, OBXPropertyFlags flags); +/// @param flags See OBXPropertyFlags for values (use bitwise OR to combine multiple flags) +OBX_C_API obx_err obx_model_property_flags(OBX_model* model, uint32_t flags); /// Refine the definition of the property declared by the most recent obx_model_property() call, declaring it a /// relation. @@ -568,7 +611,8 @@ OBX_C_API obx_err obx_model_entity_last_property_id(OBX_model* model, obx_schema struct OBX_store; // doxygen (only) picks up the typedef struct below -/// \brief A ObjectBox store represents a database storing data in a given directory on a local file system. +/// \brief A ObjectBox store represents a database storing data in a given directory on a local file system or in-memory +/// using "memory:" directory prefix. /// /// Once opened using obx_store_open(), it's an entry point to data access APIs such as box, query, cursor, transaction. /// After your work is done, you must close obx_store_close() to safely release all the handles and avoid data loss. @@ -602,6 +646,20 @@ typedef enum { OBXDebugFlags_RUN_THREADING_SELF_TEST = 512, } OBXDebugFlags; +/// Flags used to control pages validation options when opening the store. +typedef enum { + OBXValidateOnOpenPagesFlags_None = 0, ///< No special flags + + /// Enable validation of leaf pages (by default only branch pages are validated) + OBXValidateOnOpenPagesFlags_VisitLeafPages = 1, +} OBXValidateOnOpenPagesFlags; + +/// Flags used to control key/value pairs validation options when opening the store. +/// Note: this enum does not contain any actual values besides "None" yet; it's only used to future proof the API. +typedef enum { + OBXValidateOnOpenKvFlags_None = 0, ///< No special flags +} OBXValidateOnOpenKvFlags; + /// Defines a padding mode for putting data bytes. /// Depending on how that data is created, this mode may optimize data handling by avoiding copying memory. /// Internal background: data buffers used by put operations are required to have a size divisible by 4 for an @@ -621,6 +679,16 @@ typedef enum { OBXPutPaddingMode_PaddingByCaller = 3, } OBXPutPaddingMode; +/// Backup flags control how the store creates backup files. +/// E.g. when you want "deterministic" file content, you can exclude timestamp and salt. +typedef enum { + /// Do not include a timestamp in the backup file (time when the backup file is generated). + OBXBackupFlags_ExcludeTimestamp = 0x1, + + /// Do not include a random salt in the backup file. + OBXBackupFlags_ExcludeSalt = 0x2, +} OBXBackupFlags; + /// Bytes struct is an input/output wrapper typically used for a single object data (represented as FlatBuffers). typedef struct OBX_bytes { const void* data; @@ -705,6 +773,7 @@ typedef struct OBX_float_array { OBX_C_API OBX_store_options* obx_opt(); /// Set the store directory on the options. The default is "objectbox". +/// Use prefix "memory:" to open an in-memory database, e.g. "memory:myApp" (see docs for details). OBX_C_API obx_err obx_opt_directory(OBX_store_options* opt, const char* dir); /// Set the maximum db size on the options. The default is 1Gb. @@ -717,7 +786,7 @@ OBX_C_API void obx_opt_max_db_size_in_kb(OBX_store_options* opt, uint64_t size_i /// Max data and DB sizes can be combined; data size must be below the DB size. OBX_C_API void obx_opt_max_data_size_in_kb(OBX_store_options* opt, uint64_t size_in_kb); -/// Set the file mode on the options. The default is 0644 (unix-style) +/// Set the file mode on the options. The default is 0644 (unix-style). OBX_C_API void obx_opt_file_mode(OBX_store_options* opt, unsigned int file_mode); /// Set the maximum number of readers (related to read transactions) on the given options. @@ -761,8 +830,15 @@ OBX_C_API obx_err obx_opt_model_bytes_direct(OBX_store_options* opt, const void* /// Note: ObjectBox builds upon ACID storage, which guarantees consistency given that the file system is working /// correctly (in particular fsync). /// @param page_limit limits the number of checked pages (currently defaults to 0, but will be increased in the future) -/// @param leaf_level enable for visiting leaf pages (defaults to false) -OBX_C_API void obx_opt_validate_on_open(OBX_store_options* opt, size_t page_limit, bool leaf_level); +/// @param flags flags used to influence how the validation checks are performed; +/// see OBXValidateOnOpenPagesFlags for values (use bitwise OR to combine multiple flags) +OBX_C_API void obx_opt_validate_on_open_pages(OBX_store_options* opt, size_t page_limit, uint32_t flags); + +/// When the DB is opened initially, ObjectBox can do a validation over the key/value pairs to check, for example, +/// whether they're consistent towards our internal specification. +/// @param flags flags used to influence how the validation checks are performed; +/// see OBXValidateOnOpenKvFlags for values (use bitwise OR to combine multiple flags) +OBX_C_API void obx_opt_validate_on_open_kv(OBX_store_options* opt, uint32_t flags); /// Don't touch unless you know exactly what you are doing: /// Advanced setting typically meant for language bindings (not end users). See OBXPutPaddingMode description. @@ -856,6 +932,20 @@ OBX_C_API void obx_opt_async_object_bytes_max_size_to_cache(OBX_store_options* o /// Note: this does not replace the default logging, which is much more extensive (at least at this point). OBX_C_API void obx_opt_log_callback(OBX_store_options* opt, obx_log_callback* callback, void* user_data); +/// Backup restore flags control how backups are restored to the database. +typedef enum { + /// Overwrite any existing database with the content of the backup file. + OBXBackupRestoreFlags_OverwriteExistingData = 1, +} OBXBackupRestoreFlags; + +/// Before opening the database, this options instructs to restore the database content from the given backup file. +/// Note: backup is a server-only feature. +/// By default, actually restoring the backup is only performed if no database already exists +/// (database does not contain data). +/// @param flags For default behavior pass 0, or adjust defaults using OBXBackupRestoreFlags bit flags, +/// e.g., to overwrite all existing data in the database. +OBX_C_API void obx_opt_backup_restore(OBX_store_options* opt, const char* backup_file, uint32_t flags); + /// Gets the option for "directory"; this is either the default, or, the value set by obx_opt_directory(). /// The returned value must not be modified and is only valid for the lifetime of the options or until the value is /// changed. @@ -885,7 +975,7 @@ OBX_C_API void obx_opt_free(OBX_store_options* opt); /// Opens (creates) a "store", which represents an ObjectBox database instance in a given directory. /// The store is an entry point to data access APIs such as box (obx_box_*), query (obx_qb_* and obx_query_*), /// and transaction (obx_txn_*). -/// It's possible open multiple stores in different directories, e.g. at the same time. +/// It's possible to open multiple stores in different directories, e.g. at the same time. /// See also obx_store_close() to close a previously opened store. /// Note: the given options are always freed by this function, including when an error occurs. /// @param opt required parameter holding the data model (obx_opt_model()) and optional options (see obx_opt_*()) @@ -923,6 +1013,10 @@ OBX_C_API OBX_store* obx_store_attach_or_open(OBX_store_options* opt, bool check /// E.g. these IDs can be shared across threads efficiently and can serve a similar purpose as weak pointers do. OBX_C_API uint64_t obx_store_id(OBX_store* store); +/// Gives the store type ID for the given store +/// @returns One of ::OBXStoreTypeId +OBX_C_API uint32_t obx_store_type_id(OBX_store* store); + /// Clone a previously opened store; while a store instance is usable from multiple threads, situations may exist /// in which cloning a store simplifies the overall lifecycle. /// E.g. when a store is used for multiple threads and it may only be fully released once the last thread completes. @@ -959,8 +1053,14 @@ OBX_C_API bool obx_store_await_async_completion(OBX_store* store); /// @returns false if shutting down or an error occurred OBX_C_API bool obx_store_await_async_submitted(OBX_store* store); +/// Backs up the store DB to the given backup-file, using the given flags. +/// Note: backup is a server-only feature. +/// @param backup_flags 0 for defaults or OBXBackupFlags bit flags +OBX_C_API obx_err obx_store_back_up_to_file(OBX_store* store, const char* backup_file, uint32_t backup_flags); + /// Configure debug logging -OBX_C_API obx_err obx_store_debug_flags(OBX_store* store, OBXDebugFlags flags); +/// @param flags See OBXDebugFlags for values (use bitwise OR to combine multiple flags) +OBX_C_API obx_err obx_store_debug_flags(OBX_store* store, uint32_t flags); /// @returns true if the store was opened with a previous commit /// @see obx_opt_use_previous_commit() @@ -978,6 +1078,21 @@ OBX_C_API obx_err obx_store_prepare_to_close(OBX_store* store); /// @param store may be NULL OBX_C_API obx_err obx_store_close(OBX_store* store); +/// Store type to be registered with obx_store_type_id_register_default(). +typedef enum { + + /// Default store type: persistent data storage (based on LMDB) + OBXStoreTypeId_LMDB = 1, + + /// Store type ID for in-memory database (non-persistent) + OBXStoreTypeId_InMemory = 2 + +} OBXStoreTypeId; + +/// Registers the default DB type, which is used if no other types matched a path prefix. +/// @param storeTypeId Must be one of OBXStoreTypeId (for now). +OBX_C_API int obx_store_type_id_register_default(uint32_t storeTypeId); + //---------------------------------------------- // Transaction //---------------------------------------------- @@ -1027,7 +1142,7 @@ OBX_C_API obx_err obx_txn_close(OBX_txn* txn); /// Only obx_txn_close() is allowed to be called on the transaction after calling this. OBX_C_API obx_err obx_txn_abort(OBX_txn* txn); // Internal note: will make more sense once we introduce obx_txn_reset -OBX_C_API obx_err obx_txn_data_size(OBX_txn* txn, uint64_t* out_committed_size, uint64_t* out_size_change); +OBX_C_API obx_err obx_txn_data_size(OBX_txn* txn, uint64_t* out_committed_size, int64_t* out_size_change); //------------------------------------------------------------------ // Cursor (lower level API, check also the more convenient Box API) @@ -1150,6 +1265,19 @@ OBX_C_API obx_err obx_cursor_rel_remove(OBX_cursor* cursor, obx_schema_id relati /// @returns NULL if the operation failed, see functions like obx_last_error_code() to get error details OBX_C_API OBX_id_array* obx_cursor_rel_ids(OBX_cursor* cursor, obx_schema_id relation_id, obx_id source_id); +/// Gets the first object ID or zero if there was no object +OBX_C_API obx_err obx_cursor_seek_first_id(OBX_cursor* cursor, obx_id* out_id); + +/// Gets the next object ID or zero if there was no next object +OBX_C_API obx_err obx_cursor_seek_next_id(OBX_cursor* cursor, obx_id* out_id); + +/// Gets the object ID at the current position; ensures being up-to-date by verifying against database. +/// @param out_id pointer to receive an output. If the cursor is not positioned at an actual object, +/// it receives one of two special IDs: +/// 0 (OBJECT_ID_BEFORE_START) if the cursor was not yet moved or reached the end (going backwards). +/// 0xFFFFFFFFFFFFFFFF (OBJECT_ID_BEYOND_END) if the cursor reached the end (going forwards). +OBX_C_API obx_err obx_cursor_current_id(OBX_cursor* cursor, obx_id* out_id); + //---------------------------------------------- // Time series //---------------------------------------------- @@ -1632,7 +1760,7 @@ OBX_C_API obx_qb_cond obx_qb_less_or_equal_bytes(OBX_query_builder* builder, obx /// @param relation_count Number of related object an object must have to match. May be 0 if objects shall be matched /// that do not have related objects. (Typically low numbers are used for the count.) OBX_C_API obx_qb_cond obx_qb_relation_count_property(OBX_query_builder* builder, obx_schema_id relation_entity_id, - obx_schema_id relation_property_id, int32_t relation_count); + obx_schema_id relation_property_id, uint32_t relation_count); // Other condition related functions --------------------- @@ -1665,7 +1793,7 @@ OBX_C_API obx_qb_cond obx_qb_any(OBX_query_builder* builder, const obx_qb_cond c OBX_C_API obx_err obx_qb_param_alias(OBX_query_builder* builder, const char* alias); /// Configures an order of results in the query -OBX_C_API obx_err obx_qb_order(OBX_query_builder* builder, obx_schema_id property_id, OBXOrderFlags flags); +OBX_C_API obx_err obx_qb_order(OBX_query_builder* builder, obx_schema_id property_id, uint32_t flags); /// Create a link based on a property-relation (many-to-one) OBX_C_API OBX_query_builder* obx_qb_link_property(OBX_query_builder* builder, obx_schema_id property_id); @@ -2122,6 +2250,11 @@ OBX_C_API obx_err obx_tree_cursor_txn(OBX_tree_cursor* cursor, OBX_txn* txn); OBX_C_API obx_err obx_tree_cursor_get_raw(OBX_tree_cursor* cursor, const char* path, const void** data, size_t* size, const void** metadata, size_t* metadata_size); +/// Gets the full path (from the root) of the given leaf ID. +/// @returns If successful, an allocated path is returned (malloc), which must be free()-ed by the caller. +/// @returns If not successful, NULL is returned. +OBX_C_API const char* obx_tree_cursor_get_leaf_path(OBX_tree_cursor* cursor, obx_id leaf_id); + /// \brief A "low-level" put operation for a tree leaf using given raw FlatBuffer bytes. /// Any non-existing branches and meta nodes are put on the fly if an optional meta-leaf FlatBuffers is given. /// A typical usage pattern is to first try without the meta-leaf, and if it does not work, create the meta-leaf and @@ -2147,6 +2280,35 @@ OBX_C_API obx_err obx_tree_cursor_put_raw(OBX_tree_cursor* cursor, const char* p /// See also obx_tree_async_consolidate_node_conflicts() for an asynchronous version. OBX_C_API obx_err obx_tree_cursor_consolidate_node_conflicts(OBX_tree_cursor* cursor, size_t* out_consolidated_count); +struct OBX_tree_leaves_info; // doxygen (only) picks up the typedef struct below + +/// Extended information about leaf nodes. +typedef struct OBX_tree_leaves_info OBX_tree_leaves_info; + +/// \brief Given an existing path, return all existing leaves with their paths. +/// As this traverses the data tree (i.e. not the meta tree), it will only return nodes that exist (obviously). +/// Thus, the meta tree may contain additional paths, but these are unused by the data tree (currently at least). +/// @param path the branch or leaf path to use. Optional: if no path is given (null), the root node is taken. +/// @returns leaf info ordered by path depth (i.e. starting from paths with the smallest number of branches); paths at +/// same depth are ordered by id. +OBX_C_API OBX_tree_leaves_info* obx_tree_cursor_get_child_leaves_info(OBX_tree_cursor* cursor, const char* path); + +/// Gets the number of leaves from the given leaves info. +OBX_C_API size_t obx_tree_leaves_info_size(OBX_tree_leaves_info* leaves_info); + +/// Gets the path of a given leaf (by index). +OBX_C_API const char* obx_tree_leaves_info_path(OBX_tree_leaves_info* leaves_info, size_t index); + +/// Gets the property type (as OBXPropertyType) of a given leaf (by index). +/// @returns OBXPropertyType_Unknown if no property type was found. +OBX_C_API OBXPropertyType obx_tree_leaves_info_type(OBX_tree_leaves_info* leaves_info, size_t index); + +/// Gets the id of a given leaf (by index). +OBX_C_API obx_id obx_tree_leaves_info_id(OBX_tree_leaves_info* leaves_info, size_t index); + +/// Frees a leaves info reference. +OBX_C_API void obx_tree_leaves_info_free(OBX_tree_leaves_info* leaves_info); + /// Callback for obx_tree_async_put_raw(). /// \note If the given status is an error, you can use functions like obx_last_error_message() to gather more info /// during this callback (error state is thread bound and the callback uses an internal thread). @@ -2268,7 +2430,7 @@ OBX_C_API obx_err obx_admin_opt_store_path(OBX_admin_options* opt, const char* d /// Set the address and port on which the underlying http-server should server the admin web UI. /// Defaults to "http://127.0.0.1:8081" -/// @note: you can use for e.g. "http://127.0.0.1:0" for automatic free port assignment - see obx_admin_bound_port(). +/// @note: you can use for e.g. "http://127.0.0.1:0" for automatic free port assignment - see obx_admin_port(). OBX_C_API obx_err obx_admin_opt_bind(OBX_admin_options* opt, const char* uri); /// Configure the server to use SSL, with the given certificate. diff --git a/objectbox/querybuilder.go b/objectbox/querybuilder.go index 4b53247..30bf78f 100644 --- a/objectbox/querybuilder.go +++ b/objectbox/querybuilder.go @@ -225,7 +225,7 @@ func (qb *QueryBuilder) LinkManyToMany(relation *RelationToMany, conditions []Co func (qb *QueryBuilder) order(propertyId C.obx_schema_id, flags C.OBXOrderFlags) { if qb.Err == nil { qb.Err = cCall(func() C.obx_err { - return C.obx_qb_order(qb.cqb, propertyId, flags) + return C.obx_qb_order(qb.cqb, propertyId, C.uint32_t(flags)) }) } } diff --git a/objectbox/version.go b/objectbox/version.go index be25e3e..749ebd5 100644 --- a/objectbox/version.go +++ b/objectbox/version.go @@ -51,7 +51,7 @@ func (v Version) String() string { // VersionGo returns the Version of the ObjectBox-Go binding func VersionGo() Version { // for label, use `beta.0` format, increasing the counter for each subsequent release - return Version{1, 7, 0, ""} + return Version{1, 8, 0, ""} } // VersionLib returns the Version of the dynamic linked ObjectBox library (loaded at runtime) @@ -72,14 +72,14 @@ func VersionLibStatic() Version { // VersionLibMin returns the minimum Version of the dynamic linked ObjectBox library that is compatible with this Go version func VersionLibMin() Version { - return Version{0, 18, 0, ""} + return Version{0, 21, 0, ""} } // VersionLibMinRecommended returns the minimum recommended Version of the dynamic linked ObjectBox library. // This version not only considers compatibility with this Go version, but also known issues older (compatible) versions. // It is guaranteed to be at least VersionLibMin() func VersionLibMinRecommended() Version { - return Version{0, 18, 1, ""} + return Version{0, 21, 0, ""} } // VersionInfo returns a printable version string